|  | /* | 
|  | * p9cr - one-sided challenge/response authentication | 
|  | * | 
|  | * Protocol: | 
|  | * | 
|  | *	C -> S: user | 
|  | *	S -> C: challenge | 
|  | *	C -> S: response | 
|  | *	S -> C: ok or bad | 
|  | * | 
|  | * Note that this is the protocol between factotum and the local | 
|  | * program, not between the two factotums.  The information | 
|  | * exchanged here is wrapped in other protocols by the local | 
|  | * programs. | 
|  | */ | 
|  |  | 
|  | #include "std.h" | 
|  | #include "dat.h" | 
|  |  | 
|  | /* shared with auth dialing routines */ | 
|  | typedef struct ServerState ServerState; | 
|  | struct ServerState | 
|  | { | 
|  | int asfd; | 
|  | Key *k; | 
|  | Ticketreq tr; | 
|  | Ticket t; | 
|  | char *dom; | 
|  | char *hostid; | 
|  | }; | 
|  |  | 
|  | enum | 
|  | { | 
|  | MAXCHAL = 64, | 
|  | MAXRESP = 64, | 
|  | }; | 
|  |  | 
|  | extern Proto p9cr, vnc; | 
|  | static int p9response(char*, uchar*, uchar*); | 
|  | // static int vncresponse(char*, uchar*, uchar*); | 
|  | static int p9crchal(ServerState *s, int, char*, uchar*, int); | 
|  | static int p9crresp(ServerState*, uchar*, int); | 
|  |  | 
|  | static int | 
|  | p9crcheck(Key *k) | 
|  | { | 
|  | if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){ | 
|  | werrstr("need user and !password attributes"); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p9crclient(Conv *c) | 
|  | { | 
|  | char *pw, *res, *user; | 
|  | int astype, challen, resplen, ntry, ret; | 
|  | Attr *attr; | 
|  | Key *k; | 
|  | uchar chal[MAXCHAL+1], resp[MAXRESP]; | 
|  | int (*response)(char*, uchar*, uchar*); | 
|  |  | 
|  | k = nil; | 
|  | res = nil; | 
|  | ret = -1; | 
|  | attr = c->attr; | 
|  |  | 
|  | if(c->proto == &p9cr){ | 
|  | astype = AuthChal; | 
|  | challen = NETCHLEN; | 
|  | response = p9response; | 
|  | attr = _mkattr(AttrNameval, "proto", "p9sk1", _delattr(_copyattr(attr), "proto")); | 
|  | }else if(c->proto == &vnc){ | 
|  | astype = AuthVNC; | 
|  | challen = MAXCHAL; | 
|  | //	response = vncresponse; | 
|  | werrstr("no vnc"); | 
|  | goto out; | 
|  | }else{ | 
|  | werrstr("bad proto"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | c->state = "find key"; | 
|  | k = keyfetch(c, "%A %s", attr, c->proto->keyprompt); | 
|  | if(k == nil) | 
|  | goto out; | 
|  |  | 
|  | for(ntry=1;; ntry++){ | 
|  | if(c->attr != attr) | 
|  | freeattr(c->attr); | 
|  | c->attr = addattrs(copyattr(attr), k->attr); | 
|  |  | 
|  | if((pw = strfindattr(k->privattr, "!password")) == nil){ | 
|  | werrstr("key has no !password (cannot happen)"); | 
|  | goto out; | 
|  | } | 
|  | if((user = strfindattr(k->attr, "user")) == nil){ | 
|  | werrstr("key has no user (cannot happen)"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if(convprint(c, "%s", user) < 0) | 
|  | goto out; | 
|  |  | 
|  | if(convread(c, chal, challen) < 0) | 
|  | goto out; | 
|  | chal[challen] = 0; | 
|  |  | 
|  | if((resplen = (*response)(pw, chal, resp)) < 0) | 
|  | goto out; | 
|  |  | 
|  | if(convwrite(c, resp, resplen) < 0) | 
|  | goto out; | 
|  |  | 
|  | if(convreadm(c, &res) < 0) | 
|  | goto out; | 
|  |  | 
|  | if(strcmp(res, "ok") == 0) | 
|  | break; | 
|  |  | 
|  | if((k = keyreplace(c, k, "%s", res)) == nil){ | 
|  | c->state = "auth failed"; | 
|  | werrstr("%s", res); | 
|  | goto out; | 
|  | } | 
|  | } | 
|  |  | 
|  | werrstr("succeeded"); | 
|  | ret = 0; | 
|  |  | 
|  | out: | 
|  | keyclose(k); | 
|  | if(c->attr != attr) | 
|  | freeattr(attr); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p9crserver(Conv *c) | 
|  | { | 
|  | uchar chal[MAXCHAL], *resp, *resp1; | 
|  | char *user; | 
|  | ServerState s; | 
|  | int astype, ret, challen, resplen; | 
|  | Attr *a; | 
|  |  | 
|  | ret = -1; | 
|  | user = nil; | 
|  | resp = nil; | 
|  | memset(&s, 0, sizeof s); | 
|  | s.asfd = -1; | 
|  |  | 
|  | if(c->proto == &p9cr){ | 
|  | astype = AuthChal; | 
|  | challen = NETCHLEN; | 
|  | }else if(c->proto == &vnc){ | 
|  | astype = AuthVNC; | 
|  | challen = MAXCHAL; | 
|  | }else{ | 
|  | werrstr("bad proto"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | c->state = "find key"; | 
|  | if((s.k = plan9authkey(c->attr)) == nil) | 
|  | goto out; | 
|  |  | 
|  | a = copyattr(s.k->attr); | 
|  | a = delattr(a, "proto"); | 
|  | c->attr = addattrs(c->attr, a); | 
|  | freeattr(a); | 
|  |  | 
|  | c->state = "authdial"; | 
|  | s.hostid = strfindattr(s.k->attr, "user"); | 
|  | s.dom = strfindattr(s.k->attr, "dom"); | 
|  | if((s.asfd = xioauthdial(nil, s.dom)) < 0){ | 
|  | werrstr("authdial %s: %r", s.dom); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | for(;;){ | 
|  | c->state = "read user"; | 
|  | if(convreadm(c, &user) < 0) | 
|  | goto out; | 
|  |  | 
|  | c->state = "authchal"; | 
|  | if(p9crchal(&s, astype, user, chal, challen) < 0) | 
|  | goto out; | 
|  |  | 
|  | c->state = "write challenge"; | 
|  | if(convwrite(c, chal, challen) < 0) | 
|  | goto out; | 
|  |  | 
|  | c->state = "read response"; | 
|  | if((resplen = convreadm(c, (char**)(void*)&resp)) < 0) | 
|  | goto out; | 
|  | if(c->proto == &p9cr){ | 
|  | if(resplen > NETCHLEN){ | 
|  | convprint(c, "bad response too long"); | 
|  | goto out; | 
|  | } | 
|  | resp1 = emalloc(NETCHLEN); | 
|  | memset(resp1, 0, NETCHLEN); | 
|  | memmove(resp1, resp, resplen); | 
|  | free(resp); | 
|  | resp = resp1; | 
|  | resplen = NETCHLEN; | 
|  | } | 
|  |  | 
|  | c->state = "authwrite"; | 
|  | switch(p9crresp(&s, resp, resplen)){ | 
|  | case -1: | 
|  | fprint(2, "p9crresp: %r\n"); | 
|  | goto out; | 
|  | case 0: | 
|  | c->state = "write status"; | 
|  | if(convprint(c, "bad authentication failed") < 0) | 
|  | goto out; | 
|  | break; | 
|  | case 1: | 
|  | c->state = "write status"; | 
|  | if(convprint(c, "ok") < 0) | 
|  | goto out; | 
|  | goto ok; | 
|  | } | 
|  | free(user); | 
|  | free(resp); | 
|  | user = nil; | 
|  | resp = nil; | 
|  | } | 
|  |  | 
|  | ok: | 
|  | ret = 0; | 
|  | c->attr = addcap(c->attr, c->sysuser, &s.t); | 
|  |  | 
|  | out: | 
|  | keyclose(s.k); | 
|  | free(user); | 
|  | free(resp); | 
|  | xioclose(s.asfd); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p9crchal(ServerState *s, int astype, char *user, uchar *chal, int challen) | 
|  | { | 
|  | char trbuf[TICKREQLEN]; | 
|  | Ticketreq tr; | 
|  | int n; | 
|  |  | 
|  | memset(&tr, 0, sizeof tr); | 
|  |  | 
|  | tr.type = astype; | 
|  |  | 
|  | if(strlen(s->hostid) >= sizeof tr.hostid){ | 
|  | werrstr("hostid too long"); | 
|  | return -1; | 
|  | } | 
|  | strcpy(tr.hostid, s->hostid); | 
|  |  | 
|  | if(strlen(s->dom) >= sizeof tr.authdom){ | 
|  | werrstr("domain too long"); | 
|  | return -1; | 
|  | } | 
|  | strcpy(tr.authdom, s->dom); | 
|  |  | 
|  | if(strlen(user) >= sizeof tr.uid){ | 
|  | werrstr("user name too long"); | 
|  | return -1; | 
|  | } | 
|  | strcpy(tr.uid, user); | 
|  | convTR2M(&tr, trbuf); | 
|  |  | 
|  | if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) | 
|  | return -1; | 
|  |  | 
|  | if((n=xioasrdresp(s->asfd, chal, challen)) <= 0) | 
|  | return -1; | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p9crresp(ServerState *s, uchar *resp, int resplen) | 
|  | { | 
|  | char tabuf[TICKETLEN+AUTHENTLEN]; | 
|  | Authenticator a; | 
|  | Ticket t; | 
|  | Ticketreq tr; | 
|  |  | 
|  | if(xiowrite(s->asfd, resp, resplen) != resplen) | 
|  | return -1; | 
|  |  | 
|  | if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN) | 
|  | return 0; | 
|  |  | 
|  | convM2T(tabuf, &t, s->k->priv); | 
|  | if(t.num != AuthTs | 
|  | || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){ | 
|  | werrstr("key mismatch with auth server"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | convM2A(tabuf+TICKETLEN, &a, t.key); | 
|  | if(a.num != AuthAc | 
|  | || memcmp(a.chal, tr.chal, sizeof a.chal) != 0 | 
|  | || a.id != 0){ | 
|  | werrstr("key2 mismatch with auth server"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | s->t = t; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p9response(char *pw, uchar *chal, uchar *resp) | 
|  | { | 
|  | char key[DESKEYLEN]; | 
|  | uchar buf[8]; | 
|  | ulong x; | 
|  |  | 
|  | passtokey(key, pw); | 
|  | memset(buf, 0, 8); | 
|  | snprint((char*)buf, sizeof buf, "%d", atoi((char*)chal)); | 
|  | if(encrypt(key, buf, 8) < 0){ | 
|  | werrstr("can't encrypt response"); | 
|  | return -1; | 
|  | } | 
|  | x = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3]; | 
|  | return snprint((char*)resp, MAXRESP, "%.8lux", x); | 
|  | } | 
|  |  | 
|  | /* | 
|  | static int | 
|  | vncresponse(char *pw, uchar *chal, uchar *resp) | 
|  | { | 
|  | DESstate des; | 
|  |  | 
|  | memmove(resp, chal, MAXCHAL); | 
|  | setupDESstate(&des, 0, nil);  // XXX put key in for 0 | 
|  | desECBencrypt(resp, MAXCHAL, &des); | 
|  | return MAXCHAL; | 
|  | } | 
|  | */ | 
|  |  | 
|  | static Role | 
|  | p9crroles[] = | 
|  | { | 
|  | "client", p9crclient, | 
|  | "server", p9crserver, | 
|  | 0 | 
|  | }; | 
|  |  | 
|  | Proto p9cr = { | 
|  | "p9cr", | 
|  | p9crroles, | 
|  | "user? !password?", | 
|  | p9crcheck, | 
|  | nil | 
|  | }; | 
|  |  | 
|  | /* still need to implement vnc key generator */ | 
|  | Proto vnc = { | 
|  | "vnc", | 
|  | p9crroles, | 
|  | "user? !password?", | 
|  | p9crcheck, | 
|  | nil | 
|  | }; |