URI:
       tp9any.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tp9any.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 };