p9any.c (4954B)
1 #include "std.h" 2 #include "dat.h" 3 4 /* 5 * p9any - protocol negotiator 6 * 7 * Protocol: 8 * S->C: v.2 proto@dom proto@dom proto@dom... NUL 9 * C->S: proto dom NUL 10 * [negotiated proto continues] 11 */ 12 13 extern Proto p9sk1, p9sk2, p9cr; 14 15 static Proto* okproto[] = 16 { 17 &p9sk1, 18 nil 19 }; 20 21 static int 22 rolecall(Role *r, char *name, Conv *c) 23 { 24 for(; r->name; r++) 25 if(strcmp(r->name, name) == 0) 26 return (*r->fn)(c); 27 werrstr("unknown role"); 28 return -1; 29 } 30 31 static int 32 hasnul(void *v, int n) 33 { 34 char *c; 35 36 c = v; 37 if(n > 0 && c[n-1] == '\0') 38 return n; 39 else 40 return AuthRpcMax; 41 } 42 43 static int 44 p9anyserver(Conv *c) 45 { 46 char *s, *dom; 47 int i, j, n, m, ret; 48 char *tok[3]; 49 Attr *attr; 50 Key *k; 51 52 ret = -1; 53 s = estrdup("v.2"); 54 n = 0; 55 attr = delattr(copyattr(c->attr), "proto"); 56 57 for(i=0; i<ring.nkey; i++){ 58 k = ring.key[i]; 59 for(j=0; okproto[j]; j++) 60 if(k->proto == okproto[j] 61 && (dom = strfindattr(k->attr, "dom")) != nil 62 && matchattr(attr, k->attr, k->privattr)){ 63 s = estrappend(s, " %s@%s", k->proto->name, dom); 64 n++; 65 } 66 } 67 68 if(n == 0){ 69 werrstr("no valid keys"); 70 goto out; 71 } 72 73 c->state = "write offer"; 74 if(convwrite(c, s, strlen(s)+1) < 0) 75 goto out; 76 free(s); 77 s = nil; 78 79 c->state = "read choice"; 80 if(convreadfn(c, hasnul, &s) < 0) 81 goto out; 82 83 m = tokenize(s, tok, nelem(tok)); 84 if(m != 2){ 85 werrstr("bad protocol message"); 86 goto out; 87 } 88 89 for(i=0; okproto[i]; i++) 90 if(strcmp(okproto[i]->name, tok[0]) == 0) 91 break; 92 if(!okproto[i]){ 93 werrstr("bad chosen protocol %q", tok[0]); 94 goto out; 95 } 96 97 c->state = "write ok"; 98 if(convwrite(c, "OK\0", 3) < 0) 99 goto out; 100 101 c->state = "start choice"; 102 attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]); 103 free(c->attr); 104 c->attr = attr; 105 attr = nil; 106 c->proto = okproto[i]; 107 108 if(rolecall(c->proto->roles, "server", c) < 0){ 109 werrstr("%s: %r", tok[0]); 110 goto out; 111 } 112 113 ret = 0; 114 115 out: 116 free(s); 117 freeattr(attr); 118 return ret; 119 } 120 121 static int 122 p9anyclient(Conv *c) 123 { 124 char *s, **f, *tok[20], ok[3], *q, *user, *dom, *choice; 125 int i, n, ret, version; 126 Key *k; 127 Attr *attr; 128 Proto *p; 129 130 ret = -1; 131 s = nil; 132 k = nil; 133 134 user = strfindattr(c->attr, "user"); 135 dom = strfindattr(c->attr, "dom"); 136 137 /* 138 * if the user is the factotum owner, any key will do. 139 * if not, then if we have a speakfor key, 140 * we will only vouch for the user's local identity. 141 * 142 * this logic is duplicated in p9sk1.c 143 */ 144 attr = delattr(copyattr(c->attr), "role"); 145 attr = delattr(attr, "proto"); 146 if(strcmp(c->sysuser, owner) == 0) 147 attr = addattr(attr, "role=client"); 148 else if(user==nil || strcmp(c->sysuser, user)==0){ 149 attr = delattr(attr, "user"); 150 attr = addattr(attr, "role=speakfor"); 151 }else{ 152 werrstr("will not authenticate for %q as %q", c->sysuser, user); 153 goto out; 154 } 155 156 c->state = "read offer"; 157 if(convreadfn(c, hasnul, &s) < 0) 158 goto out; 159 160 c->state = "look for keys"; 161 n = tokenize(s, tok, nelem(tok)); 162 f = tok; 163 version = 1; 164 if(n > 0 && memcmp(f[0], "v.", 2) == 0){ 165 version = atoi(f[0]+2); 166 if(version != 2){ 167 werrstr("unknown p9any version: %s", f[0]); 168 goto out; 169 } 170 f++; 171 n--; 172 } 173 174 /* look for keys that don't need confirmation */ 175 for(i=0; i<n; i++){ 176 if((q = strchr(f[i], '@')) == nil) 177 continue; 178 if(dom && strcmp(q+1, dom) != 0) 179 continue; 180 *q++ = '\0'; 181 if((k = keylookup("%A proto=%q dom=%q", attr, f[i], q)) 182 && strfindattr(k->attr, "confirm") == nil) 183 goto found; 184 *--q = '@'; 185 } 186 187 /* look for any keys at all */ 188 for(i=0; i<n; i++){ 189 if((q = strchr(f[i], '@')) == nil) 190 continue; 191 if(dom && strcmp(q+1, dom) != 0) 192 continue; 193 *q++ = '\0'; 194 if(k = keylookup("%A proto=%q dom=%q", attr, f[i], q)) 195 goto found; 196 *--q = '@'; 197 } 198 199 /* ask for new keys */ 200 c->state = "ask for keys"; 201 for(i=0; i<n; i++){ 202 if((q = strchr(f[i], '@')) == nil) 203 continue; 204 if(dom && strcmp(q+1, dom) != 0) 205 continue; 206 *q++ = '\0'; 207 p = protolookup(f[i]); 208 if(p == nil || p->keyprompt == nil){ 209 *--q = '@'; 210 continue; 211 } 212 if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt)) 213 goto found; 214 *--q = '@'; 215 } 216 217 /* nothing worked */ 218 werrstr("unable to find common key"); 219 goto out; 220 221 found: 222 /* f[i] is the chosen protocol, q the chosen domain */ 223 attr = addattr(attr, "proto=%q dom=%q", f[i], q); 224 c->state = "write choice"; 225 226 /* have a key: go for it */ 227 choice = estrappend(nil, "%q %q", f[i], q); 228 if(convwrite(c, choice, strlen(choice)+1) < 0){ 229 free(choice); 230 goto out; 231 } 232 free(choice); 233 234 if(version == 2){ 235 c->state = "read ok"; 236 if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0) 237 goto out; 238 } 239 240 c->state = "start choice"; 241 c->proto = protolookup(f[i]); 242 freeattr(c->attr); 243 c->attr = attr; 244 attr = nil; 245 246 if(rolecall(c->proto->roles, "client", c) < 0){ 247 werrstr("%s: %r", c->proto->name); 248 goto out; 249 } 250 251 ret = 0; 252 253 out: 254 keyclose(k); 255 freeattr(attr); 256 free(s); 257 return ret; 258 } 259 260 static Role 261 p9anyroles[] = 262 { 263 "client", p9anyclient, 264 "server", p9anyserver, 265 0 266 }; 267 268 Proto p9any = { 269 "p9any", 270 p9anyroles 271 };