tchap.c - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tchap.c (8049B)
       ---
            1 /*
            2  * CHAP, MSCHAP
            3  *
            4  * The client does not authenticate the server, hence no CAI
            5  *
            6  * Protocol:
            7  *
            8  *        S -> C: random 8-byte challenge
            9  *        C -> S: user in UTF-8
           10  *        C -> S: Chapreply or MSchapreply structure
           11  *        S -> C: ok or 'bad why'
           12  *
           13  * The chap protocol requires the client to give it id=%d, the id of
           14  * the PPP message containing the challenge, which is used
           15  * as part of the response.  Because the client protocol is message-id
           16  * specific, there is no point in looping to try multiple keys.
           17  *
           18  * The MS chap protocol actually uses two different hashes, an
           19  * older insecure one called the LM (Lan Manager) hash, and a newer
           20  * more secure one called the NT hash.  By default we send back only
           21  * the NT hash, because the LM hash can help an eavesdropper run
           22  * a brute force attack.  If the key has an lm attribute, then we send only the
           23  * LM hash.
           24  */
           25 
           26 #include "std.h"
           27 #include "dat.h"
           28 
           29 extern Proto chap, mschap;
           30 
           31 enum {
           32         ChapChallen = 8,
           33 
           34         MShashlen = 16,
           35         MSchallen = 8,
           36         MSresplen = 24
           37 };
           38 
           39 static int
           40 chapcheck(Key *k)
           41 {
           42         if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
           43                 werrstr("need user and !password attributes");
           44                 return -1;
           45         }
           46         return 0;
           47 }
           48 
           49 static void
           50 nthash(uchar hash[MShashlen], char *passwd)
           51 {
           52         uchar buf[512];
           53         int i;
           54 
           55         for(i=0; *passwd && i<sizeof(buf); passwd++) {
           56                 buf[i++] = *passwd;
           57                 buf[i++] = 0;
           58         }
           59 
           60         memset(hash, 0, 16);
           61 
           62         md4(buf, i, hash, 0);
           63 }
           64 
           65 static void
           66 desencrypt(uchar data[8], uchar key[7])
           67 {
           68         ulong ekey[32];
           69 
           70         key_setup(key, ekey);
           71         block_cipher(ekey, data, 0);
           72 }
           73 
           74 static void
           75 lmhash(uchar hash[MShashlen], char *passwd)
           76 {
           77         uchar buf[14];
           78         char *stdtext = "KGS!@#$%";
           79         int i;
           80 
           81         strncpy((char*)buf, passwd, sizeof(buf));
           82         for(i=0; i<sizeof(buf); i++)
           83                 if(buf[i] >= 'a' && buf[i] <= 'z')
           84                         buf[i] += 'A' - 'a';
           85 
           86         memset(hash, 0, 16);
           87         memcpy(hash, stdtext, 8);
           88         memcpy(hash+8, stdtext, 8);
           89 
           90         desencrypt(hash, buf);
           91         desencrypt(hash+8, buf+7);
           92 }
           93 
           94 static void
           95 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
           96 {
           97         int i;
           98         uchar buf[21];
           99 
          100         memset(buf, 0, sizeof(buf));
          101         memcpy(buf, hash, MShashlen);
          102 
          103         for(i=0; i<3; i++) {
          104                 memmove(resp+i*MSchallen, chal, MSchallen);
          105                 desencrypt(resp+i*MSchallen, buf+i*7);
          106         }
          107 }
          108 
          109 static int
          110 chapclient(Conv *c)
          111 {
          112         int id, astype, nchal, npw, ret;
          113         uchar *chal;
          114         char *s, *pw, *user, *res;
          115         Attr *attr;
          116         Key *k;
          117         Chapreply cr;
          118         MSchapreply mscr;
          119         DigestState *ds;
          120 
          121         ret = -1;
          122         chal = nil;
          123         k = nil;
          124         attr = c->attr;
          125         res = nil;
          126 
          127         if(c->proto == &chap){
          128                 astype = AuthChap;
          129                 s = strfindattr(attr, "id");
          130                 if(s == nil || *s == 0){
          131                         werrstr("need id=n attr in start message");
          132                         goto out;
          133                 }
          134                 id = strtol(s, &s, 10);
          135                 if(*s != 0 || id < 0 || id >= 256){
          136                         werrstr("bad id=n attr in start message");
          137                         goto out;
          138                 }
          139                 cr.id = id;
          140         }else if(c->proto == &mschap)
          141                 astype = AuthMSchap;
          142         else{
          143                 werrstr("bad proto");
          144                 goto out;
          145         }
          146 
          147         c->state = "find key";
          148         k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
          149         if(k == nil)
          150                 goto out;
          151 
          152         c->attr = addattrs(copyattr(attr), k->attr);
          153 
          154         c->state = "read challenge";
          155         if((nchal = convreadm(c, (char**)(void*)&chal)) < 0)
          156                 goto out;
          157         if(astype == AuthMSchap && nchal != MSchallen)
          158         c->state = "write user";
          159         if((user = strfindattr(k->attr, "user")) == nil){
          160                 werrstr("key has no user (cannot happen?)");
          161                 goto out;
          162         }
          163         if(convprint(c, "%s", user) < 0)
          164                 goto out;
          165 
          166         c->state = "write response";
          167         if((pw = strfindattr(k->privattr, "!password")) == nil){
          168                 werrstr("key has no password (cannot happen?)");
          169                 goto out;
          170         }
          171         npw = strlen(pw);
          172 
          173         if(astype == AuthChap){
          174                 ds = md5(&cr.id, 1, 0, 0);
          175                 md5((uchar*)pw, npw, 0, ds);
          176                 md5(chal, nchal, (uchar*)cr.resp, ds);
          177                 if(convwrite(c, &cr, sizeof cr) < 0)
          178                         goto out;
          179         }else{
          180                 uchar hash[MShashlen];
          181 
          182                 memset(&mscr, 0, sizeof mscr);
          183                 if(strfindattr(k->attr, "lm")){
          184                         lmhash(hash, pw);
          185                         mschalresp((uchar*)mscr.LMresp, hash, chal);
          186                 }else{
          187                         nthash(hash, pw);
          188                         mschalresp((uchar*)mscr.NTresp, hash, chal);
          189                 }
          190                 if(convwrite(c, &mscr, sizeof mscr) < 0)
          191                         goto out;
          192         }
          193 
          194         c->state = "read result";
          195         if(convreadm(c, &res) < 0)
          196                 goto out;
          197         if(strcmp(res, "ok") == 0){
          198                 ret = 0;
          199                 werrstr("succeeded");
          200                 goto out;
          201         }
          202         if(strncmp(res, "bad ", 4) != 0){
          203                 werrstr("bad result: %s", res);
          204                 goto out;
          205         }
          206 
          207         c->state = "replace key";
          208         keyevict(c, k, "%s", res+4);
          209         werrstr("%s", res+4);
          210 
          211 out:
          212         free(res);
          213         keyclose(k);
          214         free(chal);
          215         if(c->attr != attr)
          216                 freeattr(attr);
          217         return ret;
          218 }
          219 
          220 /* shared with auth dialing routines */
          221 typedef struct ServerState ServerState;
          222 struct ServerState
          223 {
          224         int asfd;
          225         Key *k;
          226         Ticketreq tr;
          227         Ticket t;
          228         char *dom;
          229         char *hostid;
          230 };
          231 
          232 static int chapchal(ServerState*, int, char[ChapChallen]);
          233 static int chapresp(ServerState*, char*, char*);
          234 
          235 static int
          236 chapserver(Conv *c)
          237 {
          238         char chal[ChapChallen], *user, *resp;
          239         ServerState s;
          240         int astype, ret;
          241         Attr *a;
          242 
          243         ret = -1;
          244         user = nil;
          245         resp = nil;
          246         memset(&s, 0, sizeof s);
          247         s.asfd = -1;
          248 
          249         if(c->proto == &chap)
          250                 astype = AuthChap;
          251         else if(c->proto == &mschap)
          252                 astype = AuthMSchap;
          253         else{
          254                 werrstr("bad proto");
          255                 goto out;
          256         }
          257 
          258         c->state = "find key";
          259         if((s.k = plan9authkey(c->attr)) == nil)
          260                 goto out;
          261 
          262         a = copyattr(s.k->attr);
          263         a = delattr(a, "proto");
          264         c->attr = addattrs(c->attr, a);
          265         freeattr(a);
          266 
          267         c->state = "authdial";
          268         s.hostid = strfindattr(s.k->attr, "user");
          269         s.dom = strfindattr(s.k->attr, "dom");
          270         if((s.asfd = xioauthdial(nil, s.dom)) < 0){
          271                 werrstr("authdial %s: %r", s.dom);
          272                 goto out;
          273         }
          274 
          275         c->state = "authchal";
          276         if(chapchal(&s, astype, chal) < 0)
          277                 goto out;
          278 
          279         c->state = "write challenge";
          280         if(convprint(c, "%s", chal) < 0)
          281                 goto out;
          282 
          283         c->state = "read user";
          284         if(convreadm(c, &user) < 0)
          285                 goto out;
          286 
          287         c->state = "read response";
          288         if(convreadm(c, &resp) < 0)
          289                 goto out;
          290 
          291         c->state = "authwrite";
          292         switch(chapresp(&s, user, resp)){
          293         default:
          294                 fprint(2, "factotum: bad result from chapresp\n");
          295                 goto out;
          296         case -1:
          297                 goto out;
          298         case 0:
          299                 c->state = "write status";
          300                 if(convprint(c, "bad authentication failed") < 0)
          301                         goto out;
          302                 goto out;
          303 
          304         case 1:
          305                 c->state = "write status";
          306                 if(convprint(c, "ok") < 0)
          307                         goto out;
          308                 goto ok;
          309         }
          310 
          311 ok:
          312         ret = 0;
          313         c->attr = addcap(c->attr, c->sysuser, &s.t);
          314 
          315 out:
          316         keyclose(s.k);
          317         free(user);
          318         free(resp);
          319 /*        xioclose(s.asfd); */
          320         return ret;
          321 }
          322 
          323 static int
          324 chapchal(ServerState *s, int astype, char chal[ChapChallen])
          325 {
          326         char trbuf[TICKREQLEN];
          327         Ticketreq tr;
          328 
          329         memset(&tr, 0, sizeof tr);
          330 
          331         tr.type = astype;
          332 
          333         if(strlen(s->hostid) >= sizeof tr.hostid){
          334                 werrstr("hostid too long");
          335                 return -1;
          336         }
          337         strcpy(tr.hostid, s->hostid);
          338 
          339         if(strlen(s->dom) >= sizeof tr.authdom){
          340                 werrstr("domain too long");
          341                 return -1;
          342         }
          343         strcpy(tr.authdom, s->dom);
          344 
          345         convTR2M(&tr, trbuf);
          346         if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
          347                 return -1;
          348 
          349         if(xioasrdresp(s->asfd, chal, ChapChallen) <= 5)
          350                 return -1;
          351 
          352         s->tr = tr;
          353         return 0;
          354 }
          355 
          356 static int
          357 chapresp(ServerState *s, char *user, char *resp)
          358 {
          359         char tabuf[TICKETLEN+AUTHENTLEN];
          360         char trbuf[TICKREQLEN];
          361         int len;
          362         Authenticator a;
          363         Ticket t;
          364         Ticketreq tr;
          365 
          366         tr = s->tr;
          367         if(memrandom(tr.chal, CHALLEN) < 0)
          368                 return -1;
          369 
          370         if(strlen(user) >= sizeof tr.uid){
          371                 werrstr("uid too long");
          372                 return -1;
          373         }
          374         strcpy(tr.uid, user);
          375 
          376         convTR2M(&tr, trbuf);
          377         if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
          378                 return -1;
          379 
          380         len = strlen(resp);
          381         if(xiowrite(s->asfd, resp, len) != len)
          382                 return -1;
          383 
          384         if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
          385                 return 0;
          386 
          387         convM2T(tabuf, &t, s->k->priv);
          388         if(t.num != AuthTs
          389         || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
          390                 werrstr("key mismatch with auth server");
          391                 return -1;
          392         }
          393 
          394         convM2A(tabuf+TICKETLEN, &a, t.key);
          395         if(a.num != AuthAc
          396         || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
          397         || a.id != 0){
          398                 werrstr("key2 mismatch with auth server");
          399                 return -1;
          400         }
          401 
          402         s->t = t;
          403         return 1;
          404 }
          405 
          406 static Role
          407 chaproles[] =
          408 {
          409         "client",        chapclient,
          410         "server",        chapserver,
          411         0
          412 };
          413 
          414 Proto chap = {
          415         "chap",
          416         chaproles,
          417         "user? !password?",
          418         chapcheck
          419 };
          420 
          421 Proto mschap = {
          422         "mschap",
          423         chaproles,
          424         "user? !password?",
          425         chapcheck
          426 };