mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
267 lines
4.8 KiB
C
267 lines
4.8 KiB
C
|
#include "std.h"
|
||
|
#include "dat.h"
|
||
|
|
||
|
/*
|
||
|
* p9any - protocol negotiator
|
||
|
*
|
||
|
* Protocol:
|
||
|
* S->C: v.2 proto@dom proto@dom proto@dom... NUL
|
||
|
* C->S: proto dom NUL
|
||
|
* [negotiated proto continues]
|
||
|
*/
|
||
|
|
||
|
static Proto* okproto[] =
|
||
|
{
|
||
|
&p9sk1,
|
||
|
nil,
|
||
|
};
|
||
|
|
||
|
static int
|
||
|
rolecall(Role *r, char *name, Conv *c)
|
||
|
{
|
||
|
for(; r->name; r++)
|
||
|
if(strcmp(r->name, name) == 0)
|
||
|
return (*r->fn)(c);
|
||
|
werrstr("unknown role");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
hasnul(void *v, int n)
|
||
|
{
|
||
|
char *c;
|
||
|
|
||
|
c = v;
|
||
|
if(n > 0 && c[n-1] == '\0')
|
||
|
return n;
|
||
|
else
|
||
|
return n+1;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
p9anyserver(Conv *c)
|
||
|
{
|
||
|
char *s, *dom;
|
||
|
int i, j, n, m, ret;
|
||
|
char *tok[3];
|
||
|
Attr *attr;
|
||
|
Key *k;
|
||
|
|
||
|
ret = -1;
|
||
|
s = estrdup("v.2");
|
||
|
n = 0;
|
||
|
attr = delattr(copyattr(c->attr), "proto");
|
||
|
|
||
|
for(i=0; i<ring.nkey; i++){
|
||
|
k = ring.key[i];
|
||
|
for(j=0; okproto[j]; j++)
|
||
|
if(k->proto == okproto[j]
|
||
|
&& (dom = strfindattr(k->attr, "dom")) != nil
|
||
|
&& matchattr(attr, k->attr, k->privattr)){
|
||
|
s = estrappend(s, " %s@%s", k->proto->name, dom);
|
||
|
n++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(n == 0){
|
||
|
werrstr("no valid keys");
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
c->state = "write offer";
|
||
|
if(convwrite(c, s, strlen(s)+1) < 0)
|
||
|
goto out;
|
||
|
free(s);
|
||
|
s = nil;
|
||
|
|
||
|
c->state = "read choice";
|
||
|
if(convreadfn(c, hasnul, &s) < 0)
|
||
|
goto out;
|
||
|
|
||
|
m = tokenize(s, tok, nelem(tok));
|
||
|
if(m != 2){
|
||
|
werrstr("bad protocol message");
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
for(i=0; okproto[i]; i++)
|
||
|
if(strcmp(okproto[i]->name, tok[0]) == 0)
|
||
|
break;
|
||
|
if(!okproto[i]){
|
||
|
werrstr("bad chosen protocol %q", tok[0]);
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
c->state = "write ok";
|
||
|
if(convwrite(c, "OK\0", 3) < 0)
|
||
|
goto out;
|
||
|
|
||
|
c->state = "start choice";
|
||
|
attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]);
|
||
|
free(c->attr);
|
||
|
c->attr = attr;
|
||
|
attr = nil;
|
||
|
c->proto = okproto[i];
|
||
|
|
||
|
if(rolecall(c->proto->roles, "server", c) < 0){
|
||
|
werrstr("%s: %r", tok[0]);
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
ret = 0;
|
||
|
|
||
|
out:
|
||
|
free(s);
|
||
|
freeattr(attr);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
p9anyclient(Conv *c)
|
||
|
{
|
||
|
char *s, **f, *tok[20], ok[3], *q, *user, *dom;
|
||
|
int i, n, ret, version;
|
||
|
Key *k;
|
||
|
Attr *attr;
|
||
|
Proto *p;
|
||
|
|
||
|
ret = -1;
|
||
|
s = nil;
|
||
|
k = nil;
|
||
|
|
||
|
user = strfindattr(c->attr, "user");
|
||
|
dom = strfindattr(c->attr, "dom");
|
||
|
|
||
|
/*
|
||
|
* if the user is the factotum owner, any key will do.
|
||
|
* if not, then if we have a speakfor key,
|
||
|
* we will only vouch for the user's local identity.
|
||
|
*
|
||
|
* this logic is duplicated in p9sk1.c
|
||
|
*/
|
||
|
attr = delattr(copyattr(c->attr), "role");
|
||
|
attr = delattr(attr, "proto");
|
||
|
if(strcmp(c->sysuser, owner) == 0)
|
||
|
attr = addattr(attr, "role=client");
|
||
|
else if(user==nil || strcmp(c->sysuser, user)==0){
|
||
|
attr = delattr(attr, "user");
|
||
|
attr = addattr(attr, "role=speakfor");
|
||
|
}else{
|
||
|
werrstr("will not authenticate for %q as %q", c->sysuser, user);
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
c->state = "read offer";
|
||
|
if(convreadfn(c, hasnul, &s) < 0)
|
||
|
goto out;
|
||
|
|
||
|
c->state = "look for keys";
|
||
|
n = tokenize(s, tok, nelem(tok));
|
||
|
f = tok;
|
||
|
version = 1;
|
||
|
if(n > 0 && memcmp(f[0], "v.", 2) == 0){
|
||
|
version = atoi(f[0]+2);
|
||
|
if(version != 2){
|
||
|
werrstr("unknown p9any version: %s", f[0]);
|
||
|
goto out;
|
||
|
}
|
||
|
f++;
|
||
|
n--;
|
||
|
}
|
||
|
|
||
|
/* look for keys that don't need confirmation */
|
||
|
for(i=0; i<n; i++){
|
||
|
if((q = strchr(f[i], '@')) == nil)
|
||
|
continue;
|
||
|
if(dom && strcmp(q+1, dom) != 0)
|
||
|
continue;
|
||
|
*q++ = '\0';
|
||
|
if((k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
|
||
|
&& strfindattr(k->attr, "confirm") == nil)
|
||
|
goto found;
|
||
|
*--q = '@';
|
||
|
}
|
||
|
|
||
|
/* look for any keys at all */
|
||
|
for(i=0; i<n; i++){
|
||
|
if((q = strchr(f[i], '@')) == nil)
|
||
|
continue;
|
||
|
if(dom && strcmp(q+1, dom) != 0)
|
||
|
continue;
|
||
|
*q++ = '\0';
|
||
|
if(k = keyfetch(c, "%A proto=%q dom=%q", attr, f[i], q))
|
||
|
goto found;
|
||
|
*--q = '@';
|
||
|
}
|
||
|
|
||
|
/* ask for new keys */
|
||
|
c->state = "ask for keys";
|
||
|
for(i=0; i<n; i++){
|
||
|
if((q = strchr(f[i], '@')) == nil)
|
||
|
continue;
|
||
|
if(dom && strcmp(q+1, dom) != 0)
|
||
|
continue;
|
||
|
*q++ = '\0';
|
||
|
p = protolookup(f[i]);
|
||
|
if(p == nil || p->keyprompt == nil){
|
||
|
*--q = '@';
|
||
|
continue;
|
||
|
}
|
||
|
if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt))
|
||
|
goto found;
|
||
|
*--q = '@';
|
||
|
}
|
||
|
|
||
|
/* nothing worked */
|
||
|
werrstr("unable to find common key");
|
||
|
goto out;
|
||
|
|
||
|
found:
|
||
|
/* f[i] is the chosen protocol, q the chosen domain */
|
||
|
attr = addattr(attr, "proto=%q dom=%q", f[i], q);
|
||
|
c->state = "write choice";
|
||
|
/* have a key: go for it */
|
||
|
if(convprint(c, "%q %q", f[i], q) < 0
|
||
|
|| convwrite(c, "\0", 1) < 0)
|
||
|
goto out;
|
||
|
|
||
|
if(version == 2){
|
||
|
c->state = "read ok";
|
||
|
if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0)
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
c->state = "start choice";
|
||
|
c->proto = protolookup(f[i]);
|
||
|
freeattr(c->attr);
|
||
|
c->attr = attr;
|
||
|
attr = nil;
|
||
|
|
||
|
if(rolecall(c->proto->roles, "client", c) < 0){
|
||
|
werrstr("%s: %r", c->proto->name);
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
ret = 0;
|
||
|
|
||
|
out:
|
||
|
keyclose(k);
|
||
|
freeattr(attr);
|
||
|
free(s);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static Role
|
||
|
p9anyroles[] =
|
||
|
{
|
||
|
"client", p9anyclient,
|
||
|
"server", p9anyserver,
|
||
|
0
|
||
|
};
|
||
|
|
||
|
Proto p9any = {
|
||
|
.name= "p9any",
|
||
|
.roles= p9anyroles,
|
||
|
};
|
||
|
|