URI: 
       tapop.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
       ---
       tapop.c (6341B)
       ---
            1 /*
            2  * APOP, CRAM - MD5 challenge/response authentication
            3  *
            4  * The client does not authenticate the server, hence no CAI.
            5  *
            6  * Protocol:
            7  *
            8  *        S -> C:        random@domain
            9  *        C -> S:        user hex-response
           10  *        S -> C:        ok
           11  *
           12  * Note that this is the protocol between factotum and the local
           13  * program, not between the two factotums.  The information
           14  * exchanged here is wrapped in the APOP protocol by the local
           15  * programs.
           16  *
           17  * If S sends "bad [msg]" instead of "ok", that is a hint that the key is bad.
           18  * The protocol goes back to "C -> S: user hex-response".
           19  */
           20 
           21 #include "std.h"
           22 #include "dat.h"
           23 
           24 extern Proto apop, cram;
           25 
           26 static int
           27 apopcheck(Key *k)
           28 {
           29         if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
           30                 werrstr("need user and !password attributes");
           31                 return -1;
           32         }
           33         return 0;
           34 }
           35 
           36 static int
           37 apopclient(Conv *c)
           38 {
           39         char *chal, *pw, *res;
           40         int astype, nchal, npw, ntry, ret;
           41         uchar resp[MD5dlen];
           42         Attr *attr;
           43         DigestState *ds;
           44         Key *k;
           45 
           46         chal = nil;
           47         k = nil;
           48         res = nil;
           49         ret = -1;
           50         attr = c->attr;
           51 
           52         if(c->proto == &apop)
           53                 astype = AuthApop;
           54         else if(c->proto == &cram)
           55                 astype = AuthCram;
           56         else{
           57                 werrstr("bad proto");
           58                 goto out;
           59         }
           60 
           61         c->state = "find key";
           62         k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
           63         if(k == nil)
           64                 goto out;
           65 
           66         c->state = "read challenge";
           67         if((nchal = convreadm(c, &chal)) < 0)
           68                 goto out;
           69 
           70           for(ntry=1;; ntry++){
           71                 if(c->attr != attr)
           72                         freeattr(c->attr);
           73                 c->attr = addattrs(copyattr(attr), k->attr);
           74                 if((pw = strfindattr(k->privattr, "!password")) == nil){
           75                         werrstr("key has no password (cannot happen?)");
           76                         goto out;
           77                 }
           78                 npw = strlen(pw);
           79 
           80                 switch(astype){
           81                 case AuthApop:
           82                         ds = md5((uchar*)chal, nchal, nil, nil);
           83                         md5((uchar*)pw, npw, resp, ds);
           84                         break;
           85                 case AuthCram:
           86                         hmac_md5((uchar*)chal, nchal, (uchar*)pw, npw, resp, nil);
           87                         break;
           88                 }
           89 
           90                 /* C->S: APOP user hex-response\n */
           91                 if(ntry == 1)
           92                         c->state = "write user";
           93                 else{
           94                         sprint(c->statebuf, "write user (auth attempt #%d)", ntry);
           95                         c->state = c->statebuf;
           96                 }
           97                 if(convprint(c, "%s", strfindattr(k->attr, "user")) < 0)
           98                         goto out;
           99 
          100                 c->state = "write response";
          101                 if(convprint(c, "%.*H", sizeof resp, resp) < 0)
          102                         goto out;
          103 
          104                 c->state = "read result";
          105                 if(convreadm(c, &res) < 0)
          106                         goto out;
          107 
          108                 if(strcmp(res, "ok") == 0)
          109                         break;
          110 
          111                 if(strncmp(res, "bad ", 4) != 0){
          112                         werrstr("bad result: %s", res);
          113                         goto out;
          114                 }
          115 
          116                 c->state = "replace key";
          117                 if((k = keyreplace(c, k, "%s", res+4)) == nil){
          118                         c->state = "auth failed";
          119                         werrstr("%s", res+4);
          120                         goto out;
          121                 }
          122                 free(res);
          123                 res = nil;
          124         }
          125 
          126         werrstr("succeeded");
          127         ret = 0;
          128 
          129 out:
          130         keyclose(k);
          131         free(chal);
          132         if(c->attr != attr)
          133                 freeattr(attr);
          134         return ret;
          135 }
          136 
          137 /* shared with auth dialing routines */
          138 typedef struct ServerState ServerState;
          139 struct ServerState
          140 {
          141         int asfd;
          142         Key *k;
          143         Ticketreq tr;
          144         Ticket t;
          145         char *dom;
          146         char *hostid;
          147 };
          148 
          149 enum
          150 {
          151         APOPCHALLEN = 128
          152 };
          153 
          154 static int apopchal(ServerState*, int, char[APOPCHALLEN]);
          155 static int apopresp(ServerState*, char*, char*);
          156 
          157 static int
          158 apopserver(Conv *c)
          159 {
          160         char chal[APOPCHALLEN], *user, *resp;
          161         ServerState s;
          162         int astype, ret;
          163         Attr *a;
          164 
          165         ret = -1;
          166         user = nil;
          167         resp = nil;
          168         memset(&s, 0, sizeof s);
          169         s.asfd = -1;
          170 
          171         if(c->proto == &apop)
          172                 astype = AuthApop;
          173         else if(c->proto == &cram)
          174                 astype = AuthCram;
          175         else{
          176                 werrstr("bad proto");
          177                 goto out;
          178         }
          179 
          180         c->state = "find key";
          181         if((s.k = plan9authkey(c->attr)) == nil)
          182                 goto out;
          183 
          184         a = copyattr(s.k->attr);
          185         a = delattr(a, "proto");
          186         c->attr = addattrs(c->attr, a);
          187         freeattr(a);
          188 
          189         c->state = "authdial";
          190         s.hostid = strfindattr(s.k->attr, "user");
          191         s.dom = strfindattr(s.k->attr, "dom");
          192         if((s.asfd = xioauthdial(nil, s.dom)) < 0){
          193                 werrstr("authdial %s: %r", s.dom);
          194                 goto out;
          195         }
          196 
          197         c->state = "authchal";
          198         if(apopchal(&s, astype, chal) < 0)
          199                 goto out;
          200 
          201         c->state = "write challenge";
          202         if(convprint(c, "%s", chal) < 0)
          203                 goto out;
          204 
          205         for(;;){
          206                 c->state = "read user";
          207                 if(convreadm(c, &user) < 0)
          208                         goto out;
          209 
          210                 c->state = "read response";
          211                 if(convreadm(c, &resp) < 0)
          212                         goto out;
          213 
          214                 c->state = "authwrite";
          215                 switch(apopresp(&s, user, resp)){
          216                 case -1:
          217                         goto out;
          218                 case 0:
          219                         c->state = "write status";
          220                         if(convprint(c, "bad authentication failed") < 0)
          221                                 goto out;
          222                         break;
          223                 case 1:
          224                         c->state = "write status";
          225                         if(convprint(c, "ok") < 0)
          226                                 goto out;
          227                         goto ok;
          228                 }
          229                 free(user);
          230                 free(resp);
          231                 user = nil;
          232                 resp = nil;
          233         }
          234 
          235 ok:
          236         ret = 0;
          237         c->attr = addcap(c->attr, c->sysuser, &s.t);
          238 
          239 out:
          240         keyclose(s.k);
          241         free(user);
          242         free(resp);
          243         xioclose(s.asfd);
          244         return ret;
          245 }
          246 
          247 static int
          248 apopchal(ServerState *s, int astype, char chal[APOPCHALLEN])
          249 {
          250         char trbuf[TICKREQLEN];
          251         Ticketreq tr;
          252 
          253         memset(&tr, 0, sizeof tr);
          254 
          255         tr.type = astype;
          256 
          257         if(strlen(s->hostid) >= sizeof tr.hostid){
          258                 werrstr("hostid too long");
          259                 return -1;
          260         }
          261         strcpy(tr.hostid, s->hostid);
          262 
          263         if(strlen(s->dom) >= sizeof tr.authdom){
          264                 werrstr("domain too long");
          265                 return -1;
          266         }
          267         strcpy(tr.authdom, s->dom);
          268 
          269         convTR2M(&tr, trbuf);
          270         if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
          271                 return -1;
          272 
          273         if(xioasrdresp(s->asfd, chal, APOPCHALLEN) <= 5)
          274                 return -1;
          275 
          276         s->tr = tr;
          277         return 0;
          278 }
          279 
          280 static int
          281 apopresp(ServerState *s, char *user, char *resp)
          282 {
          283         char tabuf[TICKETLEN+AUTHENTLEN];
          284         char trbuf[TICKREQLEN];
          285         int len;
          286         Authenticator a;
          287         Ticket t;
          288         Ticketreq tr;
          289 
          290         tr = s->tr;
          291         if(memrandom(tr.chal, CHALLEN) < 0)
          292                 return -1;
          293 
          294         if(strlen(user) >= sizeof tr.uid){
          295                 werrstr("uid too long");
          296                 return -1;
          297         }
          298         strcpy(tr.uid, user);
          299 
          300         convTR2M(&tr, trbuf);
          301         if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
          302                 return -1;
          303 
          304         len = strlen(resp);
          305         if(xiowrite(s->asfd, resp, len) != len)
          306                 return -1;
          307 
          308         if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
          309                 return 0;
          310 
          311         convM2T(tabuf, &t, s->k->priv);
          312         if(t.num != AuthTs
          313         || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
          314                 werrstr("key mismatch with auth server");
          315                 return -1;
          316         }
          317 
          318         convM2A(tabuf+TICKETLEN, &a, t.key);
          319         if(a.num != AuthAc
          320         || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
          321         || a.id != 0){
          322                 werrstr("key2 mismatch with auth server");
          323                 return -1;
          324         }
          325 
          326         s->t = t;
          327         return 1;
          328 }
          329 
          330 static Role
          331 apoproles[] =
          332 {
          333         "client",        apopclient,
          334         "server",        apopserver,
          335         0
          336 };
          337 
          338 Proto apop = {
          339         "apop",
          340         apoproles,
          341         "user? !password?",
          342         apopcheck,
          343         nil
          344 };
          345 
          346 Proto cram = {
          347         "cram",
          348         apoproles,
          349         "user? !password?",
          350         apopcheck,
          351         nil
          352 };