diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/apop.c /sys/src/cmd/auth/factotum/apop.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/apop.c Wed Oct 19 22:36:52 2011 +++ /sys/src/cmd/auth/factotum/apop.c Tue Jan 1 00:00:00 2013 @@ -1,324 +1,360 @@ /* * APOP, CRAM - MD5 challenge/response authentication * - * The client does not authenticate the server, hence no CAI + * The client does not authenticate the server, hence no CAI. * - * Client protocol: - * write challenge: randomstring@domain - * read response: 2*MD5dlen hex digits + * Protocol: * - * Server protocol: - * read challenge: randomstring@domain - * write user: user - * write response: 2*MD5dlen hex digits + * S -> C: random@domain + * C -> S: user hex-response + * S -> C: ok + * + * Note that this is the protocol between factotum and the local + * program, not between the two factotums. The information + * exchanged here is wrapped in the APOP protocol by the local + * programs. + * + * If S sends "bad [msg]" instead of "ok", that is a hint that the key is bad. + * The protocol goes back to "C -> S: user hex-response". */ +#include "std.h" #include "dat.h" -struct State -{ - int asfd; - int astype; - Key *key; - Ticket t; - Ticketreq tr; - char chal[128]; - char resp[64]; - char *user; -}; - -enum -{ - CNeedChal, - CHaveResp, - - SHaveChal, - SNeedUser, - SNeedResp, - - Maxphase, -}; - -static char *phasenames[Maxphase] = { -[CNeedChal] "CNeedChal", -[CHaveResp] "CHaveResp", - -[SHaveChal] "SHaveChal", -[SNeedUser] "SNeedUser", -[SNeedResp] "SNeedResp", -}; - -static int dochal(State*); -static int doreply(State*, char*, char*); +extern Proto apop, cram; static int -apopinit(Proto *p, Fsstate *fss) +apopcheck(Key *k) { - int iscli, ret; - State *s; - - if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) - return failure(fss, nil); - - s = emalloc(sizeof *s); - fss->phasename = phasenames; - fss->maxphase = Maxphase; - s->asfd = -1; - if(p == &apop) - s->astype = AuthApop; - else if(p == &cram) - s->astype = AuthCram; - else - abort(); - - if(iscli) - fss->phase = CNeedChal; - else{ - if((ret = findp9authkey(&s->key, fss)) != RpcOk){ - free(s); - return ret; - } - if(dochal(s) < 0){ - free(s); - return failure(fss, nil); - } - fss->phase = SHaveChal; + if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){ + werrstr("need user and !password attributes"); + return -1; } - fss->ps = s; - return RpcOk; + return 0; } static int -apopwrite(Fsstate *fss, void *va, uint n) +apopclient(Conv *c) { - char *a, *v; - int i, ret; - uchar digest[MD5dlen]; + char *chal, *pw, *res; + int astype, nchal, npw, ntry, ret; + uchar resp[MD5dlen]; + Attr *attr; DigestState *ds; Key *k; - State *s; - Keyinfo ki; + + chal = nil; + k = nil; + res = nil; + ret = -1; + attr = c->attr; + + if(c->proto == &apop) + astype = AuthApop; + else if(c->proto == &cram) + astype = AuthCram; + else{ + werrstr("bad proto"); + goto out; + } - s = fss->ps; - a = va; - switch(fss->phase){ - default: - return phaseerror(fss, "write"); - - case CNeedChal: - ret = findkey(&k, mkkeyinfo(&ki, fss, nil), "%s", fss->proto->keyprompt); - if(ret != RpcOk) - return ret; - v = _strfindattr(k->privattr, "!password"); - if(v == nil) - return failure(fss, "key has no password"); - setattrs(fss->attr, k->attr); - switch(s->astype){ - default: - abort(); + c->state = "find key"; + k = keyfetch(c, "%A %s", attr, c->proto->keyprompt); + if(k == nil) + goto out; + + c->state = "read challenge"; + if((nchal = convreadm(c, &chal)) < 0) + 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; + } + npw = strlen(pw); + + switch(astype){ + case AuthApop: + ds = md5((uchar*)chal, nchal, nil, nil); + md5((uchar*)pw, npw, resp, ds); + break; case AuthCram: - hmac_md5((uchar*)a, n, (uchar*)v, strlen(v), - digest, nil); - snprint(s->resp, sizeof s->resp, "%.*H", MD5dlen, digest); + hmac_md5((uchar*)chal, nchal, (uchar*)pw, npw, resp, nil); break; - case AuthApop: - ds = md5((uchar*)a, n, nil, nil); - md5((uchar*)v, strlen(v), digest, ds); - for(i=0; iresp[2*i], "%2.2x", digest[i]); + } + + /* C->S: APOP user hex-response\n */ +/* + if(ntry == 1) + c->state = "write user"; + else{ + sprint(c->statebuf, "write user (auth attempt #%d)", ntry); + c->state = c->statebuf; + } + if(convprint(c, "%s", strfindattr(k->attr, "user")) < 0) + goto out; +*/ + + c->state = "write response"; + if(convprint(c, "%.*H", sizeof resp, resp) < 0) + goto out; + + c->state = "read result"; + if(convreadm(c, &res) < 0) + goto out; + + if(strcmp(res, "ok") == 0) break; + + if(strncmp(res, "bad ", 4) != 0){ + werrstr("bad result: %s", res); + goto out; } - closekey(k); - fss->phase = CHaveResp; - return RpcOk; - - case SNeedUser: - if((v = _strfindattr(fss->attr, "user")) && strcmp(v, a) != 0) - return failure(fss, "bad user"); - fss->attr = setattr(fss->attr, "user=%q", a); - s->user = estrdup(a); - fss->phase = SNeedResp; - return RpcOk; - - case SNeedResp: - if(n != 2*MD5dlen) - return failure(fss, "response not MD5 digest"); - if(doreply(s, s->user, a) < 0){ - fss->phase = SNeedUser; - return failure(fss, nil); + + c->state = "replace key"; + if((k = keyreplace(c, k, "%s", res+4)) == nil){ + c->state = "auth failed"; + werrstr("%s", res+4); + goto out; } - fss->haveai = 1; - fss->ai.cuid = s->t.cuid; - fss->ai.suid = s->t.suid; - fss->ai.nsecret = 0; - fss->ai.secret = nil; - fss->phase = Established; - return RpcOk; + free(res); + res = nil; } -} -static int -apopread(Fsstate *fss, void *va, uint *n) -{ - State *s; + werrstr("succeeded"); + ret = 0; - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "read"); - - case CHaveResp: - if(*n > strlen(s->resp)) - *n = strlen(s->resp); - memmove(va, s->resp, *n); - fss->phase = Established; - fss->haveai = 0; - return RpcOk; - - case SHaveChal: - if(*n > strlen(s->chal)) - *n = strlen(s->chal); - memmove(va, s->chal, *n); - fss->phase = SNeedUser; - return RpcOk; - } +out: + keyclose(k); + free(chal); + if(c->attr != attr) + freeattr(attr); + return ret; } -static void -apopclose(Fsstate *fss) +/* shared with auth dialing routines */ +typedef struct ServerState ServerState; +struct ServerState +{ + int asfd; + Key *k; + Ticketreq tr; + Ticket t; + char *dom; + char *hostid; +}; + +enum { - State *s; + APOPCHALLEN = 128 +}; + +static int apopchal(ServerState*, int, char[APOPCHALLEN]); +static int apopresp(ServerState*, char*, char*); - s = fss->ps; - if(s->asfd >= 0){ - close(s->asfd); - s->asfd = -1; - } - if(s->key != nil){ - closekey(s->key); - s->key = nil; - } - if(s->user != nil){ - free(s->user); - s->user = nil; +static int +apopserver(Conv *c) +{ + char chal[APOPCHALLEN], *user, *resp; + ServerState s; + int astype, ret; + Attr *a; + + ret = -1; + user = nil; + resp = nil; + memset(&s, 0, sizeof s); + s.asfd = -1; + + if(c->proto == &apop) + astype = AuthApop; + else if(c->proto == &cram) + astype = AuthCram; + else{ + werrstr("bad proto"); + goto out; } - free(s); + + 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; + } + + c->state = "authchal"; + if(apopchal(&s, astype, chal) < 0) + goto out; + + c->state = "write challenge"; + if(convprint(c, "%s", chal) < 0) + goto out; + + for(;;){ + c->state = "read user"; + if(convreadm(c, &user) < 0) + goto out; + c->state = "read response"; + if(convreadm(c, &resp) < 0) + goto out; + c->state = "authwrite"; + switch(apopresp(&s, user, resp)){ + default: + case -1: + goto out; + case 0: + c->state = "write status"; + if(convprint(c, "bad authentication failed") < 0) + goto out; + break; + case 1: + c->done = 1; + c->active = 0; + 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 -dochal(State *s) +apopchal(ServerState *s, int astype, char chal[APOPCHALLEN]) { - char *dom, *user, trbuf[TICKREQLEN]; + char trbuf[TICKREQLEN]; + Ticketreq tr; - s->asfd = -1; + memset(&tr, 0, sizeof tr); - /* send request to authentication server and get challenge */ - /* send request to authentication server and get challenge */ - if((dom = _strfindattr(s->key->attr, "dom")) == nil - || (user = _strfindattr(s->key->attr, "user")) == nil){ - werrstr("apop/dochal cannot happen"); - goto err; - } - - s->asfd = _authdial(nil, dom); - - /* could generate our own challenge on error here */ - if(s->asfd < 0) - goto err; - - memset(&s->tr, 0, sizeof(s->tr)); - s->tr.type = s->astype; - safecpy(s->tr.authdom, dom, sizeof s->tr.authdom); - safecpy(s->tr.hostid, user, sizeof(s->tr.hostid)); - convTR2M(&s->tr, trbuf); - - if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) - goto err; - if(_asrdresp(s->asfd, s->chal, sizeof s->chal) <= 5) - goto err; - return 0; + 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); + + convTR2M(&tr, trbuf); + if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + return -1; + + if(xioasrdresp(s->asfd, chal, APOPCHALLEN) <= 5) + return -1; -err: - if(s->asfd >= 0) - close(s->asfd); - s->asfd = -1; - return -1; + s->tr = tr; + return 0; } static int -doreply(State *s, char *user, char *response) +apopresp(ServerState *s, char *user, char *resp) { - char ticket[TICKETLEN+AUTHENTLEN]; + char tabuf[TICKETLEN+AUTHENTLEN]; char trbuf[TICKREQLEN]; - int n; + int len; Authenticator a; + Ticket t; + Ticketreq tr; + + tr = s->tr; + if(memrandom(tr.chal, CHALLEN) < 0) + return -1; - memrandom(s->tr.chal, CHALLEN); - safecpy(s->tr.uid, user, sizeof(s->tr.uid)); - convTR2M(&s->tr, trbuf); - if((n=write(s->asfd, trbuf, TICKREQLEN)) != TICKREQLEN){ - if(n >= 0) - werrstr("short write to auth server"); - goto err; + if(strlen(user) >= sizeof tr.uid){ + werrstr("uid too long"); + return -1; } - /* send response to auth server */ - if(strlen(response) != MD5dlen*2){ + strcpy(tr.uid, user); + + convTR2M(&tr, trbuf); + if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + return -1; + + len = strlen(resp); + if(len != 2*MD5dlen){ werrstr("response not MD5 digest"); - goto err; + return -1; } - if((n=write(s->asfd, response, MD5dlen*2)) != MD5dlen*2){ - if(n >= 0) - werrstr("short write to auth server"); - goto err; - } - if(_asrdresp(s->asfd, ticket, TICKETLEN+AUTHENTLEN) < 0){ - /* leave connection open so we can try again */ - return -1; - } - close(s->asfd); - s->asfd = -1; - - convM2T(ticket, &s->t, (char*)s->key->priv); - if(s->t.num != AuthTs - || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ - if(s->key->successes == 0) - disablekey(s->key); - werrstr(Easproto); - goto err; + if(xiowrite(s->asfd, resp, len) != len) + 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; } - s->key->successes++; - convM2A(ticket+TICKETLEN, &a, s->t.key); + + convM2A(tabuf+TICKETLEN, &a, t.key); if(a.num != AuthAc - || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0 + || memcmp(a.chal, tr.chal, sizeof a.chal) != 0 || a.id != 0){ - werrstr(Easproto); - goto err; + werrstr("key2 mismatch with auth server"); + return -1; } - return 0; -err: - if(s->asfd >= 0) - close(s->asfd); - s->asfd = -1; - return -1; + s->t = t; + return 1; } +static Role +apoproles[] = +{ + "client", apopclient, + "server", apopserver, + 0 +}; + Proto apop = { -.name= "apop", -.init= apopinit, -.write= apopwrite, -.read= apopread, -.close= apopclose, -.addkey= replacekey, -.keyprompt= "!password?" + "apop", + apoproles, + "user? !password?", + apopcheck, + nil }; Proto cram = { -.name= "cram", -.init= apopinit, -.write= apopwrite, -.read= apopread, -.close= apopclose, -.addkey= replacekey, -.keyprompt= "!password?" + "cram", + apoproles, + "user? !password?", + apopcheck, + nil }; + diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/attr.c /sys/src/cmd/auth/factotum/attr.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/attr.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/attr.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,231 @@ +#include "std.h" +#include "dat.h" + +Attr* +addattr(Attr *a, char *fmt, ...) +{ + char buf[8192]; + va_list arg; + Attr *b; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof buf, fmt, arg); + va_end(arg); + b = _parseattr(buf); + a = addattrs(a, b); + setmalloctag(a, getcallerpc(&a)); + _freeattr(b); + return a; +} + +/* + * add attributes in list b to list a. If any attributes are in + * both lists, replace those in a by those in b. + */ +Attr* +addattrs(Attr *a, Attr *b) +{ + int found; + Attr **l, *aa; + + for(; b; b=b->next){ + switch(b->type){ + case AttrNameval: + for(l=&a; *l; ){ + if(strcmp((*l)->name, b->name) != 0){ + l=&(*l)->next; + continue; + } + aa = *l; + *l = aa->next; + aa->next = nil; + freeattr(aa); + } + *l = mkattr(AttrNameval, b->name, b->val, nil); + break; + case AttrQuery: + found = 0; + for(l=&a; *l; l=&(*l)->next) + if((*l)->type==AttrNameval && strcmp((*l)->name, b->name) == 0) + found++; + if(!found) + *l = mkattr(AttrQuery, b->name, b->val, nil); + break; + } + } + return a; +} + +void +setmalloctaghere(void *v) +{ + setmalloctag(v, getcallerpc(&v)); +} + +Attr* +sortattr(Attr *a) +{ + int i; + Attr *anext, *a0, *a1, **l; + + if(a == nil || a->next == nil) + return a; + + /* cut list in halves */ + a0 = nil; + a1 = nil; + i = 0; + for(; a; a=anext){ + anext = a->next; + if(i++%2){ + a->next = a0; + a0 = a; + }else{ + a->next = a1; + a1 = a; + } + } + + /* sort */ + a0 = sortattr(a0); + a1 = sortattr(a1); + + /* merge */ + l = &a; + while(a0 || a1){ + if(a1==nil){ + anext = a0; + a0 = a0->next; + }else if(a0==nil){ + anext = a1; + a1 = a1->next; + }else if(strcmp(a0->name, a1->name) < 0){ + anext = a0; + a0 = a0->next; + }else{ + anext = a1; + a1 = a1->next; + } + *l = anext; + l = &(*l)->next; + } + *l = nil; + return a; +} + +int +attrnamefmt(Fmt *fmt) +{ + char *b, buf[8192], *ebuf; + Attr *a; + + ebuf = buf+sizeof buf; + b = buf; + strcpy(buf, " "); + for(a=va_arg(fmt->args, Attr*); a; a=a->next){ + if(a->name == nil) + continue; + b = seprint(b, ebuf, " %q?", a->name); + } + return fmtstrcpy(fmt, buf+1); +} + +/* +static int +hasqueries(Attr *a) +{ + for(; a; a=a->next) + if(a->type == AttrQuery) + return 1; + return 0; +} +*/ + +char *ignored[] = { + "role", + "disabled" +}; + +static int +ignoreattr(char *s) +{ + int i; + + for(i=0; inext, name)) + if(strcmp(a->val, val) == 0) + return 1; + for(a=_findattr(a1, name); a; a=_findattr(a->next, name)) + if(strcmp(a->val, val) == 0) + return 1; + return 0; +} + +int +matchattr(Attr *pat, Attr *a0, Attr *a1) +{ + int type; + + for(; pat; pat=pat->next){ + type = pat->type; + if(ignoreattr(pat->name)) + type = AttrDefault; + switch(type){ + case AttrQuery: /* name=something be present */ + if(!hasname(a0, a1, pat->name)) + return 0; + break; + case AttrNameval: /* name=val must be present */ + if(!hasnameval(a0, a1, pat->name, pat->val)) + return 0; + break; + case AttrDefault: /* name=val must be present if name=anything is present */ + if(hasname(a0, a1, pat->name) && !hasnameval(a0, a1, pat->name, pat->val)) + return 0; + break; + } + } + return 1; +} + +Attr* +parseattrfmtv(char *fmt, va_list arg) +{ + char *s; + Attr *a; + + s = vsmprint(fmt, arg); + if(s == nil) + sysfatal("vsmprint: out of memory"); + a = parseattr(s); + free(s); + return a; +} + +Attr* +parseattrfmt(char *fmt, ...) +{ + va_list arg; + Attr *a; + + va_start(arg, fmt); + a = parseattrfmtv(fmt, arg); + va_end(arg); + return a; +} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/chap.c /sys/src/cmd/auth/factotum/chap.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/chap.c Sat Feb 17 05:28:32 2007 +++ /sys/src/cmd/auth/factotum/chap.c Tue Jan 1 00:00:00 2013 @@ -3,450 +3,421 @@ * * The client does not authenticate the server, hence no CAI * - * Client protocol: - * write Chapchal - * read response Chapreply or MSchaprely structure + * Protocol: * - * Server protocol: - * read challenge: 8 bytes binary - * write user: utf8 - * write response: Chapreply or MSchapreply structure + * S -> C: random 8-byte challenge + * C -> S: user in UTF-8 + * C -> S: Chapreply or MSchapreply structure + * S -> C: ok or 'bad why' + * + * The chap protocol requires the client to give it id=%d, the id of + * the PPP message containing the challenge, which is used + * as part of the response. Because the client protocol is message-id + * specific, there is no point in looping to try multiple keys. + * + * The MS chap protocol actually uses two different hashes, an + * older insecure one called the LM (Lan Manager) hash, and a newer + * more secure one called the NT hash. By default we send back only + * the NT hash, because the LM hash can help an eavesdropper run + * a brute force attack. If the key has an lm attribute, then we send only the + * LM hash. */ -#include +#include "std.h" #include "dat.h" +extern Proto chap, mschap; + enum { ChapChallen = 8, - ChapResplen = 16, - MSchapResplen = 24, -}; - -static int dochal(State*); -static int doreply(State*, void*, int); -static void doLMchap(char *, uchar [ChapChallen], uchar [MSchapResplen]); -static void doNTchap(char *, uchar [ChapChallen], uchar [MSchapResplen]); -static void dochap(char *, int, char [ChapChallen], uchar [ChapResplen]); + MShashlen = 16, + MSchallen = 8, + MSresplen = 24 +}; -struct State +static int +chapcheck(Key *k) { - char *protoname; - int astype; - int asfd; - Key *key; - Ticket t; - Ticketreq tr; - char chal[ChapChallen]; - MSchapreply mcr; - char cr[ChapResplen]; - char err[ERRMAX]; - char user[64]; - uchar secret[16]; /* for mschap */ - int nsecret; -}; + if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){ + werrstr("need user and !password attributes"); + return -1; + } + return 0; +} -enum +static void +nthash(uchar hash[MShashlen], char *passwd) { - CNeedChal, - CHaveResp, + uchar buf[512]; + int i; + + for(i=0; *passwd && iattr, "role"))) < 0) - return failure(fss, nil); + strncpy((char*)buf, passwd, sizeof(buf)); + for(i=0; i= 'a' && buf[i] <= 'z') + buf[i] += 'A' - 'a'; + + memset(hash, 0, 16); + memcpy(hash, stdtext, 8); + memcpy(hash+8, stdtext, 8); - s = emalloc(sizeof *s); - fss->phasename = phasenames; - fss->maxphase = Maxphase; - s->asfd = -1; - if(p == &chap){ - s->astype = AuthChap; - s->protoname = "chap"; - }else{ - s->astype = AuthMSchap; - s->protoname = "mschap"; - } - - if(iscli) - fss->phase = CNeedChal; - else{ - if((ret = findp9authkey(&s->key, fss)) != RpcOk){ - free(s); - return ret; - } - if(dochal(s) < 0){ - free(s); - return failure(fss, nil); - } - fss->phase = SHaveChal; - } - - fss->ps = s; - return RpcOk; + desencrypt(hash, buf); + desencrypt(hash+8, buf+7); } static void -chapclose(Fsstate *fss) +mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]) { - State *s; + int i; + uchar buf[21]; + + memset(buf, 0, sizeof(buf)); + memcpy(buf, hash, MShashlen); - s = fss->ps; - if(s->asfd >= 0){ - close(s->asfd); - s->asfd = -1; + for(i=0; i<3; i++) { + memmove(resp+i*MSchallen, chal, MSchallen); + desencrypt(resp+i*MSchallen, buf+i*7); } - free(s); } - static int -chapwrite(Fsstate *fss, void *va, uint n) +chapclient(Conv *c) { - int ret, nreply; - char *a, *v; - void *reply; + int id, astype, nchal, npw, ret; + uchar *chal; + char *s, *pw, *res; + Attr *attr; Key *k; - Keyinfo ki; - State *s; Chapreply cr; - MSchapreply mcr; - OChapreply ocr; - OMSchapreply omcr; - - s = fss->ps; - a = va; - switch(fss->phase){ - default: - return phaseerror(fss, "write"); + MSchapreply mscr; + DigestState *ds; - case CNeedChal: - ret = findkey(&k, mkkeyinfo(&ki, fss, nil), "%s", fss->proto->keyprompt); - if(ret != RpcOk) - return ret; - v = _strfindattr(k->privattr, "!password"); - if(v == nil) - return failure(fss, "key has no password"); - setattrs(fss->attr, k->attr); - switch(s->astype){ - default: - abort(); - case AuthMSchap: - doLMchap(v, (uchar *)a, (uchar *)s->mcr.LMresp); - doNTchap(v, (uchar *)a, (uchar *)s->mcr.NTresp); - break; - case AuthChap: - dochap(v, *a, a+1, (uchar *)s->cr); - break; + ret = -1; + chal = nil; + k = nil; + attr = c->attr; + + if(c->proto == &chap){ + astype = AuthChap; + s = strfindattr(attr, "id"); + if(s == nil || *s == 0){ + werrstr("need id=n attr in start message"); + goto out; } - closekey(k); - fss->phase = CHaveResp; - return RpcOk; - - case SNeedUser: - if(n >= sizeof s->user) - return failure(fss, "user name too long"); - memmove(s->user, va, n); - s->user[n] = '\0'; - fss->phase = SNeedResp; - return RpcOk; - - case SNeedResp: - switch(s->astype){ - default: - return failure(fss, "chap internal botch"); - case AuthChap: - if(n != sizeof(Chapreply)) - return failure(fss, "did not get Chapreply"); - memmove(&cr, va, sizeof cr); - ocr.id = cr.id; - memmove(ocr.resp, cr.resp, sizeof ocr.resp); - memset(omcr.uid, 0, sizeof(omcr.uid)); - strecpy(ocr.uid, ocr.uid+sizeof ocr.uid, s->user); - reply = &ocr; - nreply = sizeof ocr; - break; - case AuthMSchap: - if(n != sizeof(MSchapreply)) - return failure(fss, "did not get MSchapreply"); - memmove(&mcr, va, sizeof mcr); - memmove(omcr.LMresp, mcr.LMresp, sizeof omcr.LMresp); - memmove(omcr.NTresp, mcr.NTresp, sizeof omcr.NTresp); - memset(omcr.uid, 0, sizeof(omcr.uid)); - strecpy(omcr.uid, omcr.uid+sizeof omcr.uid, s->user); - reply = &omcr; - nreply = sizeof omcr; - break; + id = strtol(s, &s, 10); + if(*s != 0 || id < 0 || id >= 256){ + werrstr("bad id=n attr in start message"); + goto out; + } + cr.id = id; + }else if(c->proto == &mschap) + astype = AuthMSchap; + else{ + werrstr("bad proto"); + goto out; + } + + c->state = "find key"; + k = keyfetch(c, "%A %s", attr, c->proto->keyprompt); + if(k == nil) + goto out; + + c->attr = addattrs(copyattr(attr), k->attr); + + c->state = "read challenge"; + if((nchal = convreadm(c, (char**)(void*)&chal)) < 0) + goto out; + if(strfindattr(k->attr, "user") == nil){ + werrstr("key has no user (cannot happen?)"); + goto out; + } + + c->state = "write response"; + if((pw = strfindattr(k->privattr, "!password")) == nil){ + werrstr("key has no password (cannot happen?)"); + goto out; + } + npw = strlen(pw); + + if(astype == AuthChap){ + ds = md5(&cr.id, 1, 0, 0); + md5((uchar*)pw, npw, 0, ds); + md5(chal, nchal, (uchar*)cr.resp, ds); + if(convwrite(c, &cr, sizeof cr) < 0) + goto out; + }else{ + uchar hash[MShashlen]; + + memset(&mscr, 0, sizeof mscr); + if(strfindattr(k->attr, "lm")){ + lmhash(hash, pw); + mschalresp((uchar*)mscr.LMresp, hash, chal); + }else{ + nthash(hash, pw); + mschalresp((uchar*)mscr.NTresp, hash, chal); } - if(doreply(s, reply, nreply) < 0) - return failure(fss, nil); - fss->phase = Established; - fss->ai.cuid = s->t.cuid; - fss->ai.suid = s->t.suid; - fss->ai.secret = s->secret; - fss->ai.nsecret = s->nsecret; - fss->haveai = 1; - return RpcOk; + if(convwrite(c, &mscr, sizeof mscr) < 0) + goto out; } + + c->state = "read result"; + if(convreadm(c, &res) < 0) + goto out; + if(strcmp(res, "ok") == 0){ + ret = 0; + werrstr("succeeded"); + goto out; + } + if(strncmp(res, "bad ", 4) != 0){ + werrstr("bad result: %s", res); + goto out; + } + + c->state = "replace key"; + keyevict(c, k, "%s", res+4); + werrstr("%s", res+4); + +out: + free(res); + keyclose(k); + free(chal); + if(c->attr != attr) + freeattr(attr); + return ret; } +/* shared with auth dialing routines */ +typedef struct ServerState ServerState; +struct ServerState +{ + int asfd; + Key *k; + Ticketreq tr; + Ticket t; + char *dom; + char *hostid; +}; + +static int chapchal(ServerState*, int, char[ChapChallen]); +static int chapresp(ServerState*, char*, char*); + static int -chapread(Fsstate *fss, void *va, uint *n) +chapserver(Conv *c) { - State *s; + char chal[ChapChallen], *user, *resp; + ServerState s; + int astype, ret; + Attr *a; + + ret = -1; + user = nil; + resp = nil; + memset(&s, 0, sizeof s); + s.asfd = -1; + + if(c->proto == &chap) + astype = AuthChap; + else if(c->proto == &mschap) + astype = AuthMSchap; + else{ + werrstr("bad proto"); + goto out; + } - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "read"); + c->state = "find key"; + if((s.k = plan9authkey(c->attr)) == nil) + goto out; - case CHaveResp: - switch(s->astype){ - default: - phaseerror(fss, "write"); - break; - case AuthMSchap: - if(*n > sizeof(MSchapreply)) - *n = sizeof(MSchapreply); - memmove(va, &s->mcr, *n); - break; - case AuthChap: - if(*n > ChapResplen) - *n = ChapResplen; - memmove(va, s->cr, ChapResplen); - break; - } - fss->phase = Established; - fss->haveai = 0; - return RpcOk; - - case SHaveChal: - if(*n > sizeof s->chal) - *n = sizeof s->chal; - memmove(va, s->chal, *n); - fss->phase = SNeedUser; - return RpcOk; + 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; } + + c->state = "authchal"; + if(chapchal(&s, astype, chal) < 0) + goto out; + + c->state = "write challenge"; + if(convprint(c, "%s", chal) < 0) + goto out; + + c->state = "read user"; + if(convreadm(c, &user) < 0) + goto out; + + c->state = "read response"; + if(convreadm(c, &resp) < 0) + goto out; + + c->state = "authwrite"; + switch(chapresp(&s, user, resp)){ + default: + fprint(2, "factotum: bad result from chapresp\n"); + goto out; + case -1: + goto out; + case 0: + c->state = "write status"; + if(convprint(c, "bad authentication failed") < 0) + goto out; + goto out; + + case 1: + c->state = "write status"; + if(convprint(c, "ok") < 0) + goto out; + goto ok; + } + +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 -dochal(State *s) +chapchal(ServerState *s, int astype, char chal[ChapChallen]) { - char *dom, *user; char trbuf[TICKREQLEN]; + Ticketreq tr; - s->asfd = -1; + memset(&tr, 0, sizeof tr); - /* send request to authentication server and get challenge */ - if((dom = _strfindattr(s->key->attr, "dom")) == nil - || (user = _strfindattr(s->key->attr, "user")) == nil){ - werrstr("chap/dochal cannot happen"); - goto err; - } - s->asfd = _authdial(nil, dom); - if(s->asfd < 0) - goto err; - - memset(&s->tr, 0, sizeof(s->tr)); - s->tr.type = s->astype; - safecpy(s->tr.authdom, dom, sizeof s->tr.authdom); - safecpy(s->tr.hostid, user, sizeof(s->tr.hostid)); - convTR2M(&s->tr, trbuf); - - if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) - goto err; - - /* readn, not _asrdresp. needs to match auth.srv.c. */ - if(readn(s->asfd, s->chal, sizeof s->chal) != sizeof s->chal) - goto err; - return 0; + 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); + + convTR2M(&tr, trbuf); + if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + return -1; + + if(xioasrdresp(s->asfd, chal, ChapChallen) <= 5) + return -1; -err: - if(s->asfd >= 0) - close(s->asfd); - s->asfd = -1; - return -1; + s->tr = tr; + return 0; } static int -doreply(State *s, void *reply, int nreply) +chapresp(ServerState *s, char *user, char *resp) { - char ticket[TICKETLEN+AUTHENTLEN]; - int n; + char tabuf[TICKETLEN+AUTHENTLEN]; + char trbuf[TICKREQLEN]; + int len; Authenticator a; + Ticket t; + Ticketreq tr; - if((n=write(s->asfd, reply, nreply)) != nreply){ - if(n >= 0) - werrstr("short write to auth server"); - goto err; - } - - if(_asrdresp(s->asfd, ticket, TICKETLEN+AUTHENTLEN) < 0){ - /* leave connection open so we can try again */ - return -1; - } - s->nsecret = readn(s->asfd, s->secret, sizeof s->secret); - if(s->nsecret < 0) - s->nsecret = 0; - close(s->asfd); - s->asfd = -1; - convM2T(ticket, &s->t, s->key->priv); - if(s->t.num != AuthTs - || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ - if(s->key->successes == 0) - disablekey(s->key); - werrstr(Easproto); + tr = s->tr; + if(memrandom(tr.chal, CHALLEN) < 0) return -1; - } - s->key->successes++; - convM2A(ticket+TICKETLEN, &a, s->t.key); - if(a.num != AuthAc - || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0 - || a.id != 0){ - werrstr(Easproto); + + if(strlen(user) >= sizeof tr.uid){ + werrstr("uid too long"); return -1; } + strcpy(tr.uid, user); - return 0; -err: - if(s->asfd >= 0) - close(s->asfd); - s->asfd = -1; - return -1; -} - -Proto chap = { -.name= "chap", -.init= chapinit, -.write= chapwrite, -.read= chapread, -.close= chapclose, -.addkey= replacekey, -.keyprompt= "!password?" -}; + convTR2M(&tr, trbuf); + if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + return -1; -Proto mschap = { -.name= "mschap", -.init= chapinit, -.write= chapwrite, -.read= chapread, -.close= chapclose, -.addkey= replacekey, -.keyprompt= "!password?" -}; + len = strlen(resp); + if(xiowrite(s->asfd, resp, len) != len) + return -1; -static void -hash(uchar pass[16], uchar c8[ChapChallen], uchar p24[MSchapResplen]) -{ - int i; - uchar p21[21]; - ulong schedule[32]; + if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN) + return 0; - memset(p21, 0, sizeof p21 ); - memmove(p21, pass, 16); + 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; + } - for(i=0; i<3; i++) { - key_setup(p21+i*7, schedule); - memmove(p24+i*8, c8, 8); - block_cipher(schedule, p24+i*8, 0); + 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; } -} -static void -doNTchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen]) -{ - Rune r; - int i, n; - uchar digest[MD4dlen]; - uchar *w, unipass[256]; - - // Standard says unlimited length, experience says 128 max - if ((n = strlen(pass)) > 128) - n = 128; - - for(i=0, w=unipass; i < n; i++) { - pass += chartorune(&r, pass); - *w++ = r & 0xff; - *w++ = r >> 8; - } - - memset(digest, 0, sizeof digest); - md4(unipass, w-unipass, digest, nil); - memset(unipass, 0, sizeof unipass); - hash(digest, chal, reply); + s->t = t; + return 1; } -static void -doLMchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen]) +static Role +chaproles[] = { - int i; - ulong schedule[32]; - uchar p14[15], p16[16]; - uchar s8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; - int n = strlen(pass); - - if(n > 14){ - // let prudent people avoid the LM vulnerability - // and protect the loop below from buffer overflow - memset(reply, 0, MSchapResplen); - return; - } - - // Spec says space padded, experience says otherwise - memset(p14, 0, sizeof p14 -1); - p14[sizeof p14 - 1] = '\0'; - - // NT4 requires uppercase, Win XP doesn't care - for (i = 0; pass[i]; i++) - p14[i] = islower(pass[i])? toupper(pass[i]): pass[i]; - - for(i=0; i<2; i++) { - key_setup(p14+i*7, schedule); - memmove(p16+i*8, s8, 8); - block_cipher(schedule, p16+i*8, 0); - } + "client", chapclient, + "server", chapserver, + 0 +}; - memset(p14, 0, sizeof p14); - hash(p16, chal, reply); -} +Proto chap = { + "chap", + chaproles, + "user? !password?", + chapcheck +}; -static void -dochap(char *pass, int id, char chal[ChapChallen], uchar resp[ChapResplen]) -{ - char buf[1+ChapChallen+MAXNAMELEN+1]; - int n = strlen(pass); +Proto mschap = { + "mschap", + chaproles, + "user? !password?", + chapcheck +}; - *buf = id; - if (n > MAXNAMELEN) - n = MAXNAMELEN-1; - memset(buf, 0, sizeof buf); - strncpy(buf+1, pass, n); - memmove(buf+1+n, chal, ChapChallen); - md5((uchar*)buf, 1+n+ChapChallen, resp, nil); -} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/confirm.c /sys/src/cmd/auth/factotum/confirm.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/confirm.c Sun Feb 9 22:26:26 2003 +++ /sys/src/cmd/auth/factotum/confirm.c Tue Jan 1 00:00:00 2013 @@ -1,67 +1,42 @@ +#include "std.h" #include "dat.h" Logbuf confbuf; -Req *cusewait; /* requests waiting for confirmation */ -Req **cuselast = &cusewait; - void confirmread(Req *r) { - logbufread(&confbuf, r); + lbread(&confbuf, r); } void confirmflush(Req *r) { - Req **l; - - for(l=&cusewait; *l; l=&(*l)->aux){ - if(*l == r){ - *l = r->aux; - if(r->aux == nil) - cuselast = l; - closereq(r); - break; - } - } - logbufflush(&confbuf, r); -} - -static int -hastag(Fsstate *fss, int tag, int *tagoff) -{ - int i; - - for(i=0; inconf; i++) - if(fss->conf[i].tag == tag){ - *tagoff = i; - return 1; - } - return 0; + lbflush(&confbuf, r); } int confirmwrite(char *s) { char *t, *ans; - int allow, tagoff; + int allow; ulong tag; Attr *a; - Fsstate *fss; - Req *r, **l; + Conv *c; a = _parseattr(s); if(a == nil){ - werrstr("empty write"); + werrstr("bad attr"); return -1; } if((t = _strfindattr(a, "tag")) == nil){ + flog("bad confirm write: no tag"); werrstr("no tag"); return -1; } tag = strtoul(t, 0, 0); if((ans = _strfindattr(a, "answer")) == nil){ + flog("bad confirm write: no answer"); werrstr("no answer"); return -1; } @@ -70,84 +45,51 @@ else if(strcmp(ans, "no") == 0) allow = 0; else{ + flog("bad confirm write: bad answer"); werrstr("bad answer"); return -1; } - r = nil; - tagoff = -1; - for(l=&cusewait; *l; l=&(*l)->aux){ - r = *l; - if(hastag(r->fid->aux, tag, &tagoff)){ - *l = r->aux; - if(r->aux == nil) - cuselast = l; + for(c=conv; c; c=c->next){ + if(tag == c->tag){ + nbsendul(c->keywait, allow); break; } } - if(r == nil || tagoff == -1){ + if(c == nil){ werrstr("tag not found"); return -1; } - fss = r->fid->aux; - fss->conf[tagoff].canuse = allow; - rpcread(r); return 0; } -void -confirmqueue(Req *r, Fsstate *fss) +int +confirmkey(Conv *c, Key *k) { - int i, n; - char msg[1024]; + int ret; - if(*confirminuse == 0){ - respond(r, "confirm is closed"); - return; - } - - n = 0; - for(i=0; inconf; i++) - if(fss->conf[i].canuse == -1){ - n++; - snprint(msg, sizeof msg, "confirm tag=%lud %A", fss->conf[i].tag, fss->conf[i].key->attr); - logbufappend(&confbuf, msg); - } - if(n == 0){ - respond(r, "no confirmations to wait for (bug)"); - return; - } - *cuselast = r; - r->aux = nil; - cuselast = &r->aux; -} + if(*confirminuse == 0) + return -1; -/* Yes, I am unhappy that the code below is a copy of the code above. */ + lbappend(&confbuf, "confirm tag=%lud %A %N", c->tag, k->attr, k->privattr); + flog("confirm %A %N", k->attr, k->privattr); + c->state = "keyconfirm"; + ret = recvul(c->keywait); + flog("confirm=%d %A %N", ret, k->attr, k->privattr); + return ret; +} Logbuf needkeybuf; -Req *needwait; /* requests that need keys */ -Req **needlast = &needwait; void needkeyread(Req *r) { - logbufread(&needkeybuf, r); + lbread(&needkeybuf, r); } void needkeyflush(Req *r) { - Req **l; - - for(l=&needwait; *l; l=&(*l)->aux){ - if(*l == r){ - *l = r->aux; - if(r->aux == nil) - needlast = l; - closereq(r); - break; - } - } - logbufflush(&needkeybuf, r); + lbflush(&needkeybuf, r); } int @@ -156,7 +98,7 @@ char *t; ulong tag; Attr *a; - Req *r, **l; + Conv *c; a = _parseattr(s); if(a == nil){ @@ -165,40 +107,44 @@ } if((t = _strfindattr(a, "tag")) == nil){ werrstr("no tag"); + freeattr(a); return -1; } tag = strtoul(t, 0, 0); - r = nil; - for(l=&needwait; *l; l=&(*l)->aux){ - r = *l; - if(r->tag == tag){ - *l = r->aux; - if(r->aux == nil) - needlast = l; + for(c=conv; c; c=c->next) + if(c->tag == tag){ + nbsendul(c->keywait, 0); break; } - } - if(r == nil){ + if(c == nil){ werrstr("tag not found"); + freeattr(a); return -1; } - rpcread(r); + freeattr(a); return 0; } int -needkeyqueue(Req *r, Fsstate *fss) +needkey(Conv *c, Attr *a) { - char msg[1024]; - - if(*needkeyinuse == 0) + if(c == nil || *needkeyinuse == 0) return -1; - snprint(msg, sizeof msg, "needkey tag=%lud %s", r->tag, fss->keyinfo); - logbufappend(&needkeybuf, msg); - *needlast = r; - r->aux = nil; - needlast = &r->aux; - return 0; + lbappend(&needkeybuf, "needkey tag=%lud %A", c->tag, a); + flog("needkey %A", a); + return nbrecvul(c->keywait); } +int +badkey(Conv *c, Key *k, char *msg, Attr *a) +{ + if(c == nil || *needkeyinuse == 0) + return -1; + + lbappend(&needkeybuf, "badkey tag=%lud %A %N\n%s\n%A", + c->tag, k->attr, k->privattr, msg, a); + flog("badkey %A / %N / %s / %A", + k->attr, k->privattr, msg, a); + return nbrecvul(c->keywait); +} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/conv.c /sys/src/cmd/auth/factotum/conv.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/conv.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/conv.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,285 @@ +#include "std.h" +#include "dat.h" + +Conv *conv; + +ulong taggen = 1; + +Conv* +convalloc(char *sysuser) +{ + Conv *c; + + c = mallocz(sizeof(Conv), 1); + if(c == nil) + return nil; + c->ref = 1; + c->tag = taggen++; + c->next = conv; + c->sysuser = estrdup(sysuser); + c->state = "nascent"; + c->rpcwait = chancreate(sizeof(void*), 0); + c->keywait = chancreate(sizeof(void*), 0); + strcpy(c->err, "protocol has not started"); + conv = c; + convreset(c); + return c; +} + +void +convfree(Conv *c) +{ + freeattr(c->attr); + free(c->sysuser); + chanfree(c->rpcwait); + chanfree(c->keywait); + free(c); +} + +void +convreset(Conv *c) +{ + if(c->ref != 1){ + c->hangup = 1; + nbsendp(c->rpcwait, 0); + while(c->ref > 1) + yield(); + c->hangup = 0; + } + c->state = "nascent"; + c->err[0] = '\0'; + freeattr(c->attr); + c->attr = nil; + c->proto = nil; + c->rpc.op = 0; + c->active = 0; + c->done = 0; + c->hangup = 0; +} + +void +convhangup(Conv *c) +{ + c->hangup = 1; + c->rpc.op = 0; + (*c->kickreply)(c); + nbsendp(c->rpcwait, 0); +} + +void +convclose(Conv *c) +{ + Conv *p; + + if(c == nil) + return; + + if(--c->ref > 0) + return; + + if(c == conv){ + conv = c->next; + goto free; + } + for(p=conv; p && p->next!=c; p=p->next) + ; + if(p == nil){ + print("cannot find conv in list\n"); + return; + } + p->next = c->next; + +free: + c->next = nil; + convfree(c); +} + +static Rpc* +convgetrpc(Conv *c, int want) +{ + for(;;){ + if(c->hangup){ + flog("convgetrpc: hangup"); + werrstr("hangup"); + return nil; + } + if(c->rpc.op == RpcUnknown){ + recvp(c->rpcwait); + if(c->hangup){ + flog("convgetrpc: hangup"); + werrstr("hangup"); + return nil; + } + if(c->rpc.op == RpcUnknown) + continue; + } + if(want < 0 || c->rpc.op == want) + return &c->rpc; + rpcrespond(c, "phase in state '%s' want '%s'", c->state, rpcname[want]); + } + /* not reached */ +} + +/* read until the done function tells us that's enough */ +int +convreadfn(Conv *c, int (*done)(void*, int), char **ps) +{ + int n; + Rpc *r; + char *s; + + for(;;){ + r = convgetrpc(c, RpcWrite); + if(r == nil) + return -1; + n = (*done)(r->data, r->count); + if(n == r->count) + break; + rpcrespond(c, "toosmall %d", n); + } + + s = emalloc(r->count+1); + memmove(s, r->data, r->count); + s[r->count] = 0; + *ps = s; + rpcrespond(c, "ok"); + return r->count; +} + +/* + * read until we get a non-zero write. assumes remote side + * knows something about the protocol (is not auth_proxy). + * the remote side typically won't bother with the zero-length + * write to find out the length -- the loop is there only so the + * test program can call auth_proxy on both sides of a pipe + * to play a conversation. + */ +int +convreadm(Conv *c, char **ps) +{ + char *s; + Rpc *r; + + *ps = nil; + for(;;){ + r = convgetrpc(c, RpcWrite); + if(r == nil) + return -1; + if(r->count > 0) + break; + rpcrespond(c, "toosmall %d", AuthRpcMax); + } + s = emalloc(r->count+1); + memmove(s, r->data, r->count); + s[r->count] = 0; + *ps = s; + rpcrespond(c, "ok"); + return r->count; +} + +/* read exactly count bytes */ +int +convread(Conv *c, void *data, int count) +{ + Rpc *r; + + for(;;){ + r = convgetrpc(c, RpcWrite); + if(r == nil) + return -1; + if(r->count == count) + break; + if(r->count < count) + rpcrespond(c, "toosmall %d", count); + else + rpcrespond(c, "error too much data; want %d got %d", count, r->count); + } + memmove(data, r->data, count); + rpcrespond(c, "ok"); + return 0; +} + +/* write exactly count bytes */ +int +convwrite(Conv *c, void *data, int count) +{ + Rpc *r; + + r = convgetrpc(c, RpcRead); + if(r == nil) + return -1; +if(c->done) +rpcrespondn(c, "done", data, count); +else + rpcrespondn(c, "ok", data, count); + return 0; +} + +/* print to the conversation */ +int +convprint(Conv *c, char *fmt, ...) +{ + char *s; + va_list arg; + int ret; + + va_start(arg, fmt); + s = vsmprint(fmt, arg); + va_end(arg); + if(s == nil) + return -1; + ret = convwrite(c, s, strlen(s)); + free(s); + return ret; +} + +/* ask for a key */ +int +convneedkey(Conv *c, Attr *a) +{ + /* + * Piggyback key requests in the usual RPC channel. + * Wait for the next RPC and then send a key request + * in response. The keys get added out-of-band (via the + * ctl file), so assume the key has been added when the + * next request comes in. + * + * The convgetrpc seems dodgy, because we might be in + * the middle of an rpc, and what about the one that comes + * in later? It's all actually okay: convgetrpc is idempotent + * until rpcrespond is called, so if we're in the middle of an rpc, + * the first convgetrpc is a no-op, the rpcrespond sends back + * the needkey, and then the client repeats the rpc we're in + * the middle of. Otherwise, if we're not in the middle of an + * rpc, the first convgetrpc waits for one, we respond needkey, + * and then the second convgetrpc waits for another. Because + * there is no second response, eventually the caller will get + * around to asking for an rpc itself, at which point the already + * gotten rpc will be returned again. + */ + if(convgetrpc(c, -1) == nil) + return -1; + if(conv->proto!=nil && c->proto->keyprompt!=nil) + a = addattrs(parseattr(c->proto->keyprompt), a); + flog("convneedkey %A", a); + rpcrespond(c, "needkey %A", a); + if(convgetrpc(c, -1) == nil) + return -1; + flog("convneedkey returning"); + return 0; +} + +/* ask for a replacement for a bad key*/ +int +convbadkey(Conv *c, Key *k, char *msg, Attr *a) +{ + if(convgetrpc(c, -1) == nil) + return -1; + flog("convbadkey %A %N / %s / %A", k->attr, k->privattr, msg, a); + rpcrespond(c, "badkey %A %N\n%s\n%A", + k->attr, k->privattr, msg, a); + if(convgetrpc(c, -1) == nil) + return -1; + return 0; +} + diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/ctl.c /sys/src/cmd/auth/factotum/ctl.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/ctl.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/ctl.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,174 @@ +#include "std.h" +#include "dat.h" + +/* + * key attr=val... - add a key + * the attr=val pairs are protocol-specific. + * for example, both of these are valid: + * key p9sk1 gre cs.bell-labs.com mysecret + * key p9sk1 gre cs.bell-labs.com 11223344556677 fmt=des7hex + * delkey ... - delete a key + * if given, the attr=val pairs are used to narrow the search + * [maybe should require a password?] + * + * debug - toggle debugging + */ + +static char *msg[] = { + "key", + "delkey", + "debug" +}; + +static int +classify(char *s) +{ + int i; + + for(i=0; i/mnt/factotum/ctl" + * and writes that (incorrectly) contain multiple key lines. + */ + if(p = strchr(a, '\n')){ + if(p[1] != '\0'){ + werrstr("multiline write not allowed"); + return -1; + } + *p = '\0'; + } + + if((p = strchr(a, ' ')) == nil) + p = ""; + else + *p++ = '\0'; + switch(classify(a)){ + default: + werrstr("unknown verb %s", a); + return -1; + case 0: /* key */ + attr = parseattr(p); + /* separate out proto= attributes */ + lprotos = &protos; + for(l=&attr; (*l); ){ + if(strcmp((*l)->name, "proto") == 0){ + *lprotos = *l; + lprotos = &(*l)->next; + *l = (*l)->next; + }else + l = &(*l)->next; + } + *lprotos = nil; + if(protos == nil){ + werrstr("key without protos"); + freeattr(attr); + return -1; + } + + /* separate out private attributes */ + lpriv = &priv; + for(l=&attr; (*l); ){ + if((*l)->name[0] == '!'){ + *lpriv = *l; + lpriv = &(*l)->next; + *l = (*l)->next; + }else + l = &(*l)->next; + } + *lpriv = nil; + flog("addkey %A %A %N", protos, attr, priv); + + /* add keys */ + ret = 0; + for(pa=protos; pa; pa=pa->next){ + if((proto = protolookup(pa->val)) == nil){ + werrstr("unknown proto %s", pa->val); + flog("addkey: %r"); + ret = -1; + continue; + } + if(proto->keyprompt){ + kpa = parseattr(proto->keyprompt); + if(!matchattr(kpa, attr, priv)){ + freeattr(kpa); + werrstr("missing attributes -- want %s", proto->keyprompt); + flog("addkey %s: %r", proto->name); + ret = -1; + continue; + } + freeattr(kpa); + } + k = emalloc(sizeof(Key)); + k->attr = mkattr(AttrNameval, "proto", proto->name, copyattr(attr)); + k->privattr = copyattr(priv); + k->ref = 1; + k->proto = proto; + if(proto->checkkey && (*proto->checkkey)(k) < 0){ + flog("addkey %s: %r", proto->name); + ret = -1; + keyclose(k); + continue; + } + flog("adding key: %A %N", k->attr, k->privattr); + keyadd(k); + keyclose(k); + } + freeattr(attr); + freeattr(priv); + freeattr(protos); + return ret; + case 1: /* delkey */ + nmatch = 0; + attr = parseattr(p); + flog("delkey %A", attr); + for(pa=attr; pa; pa=pa->next){ + if(pa->type != AttrQuery && pa->name[0]=='!'){ + werrstr("only !private? patterns are allowed for private fields"); + freeattr(attr); + return -1; + } + } + for(i=0; iattr, ring.key[i]->privattr)){ + nmatch++; + flog("deleting %A %N", ring.key[i]->attr, ring.key[i]->privattr); + keyclose(ring.key[i]); + ring.nkey--; + memmove(&ring.key[i], &ring.key[i+1], (ring.nkey-i)*sizeof(ring.key[0])); + }else + i++; + } + freeattr(attr); + if(nmatch == 0){ + werrstr("found no keys to delete"); + return -1; + } + return 0; + case 2: /* debug */ + debug ^= 1; + flog("debug = %d", debug); + return 0; + } +} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/dat.h /sys/src/cmd/auth/factotum/dat.h --- /n/sources/plan9/sys/src/cmd/auth/factotum/dat.h Sat Feb 17 05:28:06 2007 +++ /sys/src/cmd/auth/factotum/dat.h Tue Jan 1 00:00:00 2013 @@ -1,104 +1,73 @@ -#include -#include -#include -#include -#include -#include -#include -#include /* only for 9p.h */ -#include -#include <9p.h> - -#pragma varargck type "N" Attr* - enum { - Maxname = 128, - Maxrpc = 4096, + MaxRpc = 2048, /* max size of any protocol message */ + + /* keep in sync with rpc.c:/rpcname */ + RpcUnknown = 0, /* Rpc.op */ + RpcAuthinfo, + RpcAttr, + RpcRead, + RpcStart, + RpcWrite, + RpcReadHex, + RpcWriteHex, - /* common protocol phases; proto-specific phases start at 0 */ - Notstarted = -3, - Broken = -2, - Established = -1, - - /* rpc read/write return values */ - RpcFailure = 0, - RpcNeedkey, - RpcOk, - RpcErrstr, - RpcToosmall, - RpcPhase, - RpcConfirm, + /* thread stack size - big buffers for printing */ + STACK = 32*1024, }; -typedef struct Domain Domain; -typedef struct Fsstate Fsstate; +typedef struct Conv Conv; typedef struct Key Key; -typedef struct Keyinfo Keyinfo; -typedef struct Keyring Keyring; typedef struct Logbuf Logbuf; typedef struct Proto Proto; -typedef struct State State; +typedef struct Ring Ring; +typedef struct Role Role; +typedef struct Rpc Rpc; + +struct Rpc +{ + int op; + void *data; + int count; + int hex; /* should result of read be turned into hex? */ +}; + +struct Conv +{ + int ref; /* ref count */ + int hangup; /* flag: please hang up */ + int active; /* flag: there is an active thread */ + int done; /* flag: conversation finished successfully */ + ulong tag; /* identifying tag */ + Conv *next; /* in linked list */ + char *sysuser; /* system name for user speaking to us */ + char *state; /* for debugging */ + char statebuf[128]; /* for formatted states */ + char err[ERRMAX]; /* last error */ + + Attr *attr; /* current attributes */ + Proto *proto; /* protocol */ + + Channel *rpcwait; /* wait here for an rpc */ + Rpc rpc; /* current rpc. op==RpcUnknown means none */ + char rpcbuf[MaxRpc]; /* buffer for rpc */ + char reply[MaxRpc]; /* buffer for response */ + int nreply; /* count of response */ + void (*kickreply)(Conv*); /* call to send response */ + Req *req; /* 9P call to read response */ -#pragma incomplete State - - -struct Fsstate -{ - char *sysuser; /* user according to system */ - - /* keylist, protolist */ - int listoff; - - /* per-rpc transient information */ - int pending; - struct { - char *arg, buf[Maxrpc], *verb; - int iverb, narg, nbuf, nwant; - } rpc; + Channel *keywait; /* wait here for key confirmation */ - /* persistent (cross-rpc) information */ - char err[ERRMAX]; - char keyinfo[3*Maxname]; /* key request */ - char **phasename; - int haveai, maxphase, phase, seqnum, started; - Attr *attr; - AuthInfo ai; - Proto *proto; - State *ps; - struct { /* pending or finished key confirmations */ - Key *key; - int canuse; - ulong tag; - } *conf; - int nconf; }; struct Key { - int ref; - Attr *attr; - Attr *privattr; /* private attributes, like *data */ - Proto *proto; - - void *priv; /* protocol-specific; a parsed key, perhaps */ - ulong successes; -}; - -struct Keyinfo /* for findkey */ -{ - Fsstate *fss; - char *user; - int noconf; - int skip; - int usedisabled; - Attr *attr; -}; - -struct Keyring -{ - Key **key; - int nkey; + int ref; /* ref count */ + ulong tag; /* identifying tag: sequence number */ + Attr *attr; /* public attributes */ + Attr *privattr; /* private attributes, like !password */ + Proto *proto; /* protocol owner of key */ + void *priv; /* protocol-specific storage */ }; struct Logbuf @@ -110,129 +79,163 @@ char *msg[128]; }; +struct Ring +{ + Key **key; + int nkey; +}; + struct Proto { - char *name; - int (*init)(Proto*, Fsstate*); - int (*addkey)(Key*, int); - void (*closekey)(Key*); - int (*write)(Fsstate*, void*, uint); - int (*read)(Fsstate*, void*, uint*); - void (*close)(Fsstate*); - char *keyprompt; -}; - -extern char *invoker; -extern char *owner; -extern char *authdom; - -extern char Easproto[]; -extern char Ebadarg[]; -extern char Ebadkey[]; -extern char Enegotiation[]; -extern char Etoolarge[]; - -/* confirm.c */ -void confirmread(Req*); -void confirmflush(Req*); -int confirmwrite(char*); -void confirmqueue(Req*, Fsstate*); -void needkeyread(Req*); -void needkeyflush(Req*); -int needkeywrite(char*); -int needkeyqueue(Req*, Fsstate*); - -/* fs.c */ -extern int askforkeys; -extern char *authaddr; -extern int *confirminuse; -extern int debug; -extern int gflag; -extern int kflag; -extern int *needkeyinuse; -extern int sflag; -extern int uflag; -extern char *mtpt; -extern char *service; -extern Proto *prototab[]; -extern Keyring *ring; - -/* log.c */ -void flog(char*, ...); -#pragma varargck argpos flog 1 -void logread(Req*); -void logflush(Req*); -void logbufflush(Logbuf*, Req*); -void logbufread(Logbuf*, Req*); -void logbufproc(Logbuf*); -void logbufappend(Logbuf*, char*); -void needkeyread(Req*); -void needkeyflush(Req*); -int needkeywrite(char*); -int needkeyqueue(Req*, Fsstate*); - -/* rpc.c */ -int ctlwrite(char*, int); -void rpcrdwrlog(Fsstate*, char*, uint, int, int); -void rpcstartlog(Attr*, Fsstate*, int); -void rpcread(Req*); -void rpcwrite(Req*); - -/* secstore.c */ -int havesecstore(void); -int secstorefetch(char*); - -/* util.c */ -#define emalloc emalloc9p -#define estrdup estrdup9p -#define erealloc erealloc9p -#pragma varargck argpos failure 2 -#pragma varargck argpos findkey 3 -#pragma varargck argpos setattr 2 - -int _authdial(char*, char*); -void askuser(char*); -int attrnamefmt(Fmt *fmt); -int canusekey(Fsstate*, Key*); -void closekey(Key*); -uchar *convAI2M(AuthInfo*, uchar*, int); -void disablekey(Key*); -char *estrappend(char*, char*, ...); -#pragma varargck argpos estrappend 2 -int failure(Fsstate*, char*, ...); -Keyinfo* mkkeyinfo(Keyinfo*, Fsstate*, Attr*); -int findkey(Key**, Keyinfo*, char*, ...); -int findp9authkey(Key**, Fsstate*); -Proto *findproto(char*); -char *getnvramkey(int, char**); -void initcap(void); -int isclient(char*); -int matchattr(Attr*, Attr*, Attr*); -void memrandom(void*, int); -char *mkcap(char*, char*); -int phaseerror(Fsstate*, char*); -char *phasename(Fsstate*, int, char*); -void promptforhostowner(void); -char *readcons(char*, char*, int); -int replacekey(Key*, int before); -char *safecpy(char*, char*, int); -int secdial(void); -Attr *setattr(Attr*, char*, ...); -Attr *setattrs(Attr*, Attr*); -void sethostowner(void); -void setmalloctaghere(void*); -int smatch(char*, char*); -Attr *sortattr(Attr*); -int toosmall(Fsstate*, uint); -void writehostowner(char*); - -/* protocols */ -extern Proto apop, cram; /* apop.c */ -extern Proto p9any, p9sk1, p9sk2; /* p9sk.c */ -extern Proto chap, mschap; /* chap.c */ -extern Proto p9cr, vnc; /* p9cr.c */ -extern Proto pass; /* pass.c */ -extern Proto rsa; /* rsa.c */ -extern Proto wep; /* wep.c */ -/* extern Proto srs; /* srs.c */ -extern Proto httpdigest; /* httpdigest.c */ + char *name; /* name of protocol */ + Role *roles; /* list of roles and service functions */ + char *keyprompt; /* required attributes for key proto=name */ + int (*checkkey)(Key*); /* initialize k->priv or reject key */ + void (*closekey)(Key*); /* free k->priv */ +}; + +struct Role +{ + char *name; /* name of role */ + int (*fn)(Conv*); /* service function */ +}; + +extern char *authaddr; /* plan9.c */ +extern int *confirminuse; /* fs.c */ +extern Conv* conv; /* conv.c */ +extern int debug; /* main.c */ +extern char *factname; /* main.c */ +extern Srv fs; /* fs.c */ +extern int *needkeyinuse; /* fs.c */ +extern char *owner; /* main.c */ +extern Proto *prototab[]; /* main.c */ +extern Ring ring; /* key.c */ +extern char *rpcname[]; /* rpc.c */ + +extern char Easproto[]; /* err.c */ + +void fsinit0(void); + +/* provided by lib9p */ +#define emalloc emalloc9p +#define erealloc erealloc9p +#define estrdup estrdup9p + +/* hidden in libauth */ +#define attrfmt _attrfmt +#define copyattr _copyattr +#define delattr _delattr +#define findattr _findattr +#define freeattr _freeattr +#define mkattr _mkattr +#define parseattr _parseattr +#define strfindattr _strfindattr + +extern Attr* addattr(Attr*, char*, ...); + #pragma varargck argpos addattr 2 +extern Attr* addattrs(Attr*, Attr*); +extern Attr* sortattr(Attr*); +extern int attrnamefmt(Fmt*); + #pragma varargck type "N" Attr* +extern int matchattr(Attr*, Attr*, Attr*); +extern Attr* parseattrfmt(char*, ...); + #pragma varargck argpos parseattrfmt 1 +extern Attr* parseattrfmtv(char*, va_list); + +extern void confirmflush(Req*); +extern void confirmread(Req*); +extern int confirmwrite(char*); +extern int needkey(Conv*, Attr*); +extern int badkey(Conv*, Key*, char*, Attr*); +extern int confirmkey(Conv*, Key*); + +extern Conv* convalloc(char*); +extern void convclose(Conv*); +extern void convhangup(Conv*); +extern int convneedkey(Conv*, Attr*); +extern int convbadkey(Conv*, Key*, char*, Attr*); +extern int convread(Conv*, void*, int); +extern int convreadm(Conv*, char**); +extern int convprint(Conv*, char*, ...); + #pragma varargck argpos convprint 2 +extern int convreadfn(Conv*, int(*)(void*, int), char**); +extern void convreset(Conv*); +extern int convwrite(Conv*, void*, int); + +extern int ctlwrite(char*); + +extern char* estrappend(char*, char*, ...); + #pragma varargck argpos estrappend 2 +extern int hexparse(char*, uchar*, int); + +extern void keyadd(Key*); +extern Key* keylookup(char*, ...); +extern Key* keyiterate(int, char*, ...); + #pragma varargck argpos keylookup 1 +extern Key* keyfetch(Conv*, char*, ...); + #pragma varargck argpos keyfetch 2 +extern void keyclose(Key*); +extern void keyevict(Conv*, Key*, char*, ...); + #pragma varargck argpos keyevict 3 +extern Key* keyreplace(Conv*, Key*, char*, ...); + #pragma varargck argpos keyreplace 3 + +extern void lbkick(Logbuf*); +extern void lbappend(Logbuf*, char*, ...); +extern void lbvappend(Logbuf*, char*, va_list); + #pragma varargck argpos lbappend 2 +extern void lbread(Logbuf*, Req*); +extern void lbflush(Logbuf*, Req*); +extern void flog(char*, ...); + #pragma varargck argpos flog 1 + +extern void logflush(Req*); +extern void logread(Req*); +extern void logwrite(Req*); + +extern void needkeyread(Req*); +extern void needkeyflush(Req*); +extern int needkeywrite(char*); +extern int needkeyqueue(void); + +extern Attr* addcap(Attr*, char*, Ticket*); +extern Key* plan9authkey(Attr*); +extern int _authdial(char*, char*); + +extern int memrandom(void*, int); + +extern Proto* protolookup(char*); + +extern int rpcwrite(Conv*, void*, int); +extern void rpcrespond(Conv*, char*, ...); + #pragma varargck argpos rpcrespond 2 +extern void rpcrespondn(Conv*, char*, void*, int); +extern void rpcexec(Conv*); + +extern int xioauthdial(char*, char*); +extern void xioclose(int); +extern int xiodial(char*, char*, char*, int*); +extern int xiowrite(int, void*, int); +extern int xioasrdresp(int, void*, int); +extern int xioasgetticket(int, char*, char*); + +/* pkcs1.c - maybe should be in libsec */ +typedef DigestState *DigestAlg(uchar*, ulong, uchar*, DigestState*); +int rsasign(RSApriv*, DigestAlg*, uchar*, uint, uchar*, uint); +int rsaverify(RSApub*, DigestAlg*, uchar*, uint, uchar*, uint); +void mptoberjust(mpint*, uchar*, uint); + + +extern int extrafactotumdir; + +int havesecstore(void); +int secstorefetch(char*); + +char *readcons(char *, char *, int); +char *safecpy(char *, char *, int); +void initcap(void); +char *mkcap(char *, char *); +int secdial(void); +void promptforhostowner(void); +char *getnvramkey(int, char **); +void writehostowner(char *); diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/dsa.c /sys/src/cmd/auth/factotum/dsa.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/dsa.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/dsa.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,145 @@ +#include "std.h" +#include "dat.h" + +/* + * DSA signing and verification + * + * Sign: + * start p=xxx q=xxx alpha=xxx key=xxx + * write msg + * read signature(msg) + * + * Verify: (not implemented) + * start p=xxx q=xxx alpha=xxx key=xxx + * write msg + * write signature(msg) + * read ok or fail + * + * all numbers are hexadecimal bigints parsable with strtomp. + */ + +static int +xdsasign(Conv *c) +{ + mpint *m; + uchar digest[SHA1dlen], sigblob[20+20]; + DSAsig *sig; + Key *k; + + k = keylookup("%A", c->attr); + if(k == nil) + return -1; + + c->state = "read data"; + if(convread(c, digest, SHA1dlen) < 0){ + keyclose(k); + return -1; + } + m = betomp(digest, SHA1dlen, nil); + if(m == nil){ + keyclose(k); + return -1; + } + sig = dsasign(k->priv, m); + keyclose(k); + mpfree(m); + if(sig == nil) + return -1; + if(mpsignif(sig->r) > 20*8 || mpsignif(sig->s) > 20*8){ + werrstr("signature too long"); + return -1; + } + mptoberjust(sig->r, sigblob, 20); + mptoberjust(sig->s, sigblob+20, 20); + convwrite(c, sigblob, sizeof sigblob); + dsasigfree(sig); + return 0; +} + +/* + * convert to canonical form (lower case) + * for use in attribute matches. + */ +static void +strlwr(char *a) +{ + for(; *a; a++){ + if('A' <= *a && *a <= 'Z') + *a += 'a' - 'A'; + } +} + +static DSApriv* +readdsapriv(Key *k) +{ + char *a; + DSApriv *priv; + + priv = dsaprivalloc(); + + if((a=strfindattr(k->attr, "p"))==nil + || (priv->pub.p=strtomp(a, nil, 16, nil))==nil) + goto Error; + strlwr(a); + if((a=strfindattr(k->attr, "q"))==nil + || (priv->pub.q=strtomp(a, nil, 16, nil))==nil) + goto Error; + strlwr(a); + if((a=strfindattr(k->attr, "alpha"))==nil + || (priv->pub.alpha=strtomp(a, nil, 16, nil))==nil) + goto Error; + strlwr(a); + if((a=strfindattr(k->attr, "key"))==nil + || (priv->pub.key=strtomp(a, nil, 16, nil))==nil) + goto Error; + strlwr(a); + if((a=strfindattr(k->privattr, "!secret"))==nil + || (priv->secret=strtomp(a, nil, 16, nil))==nil) + goto Error; + strlwr(a); + return priv; + +Error: + dsaprivfree(priv); + return nil; +} + +static int +dsacheck(Key *k) +{ + static int first = 1; + + if(first){ + fmtinstall('B', mpfmt); + first = 0; + } + + if((k->priv = readdsapriv(k)) == nil){ + werrstr("malformed key data"); + return -1; + } + return 0; +} + +static void +dsaclose(Key *k) +{ + dsaprivfree(k->priv); + k->priv = nil; +} + +static Role +dsaroles[] = +{ + "sign", xdsasign, + 0 +}; + +Proto dsa = { + "dsa", + dsaroles, + nil, + dsacheck, + dsaclose +}; + diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/fgui.c /sys/src/cmd/auth/factotum/fgui.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/fgui.c Tue Apr 13 02:42:11 2010 +++ /sys/src/cmd/auth/factotum/fgui.c Tue Jan 1 00:00:00 2013 @@ -1,3 +1,4 @@ +#include "std.h" #include "dat.h" #include #include @@ -45,7 +46,7 @@ static void confirm(Request*); static void resizeconfirm(Controlset*); -static void needkey(Request*); +static void fguineedkey(Request*); static void resizeneedkey(Controlset*); Control *b_remember; @@ -55,7 +56,7 @@ RequestType rt[] = { { "/mnt/factotum/confirm", confirm, resizeconfirm, }, - { "/mnt/factotum/needkey", needkey, resizeneedkey, }, + { "/mnt/factotum/needkey", fguineedkey, resizeneedkey, }, { 0 }, }; @@ -718,7 +719,7 @@ } static void -needkey(Request *r) +fguineedkey(Request *r) { Channel *c; char *nam, *val; diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/fs.c /sys/src/cmd/auth/factotum/fs.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/fs.c Thu Jan 15 20:35:25 2009 +++ /sys/src/cmd/auth/factotum/fs.c Tue Jan 1 00:00:00 2013 @@ -1,229 +1,6 @@ +#include "std.h" #include "dat.h" -int askforkeys = 1; -char *authaddr; -int debug; -int doprivate = 1; -int gflag; -char *owner; -int kflag; -char *mtpt = "/mnt"; -Keyring *ring; -char *service; -int sflag; -int uflag; - -extern Srv fs; -static void notifyf(void*, char*); -static void private(void); - -char Easproto[] = "auth server protocol botch"; -char Ebadarg[] = "invalid argument"; -char Ebadkey[] = "bad key"; -char Enegotiation[] = "negotiation failed, no common protocols or keys"; -char Etoolarge[] = "rpc too large"; - -Proto* -prototab[] = -{ - &apop, - &chap, - &cram, - &httpdigest, - &mschap, - &p9any, - &p9cr, - &p9sk1, - &p9sk2, - &pass, -/* &srs, */ - &rsa, - &vnc, - &wep, - nil, -}; - -void -usage(void) -{ - fprint(2, "usage: %s [-DSdknpu] [-a authaddr] [-m mtpt] [-s service]\n", - argv0); - fprint(2, "or %s -g 'params'\n", argv0); - exits("usage"); -} - -void -main(int argc, char **argv) -{ - int i, trysecstore; - char err[ERRMAX], *s; - Dir d; - Proto *p; - char *secstorepw; - - trysecstore = 1; - secstorepw = nil; - - ARGBEGIN{ - case 'D': - chatty9p++; - break; - case 'S': /* server: read nvram, no prompting for keys */ - askforkeys = 0; - trysecstore = 0; - sflag = 1; - break; - case 'a': - authaddr = EARGF(usage()); - break; - case 'd': - debug = 1; - doprivate = 0; - break; - case 'g': /* get: prompt for key for name and domain */ - gflag = 1; - break; - case 'k': /* reinitialize nvram */ - kflag = 1; - break; - case 'm': /* set default mount point */ - mtpt = EARGF(usage()); - break; - case 'n': - trysecstore = 0; - break; - case 'p': - doprivate = 0; - break; - case 's': /* set service name */ - service = EARGF(usage()); - break; - case 'u': /* user: set hostowner */ - uflag = 1; - break; - default: - usage(); - }ARGEND - - if(argc != 0 && !gflag) - usage(); - if(doprivate) - private(); - - initcap(); - - quotefmtinstall(); - fmtinstall('A', _attrfmt); - fmtinstall('N', attrnamefmt); - fmtinstall('H', encodefmt); - - ring = emalloc(sizeof(*ring)); - notify(notifyf); - - if(gflag){ - if(argc != 1) - usage(); - askuser(argv[0]); - exits(nil); - } - - for(i=0; prototab[i]; i++){ - p = prototab[i]; - if(p->name == nil) - sysfatal("protocol %d has no name", i); - if(p->init == nil) - sysfatal("protocol %s has no init", p->name); - if(p->write == nil) - sysfatal("protocol %s has no write", p->name); - if(p->read == nil) - sysfatal("protocol %s has no read", p->name); - if(p->close == nil) - sysfatal("protocol %s has no close", p->name); - if(p->keyprompt == nil) - p->keyprompt = ""; - } - - if(sflag){ - s = getnvramkey(kflag ? NVwrite : NVwriteonerr, &secstorepw); - if(s == nil) - fprint(2, "factotum warning: cannot read nvram: %r\n"); - else if(ctlwrite(s, 0) < 0) - fprint(2, "factotum warning: cannot add nvram key: %r\n"); - if(secstorepw != nil) - trysecstore = 1; - if (s != nil) { - memset(s, 0, strlen(s)); - free(s); - } - } else if(uflag) - promptforhostowner(); - owner = getuser(); - - if(trysecstore){ - if(havesecstore() == 1){ - while(secstorefetch(secstorepw) < 0){ - rerrstr(err, sizeof err); - if(strcmp(err, "cancel") == 0) - break; - fprint(2, "factotum: secstorefetch: %r\n"); - fprint(2, "Enter an empty password to quit.\n"); - free(secstorepw); - secstorepw = nil; /* just try nvram pw once */ - } - }else{ -/* - rerrstr(err, sizeof err); - if(*err) - fprint(2, "factotum: havesecstore: %r\n"); -*/ - } - } - - postmountsrv(&fs, service, mtpt, MBEFORE); - if(service){ - nulldir(&d); - d.mode = 0666; - s = emalloc(10+strlen(service)); - strcpy(s, "/srv/"); - strcat(s, service); - if(dirwstat(s, &d) < 0) - fprint(2, "factotum warning: cannot chmod 666 %s: %r\n", s); - free(s); - } - exits(nil); -} - -char *pmsg = "Warning! %s can't protect itself from debugging: %r\n"; -char *smsg = "Warning! %s can't turn off swapping: %r\n"; - -/* don't allow other processes to debug us and steal keys */ -static void -private(void) -{ - int fd; - char buf[64]; - - snprint(buf, sizeof(buf), "#p/%d/ctl", getpid()); - fd = open(buf, OWRITE); - if(fd < 0){ - fprint(2, pmsg, argv0); - return; - } - if(fprint(fd, "private") < 0) - fprint(2, pmsg, argv0); - if(fprint(fd, "noswap") < 0) - fprint(2, smsg, argv0); - close(fd); -} - -static void -notifyf(void*, char *s) -{ - if(strncmp(s, "interrupt", 9) == 0) - noted(NCONT); - noted(NDFLT); -} - enum { Qroot, @@ -235,8 +12,11 @@ Qlog, Qctl, Qneedkey, + Qconv }; +static int qtop; + Qid mkqid(int type, int path) { @@ -248,29 +28,21 @@ return q; } -static void -fsattach(Req *r) +static struct { - r->fid->qid = mkqid(QTDIR, Qroot); - r->ofcall.qid = r->fid->qid; - respond(r, nil); -} - -static struct { char *name; int qidpath; ulong perm; } dirtab[] = { - "confirm", Qconfirm, 0600|DMEXCL, /* we know this is slot #0 below */ - "needkey", Qneedkey, 0600|DMEXCL, /* we know this is slot #1 below */ - "ctl", Qctl, 0644, - "rpc", Qrpc, 0666, - "proto", Qprotolist, 0444, - "log", Qlog, 0400|DMEXCL, + /* positions of confirm and needkey known below */ + "confirm", Qconfirm, 0600|DMEXCL, + "needkey", Qneedkey, 0600|DMEXCL, + "ctl", Qctl, 0644, + "rpc", Qrpc, 0666, + "proto", Qprotolist, 0444, + "log", Qlog, 0600|DMEXCL, + "conv", Qconv, 0400 }; -static int inuse[nelem(dirtab)]; -int *confirminuse = &inuse[0]; -int *needkeyinuse = &inuse[1]; static void fillstat(Dir *dir, char *name, int type, int path, ulong perm) @@ -287,17 +59,22 @@ } static int -rootdirgen(int n, Dir *dir, void*) +rootdirgen(int n, Dir *dir, void *v) { + USED(v); + if(n > 0) return -1; - fillstat(dir, "factotum", QTDIR, Qfactotum, DMDIR|0555); + + fillstat(dir, factname, QTDIR, Qfactotum, DMDIR|0555); return 0; } static int -fsdirgen(int n, Dir *dir, void*) +fsdirgen(int n, Dir *dir, void *v) { + USED(v); + if(n >= nelem(dirtab)) return -1; fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm); @@ -309,11 +86,11 @@ { int i; - switch((ulong)fid->qid.path){ + switch((int)fid->qid.path){ default: - return "cannot happen"; + return "fswalk1: cannot happen"; case Qroot: - if(strcmp(name, "factotum") == 0){ + if(strcmp(name, factname) == 0){ *qid = mkqid(QTDIR, Qfactotum); fid->qid = *qid; return nil; @@ -331,7 +108,7 @@ return nil; } if(strcmp(name, "..") == 0){ - *qid = mkqid(QTDIR, Qroot); + *qid = mkqid(QTDIR, qtop); fid->qid = *qid; return nil; } @@ -342,96 +119,31 @@ static void fsstat(Req *r) { - int i; - ulong path; + int i, path; path = r->fid->qid.path; - if(path == Qroot){ + switch(path){ + case Qroot: fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR); - respond(r, nil); - return; - } - if(path == Qfactotum){ + break; + case Qfactotum: fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR); - respond(r, nil); - return; - } - for(i=0; id, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm); - respond(r, nil); - return; - } - respond(r, "file not found"); -} - -static void -fsopen(Req *r) -{ - int i, *p, perm; - static int need[4] = {4, 2, 6, 1}; - int n; - Fsstate *fss; - - p = nil; - for(i=0; ifid->qid.path) - break; - if(i < nelem(dirtab)){ - if(dirtab[i].perm & DMEXCL) - p = &inuse[i]; - if(strcmp(r->fid->uid, owner) == 0) - perm = dirtab[i].perm>>6; - else - perm = dirtab[i].perm; - }else - perm = 5; - - n = need[r->ifcall.mode&3]; - if((r->ifcall.mode&~(3|OTRUNC)) || ((perm&n) != n)){ - respond(r, "permission denied"); - return; - } - if(p){ - if(*p){ - respond(r, "file in use"); - return; - } - (*p)++; - } - - r->fid->aux = fss = emalloc(sizeof(Fsstate)); - fss->phase = Notstarted; - fss->sysuser = r->fid->uid; - fss->attr = nil; - strcpy(fss->err, "factotum/fs.c no error"); - respond(r, nil); -} - -static void -fsdestroyfid(Fid *fid) -{ - int i; - Fsstate *fss; - - if(fid->omode != -1){ + break; + default: for(i=0; iqid.path) - if(dirtab[i].perm&DMEXCL) - inuse[i] = 0; + if(dirtab[i].qidpath == path){ + fillstat(&r->d, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm); + goto Break2; + } + respond(r, "file not found"); + break; } - - fss = fid->aux; - if(fss == nil) - return; - if(fss->ps) - (*fss->proto->close)(fss); - _freeattr(fss->attr); - free(fss); + Break2: + respond(r, nil); } static int -readlist(int off, int (*gen)(int, char*, uint, Fsstate*), Req *r, Fsstate *fss) +readlist(int off, int (*gen)(int, char*, uint), Req *r) { char *a, *ea; int n; @@ -439,7 +151,7 @@ a = r->ofcall.data; ea = a+r->ifcall.count; for(;;){ - n = (*gen)(off, a, ea-a, fss); + n = (*gen)(off, a, ea-a); if(n == 0){ r->ofcall.count = a - (char*)r->ofcall.data; return off; @@ -447,44 +159,35 @@ a += n; off++; } + /* not reached */ } -enum { Nearend = 2, }; /* at least room for \n and NUL */ - -/* result in `a', of `n' bytes maximum */ static int -keylist(int i, char *a, uint n, Fsstate *fss) +keylist(int i, char *a, uint nn) { - int wb; - Keyinfo ki; + int n; + char buf[4096]; Key *k; - static char zero[Nearend]; - k = nil; - mkkeyinfo(&ki, fss, nil); - ki.attr = nil; - ki.skip = i; - ki.usedisabled = 1; - if(findkey(&k, &ki, "") != RpcOk) + if(i >= ring.nkey) return 0; - memset(a + n - Nearend, 0, Nearend); - wb = snprint(a, n, "key %A %N\n", k->attr, k->privattr); - closekey(k); - if (wb >= n - 1 && a[n - 2] != '\n' && a[n - 2] != '\0') { - /* line won't fit in `a', so just truncate */ - strcpy(a + n - 2, "\n"); + k = ring.key[i]; + k->attr = sortattr(k->attr); + n = snprint(buf, sizeof buf, "key %A %N\n", k->attr, k->privattr); + if(n >= sizeof(buf)-5) + strcpy(buf+sizeof(buf)-5, "...\n"); + n = strlen(buf); + if(n > nn) return 0; - } - return wb; + memmove(a, buf, n); + return n; } static int -protolist(int i, char *a, uint n, Fsstate *fss) +protolist(int i, char *a, uint n) { - USED(fss); - - if(i >= nelem(prototab)-1) + if(prototab[i] == nil) return 0; if(strlen(prototab[i]->name)+1 > n) return 0; @@ -494,15 +197,126 @@ return n; } +/* BUG this is O(n^2) to fill in the list */ +static int +convlist(int i, char *a, uint nn) +{ + Conv *c; + char buf[512]; + int n; + + for(c=conv; c && i-- > 0; c=c->next) + ; + + if(c == nil) + return 0; + + if(c->state) + n = snprint(buf, sizeof buf, "conv state=%q %A\n", c->state, c->attr); + else + n = snprint(buf, sizeof buf, "conv state=closed err=%q\n", c->err); + + if(n >= sizeof(buf)-5) + strcpy(buf+sizeof(buf)-5, "...\n"); + n = strlen(buf); + if(n > nn) + return 0; + memmove(a, buf, n); + return n; +} + +static void +fskickreply(Conv *c) +{ + Req *r; + + if(c->hangup){ + if((r = c->req) != nil){ + c->req = nil; + respond(r, "hangup"); + } + return; + } + + if(!c->req || !c->nreply) + return; + + r = c->req; + r->ofcall.count = c->nreply; + r->ofcall.data = c->reply; + if(r->ofcall.count > r->ifcall.count) + r->ofcall.count = r->ifcall.count; + c->req = nil; + respond(r, nil); + c->nreply = 0; +} + +/* + * Some of the file system work happens in the fs proc, but + * fsopen, fsread, fswrite, fsdestroyfid, and fsflush happen in + * the main proc so that they can access the various shared + * data structures without worrying about locking. + */ +static int inuse[nelem(dirtab)]; +int *confirminuse = &inuse[0]; +int *needkeyinuse = &inuse[1]; +static void +fsopen(Req *r) +{ + int i, *inusep, perm; + static int need[4] = { 4, 2, 6, 1 }; + Conv *c; + + inusep = nil; + perm = 5; /* directory */ + for(i=0; ifid->qid.path){ + if(dirtab[i].perm & DMEXCL) + inusep = &inuse[i]; + if(strcmp(r->fid->uid, owner) == 0) + perm = dirtab[i].perm>>6; + else + perm = dirtab[i].perm; + break; + } + + if((r->ifcall.mode&~(OMASK|OTRUNC)) + || (need[r->ifcall.mode&3] & ~perm)){ + respond(r, "permission denied"); + return; + } + + if(inusep){ + if(*inusep){ + respond(r, "file in use"); + return; + } + *inusep = 1; + } + + if(r->fid->qid.path == Qrpc){ + if((c = convalloc(r->fid->uid)) == nil){ + char e[ERRMAX]; + + rerrstr(e, sizeof e); + respond(r, e); + return; + } + c->kickreply = fskickreply; + r->fid->aux = c; + } + + respond(r, nil); +} + static void fsread(Req *r) { - Fsstate *s; + Conv *c; - s = r->fid->aux; - switch((ulong)r->fid->qid.path){ + switch((int)r->fid->qid.path){ default: - respond(r, "bug in fsread"); + respond(r, "fsread: cannot happen"); break; case Qroot: dirread9p(r, rootdirgen, nil); @@ -513,10 +327,20 @@ respond(r, nil); break; case Qrpc: - rpcread(r); - break; - case Qneedkey: - needkeyread(r); + c = r->fid->aux; + if(c->rpc.op == RpcUnknown){ + respond(r, "no rpc pending"); + break; + } + if(c->req){ + respond(r, "read already pending"); + break; + } + c->req = r; + if(c->nreply) + (*c->kickreply)(c); + else + rpcexec(c); break; case Qconfirm: confirmread(r); @@ -525,11 +349,18 @@ logread(r); break; case Qctl: - s->listoff = readlist(s->listoff, keylist, r, s); + r->fid->aux = (void*)(uintptr)readlist((uintptr)r->fid->aux, keylist, r); respond(r, nil); break; + case Qneedkey: + needkeyread(r); + break; case Qprotolist: - s->listoff = readlist(s->listoff, protolist, r, s); + r->fid->aux = (void*)(uintptr)readlist((uintptr)r->fid->aux, protolist, r); + respond(r, nil); + break; + case Qconv: + r->fid->aux = (void*)(uintptr)readlist((uintptr)r->fid->aux, convlist, r); respond(r, nil); break; } @@ -540,37 +371,43 @@ { int ret; char err[ERRMAX], *s; + int (*strfn)(char*); + char *name; - switch((ulong)r->fid->qid.path){ + switch((int)r->fid->qid.path){ default: - respond(r, "bug in fswrite"); + respond(r, "fswrite: cannot happen"); break; case Qrpc: - rpcwrite(r); + if(rpcwrite(r->fid->aux, r->ifcall.data, r->ifcall.count) < 0){ + rerrstr(err, sizeof err); + respond(r, err); + }else{ + r->ofcall.count = r->ifcall.count; + respond(r, nil); + } break; case Qneedkey: - case Qconfirm: + name = "needkey"; + strfn = needkeywrite; + goto string; case Qctl: + name = "ctl"; + strfn = ctlwrite; + goto string; + case Qconfirm: + name = "confirm"; + strfn = confirmwrite; + string: s = emalloc(r->ifcall.count+1); memmove(s, r->ifcall.data, r->ifcall.count); s[r->ifcall.count] = '\0'; - switch((ulong)r->fid->qid.path){ - default: - abort(); - case Qneedkey: - ret = needkeywrite(s); - break; - case Qconfirm: - ret = confirmwrite(s); - break; - case Qctl: - ret = ctlwrite(s, 0); - break; - } + ret = (*strfn)(s); free(s); if(ret < 0){ rerrstr(err, sizeof err); respond(r, err); + flog("write %s: %s", name, err); }else{ r->ofcall.count = r->ifcall.count; respond(r, nil); @@ -583,19 +420,130 @@ fsflush(Req *r) { confirmflush(r->oldreq); - needkeyflush(r->oldreq); logflush(r->oldreq); respond(r, nil); } -Srv fs = { -.attach= fsattach, -.walk1= fswalk1, -.open= fsopen, -.read= fsread, -.write= fswrite, -.stat= fsstat, -.flush= fsflush, -.destroyfid= fsdestroyfid, -}; +static void +fsdestroyfid(Fid *fid) +{ + if(fid->qid.path == Qrpc && fid->aux){ + convhangup(fid->aux); + convclose(fid->aux); + } +} + +static Channel *creq; +static Channel *cfid, *cfidr; + +static void +fsreqthread(void *v) +{ + Req *r; + + USED(v); + + while((r = recvp(creq)) != nil){ + switch(r->ifcall.type){ + default: + respond(r, "bug in fsreqthread"); + break; + case Topen: + fsopen(r); + break; + case Tread: + fsread(r); + break; + case Twrite: + fswrite(r); + break; + case Tflush: + fsflush(r); + break; + } + } +} + +static void +fsclunkthread(void *v) +{ + Fid *f; + + USED(v); + + while((f = recvp(cfid)) != nil){ + fsdestroyfid(f); + sendp(cfidr, 0); + } +} + +static void +fsproc(void *v) +{ + USED(v); + + threadcreate(fsreqthread, nil, STACK); + threadcreate(fsclunkthread, nil, 4*1024 /*STACK*/); + threadexits(nil); +} + +static void +fsattach(Req *r) +{ + r->fid->qid = mkqid(QTDIR, qtop); + r->ofcall.qid = r->fid->qid; + respond(r, nil); +} + +static void +fssend(Req *r) +{ + sendp(creq, r); +} + +static void +fssendclunk(Fid *f) +{ + sendp(cfid, f); + recvp(cfidr); +} + +void +fsstart(Srv *s) +{ + USED(s); + + if(extrafactotumdir) + qtop = Qroot; + else + qtop = Qfactotum; + creq = chancreate(sizeof(Req*), 0); + cfid = chancreate(sizeof(Fid*), 0); + cfidr = chancreate(sizeof(Fid*), 0); + proccreate(fsproc, nil, STACK); +} + +void +fsend(Srv*) +{ + /* first approximation. just quit now. don't wait for convs to finish */ + threadkillgrp(threadgetgrp()); +} + +Srv fs; + +void +fsinit0(void) +{ + fs.attach = fsattach; + fs.walk1 = fswalk1; + fs.open = fssend; + fs.read = fssend; + fs.write = fssend; + fs.stat = fsstat; + fs.flush = fssend; + fs.destroyfid = fssendclunk; + fs.start = fsstart; + fs.end = fsend; +} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/httpdigest.c /sys/src/cmd/auth/factotum/httpdigest.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/httpdigest.c Thu Feb 23 11:41:36 2006 +++ /sys/src/cmd/auth/factotum/httpdigest.c Tue Jan 1 00:00:00 2013 @@ -8,50 +8,55 @@ * Server protocol: * unimplemented */ +#include "std.h" #include "dat.h" -enum -{ - CNeedChal, - CHaveResp, - - Maxphase, -}; - -static char *phasenames[Maxphase] = { -[CNeedChal] "CNeedChal", -[CHaveResp] "CHaveResp", -}; - -struct State -{ - char resp[MD5dlen*2+1]; -}; +static void +digest(char *user, char *realm, char *passwd, + char *nonce, char *method, char *uri, + char *dig); static int -hdinit(Proto *p, Fsstate *fss) +hdclient(Conv *c) { - int iscli; - State *s; + char *realm, *passwd, *user, *f[4], *s, resp[MD5dlen*2+1]; + int ret; + Key *k; + + ret = -1; + s = nil; + + c->state = "keylookup"; + k = keyfetch(c, "%A", c->attr); + if(k == nil) + goto out; + + user = strfindattr(k->attr, "user"); + realm = strfindattr(k->attr, "realm"); + passwd = strfindattr(k->attr, "!password"); + + if(convreadm(c, &s) < 0) + goto out; + if(tokenize(s, f, 4) != 3){ + werrstr("bad challenge -- want nonce method uri"); + goto out; + } - if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) - return failure(fss, nil); - if(!iscli) - return failure(fss, "%s server not supported", p->name); - - s = emalloc(sizeof *s); - fss->phasename = phasenames; - fss->maxphase = Maxphase; - fss->phase = CNeedChal; - fss->ps = s; - return RpcOk; + digest(user, realm, passwd, f[0], f[1], f[2], resp); + convwrite(c, resp, strlen(resp)); + ret = 0; + +out: + free(s); + keyclose(k); + return ret; } static void strtolower(char *s) { while(*s){ - *s = tolower(*s); + *s = tolower((uchar)*s); s++; } } @@ -98,87 +103,17 @@ strtolower(dig); } -static int -hdwrite(Fsstate *fss, void *va, uint n) -{ - State *s; - int ret; - char *a, *p, *r, *u, *t; - char *tok[4]; - Key *k; - Keyinfo ki; - Attr *attr; - - s = fss->ps; - a = va; - - if(fss->phase != CNeedChal) - return phaseerror(fss, "write"); - - attr = _delattr(_copyattr(fss->attr), "role"); - mkkeyinfo(&ki, fss, attr); - ret = findkey(&k, &ki, "%s", fss->proto->keyprompt); - _freeattr(attr); - if(ret != RpcOk) - return ret; - p = _strfindattr(k->privattr, "!password"); - if(p == nil) - return failure(fss, "key has no password"); - r = _strfindattr(k->attr, "realm"); - if(r == nil) - return failure(fss, "key has no realm"); - u = _strfindattr(k->attr, "user"); - if(u == nil) - return failure(fss, "key has no user"); - setattrs(fss->attr, k->attr); - - /* copy in case a is not null-terminated */ - t = emalloc(n+1); - memcpy(t, a, n); - t[n] = 0; - - /* get nonce, method, uri */ - if(tokenize(t, tok, 4) != 3) - return failure(fss, "bad challenge"); - - digest(u, r, p, tok[0], tok[1], tok[2], s->resp); - - free(t); - closekey(k); - fss->phase = CHaveResp; - return RpcOk; -} - -static int -hdread(Fsstate *fss, void *va, uint *n) +static Role hdroles[] = { - State *s; - - s = fss->ps; - if(fss->phase != CHaveResp) - return phaseerror(fss, "read"); - if(*n > strlen(s->resp)) - *n = strlen(s->resp); - memmove(va, s->resp, *n); - fss->phase = Established; - fss->haveai = 0; - return RpcOk; -} + "client", hdclient, + 0 +}; -static void -hdclose(Fsstate *fss) +Proto httpdigest = { - State *s; - s = fss->ps; - free(s); -} - -Proto httpdigest = { -.name= "httpdigest", -.init= hdinit, -.write= hdwrite, -.read= hdread, -.close= hdclose, -.addkey= replacekey, -.keyprompt= "user? realm? !password?" + "httpdigest", + hdroles, + "user? realm? !password?", + 0, + 0 }; diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/key.c /sys/src/cmd/auth/factotum/key.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/key.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/key.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,219 @@ +#include "std.h" +#include "dat.h" + +Ring ring; + +Key* +keyiterate(int skip, char *fmt, ...) +{ + int i; + Attr *a; + Key *k; + va_list arg; + + va_start(arg, fmt); + a = parseattrfmtv(fmt, arg); + va_end(arg); + + for(i=0; iattr, k->privattr)){ + if(skip-- > 0) + continue; + k->ref++; + freeattr(a); + return k; + } + } + freeattr(a); + werrstr("no key found"); + return nil; +} + +Key* +keylookup(char *fmt, ...) +{ + int i; + Attr *a; + Key *k; + va_list arg; + + va_start(arg, fmt); + a = parseattrfmtv(fmt, arg); + va_end(arg); + + for(i=0; iattr, k->privattr)){ + k->ref++; + freeattr(a); + return k; + } + } + freeattr(a); + werrstr("no key found"); + return nil; +} + +Key* +keyfetch(Conv *c, char *fmt, ...) +{ + int i, tag; + Attr *a; + Key *k; + va_list arg; + + va_start(arg, fmt); + a = parseattrfmtv(fmt, arg); + va_end(arg); + + flog("keyfetch %A", a); + tag = 0; + + for(i=0; itag) + tag = k->tag; + if(matchattr(a, k->attr, k->privattr)){ + k->ref++; + if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){ + k->ref--; + continue; + } + freeattr(a); + flog("using key %A %N", k->attr, k->privattr); + return k; + } + } + + if(needkey(c, a) < 0) + convneedkey(c, a); + + for(i=0; itag <= tag) + continue; + if(matchattr(a, k->attr, k->privattr)){ + k->ref++; + if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){ + k->ref--; + continue; + } + freeattr(a); + return k; + } + } + freeattr(a); + werrstr("no key found"); + return nil; +} + +static int taggen; + +void +keyadd(Key *k) +{ + int i; + + k->ref++; + k->tag = ++taggen; + for(i=0; iattr, ring.key[i]->attr, nil) + && matchattr(ring.key[i]->attr, k->attr, nil)){ + keyclose(ring.key[i]); + ring.key[i] = k; + return; + } + } + + ring.key = erealloc(ring.key, (ring.nkey+1)*sizeof(ring.key[0])); + ring.key[ring.nkey++] = k; +} + +void +keyclose(Key *k) +{ + if(k == nil) + return; + + if(--k->ref > 0) + return; + + if(k->proto->closekey) + (*k->proto->closekey)(k); + + freeattr(k->attr); + freeattr(k->privattr); + free(k); +} + +Key* +keyreplace(Conv *c, Key *k, char *fmt, ...) +{ + Key *kk; + char *msg; + Attr *a, *b, *bp; + va_list arg; + + va_start(arg, fmt); + msg = vsmprint(fmt, arg); + if(msg == nil) + sysfatal("out of memory"); + va_end(arg); + + /* replace prompted values with prompts */ + a = copyattr(k->attr); + bp = parseattr(k->proto->keyprompt); + for(b=bp; b; b=b->next){ + a = delattr(a, b->name); + a = addattr(a, "%q?", b->name); + } + freeattr(bp); + + if(badkey(c, k, msg, a) < 0) + convbadkey(c, k, msg, a); + kk = keylookup("%A", a); + freeattr(a); + keyclose(k); + if(kk == k){ + keyclose(kk); + werrstr("%s", msg); + return nil; + } + + if(strfindattr(kk->attr, "confirm")){ + if(confirmkey(c, kk) != 1){ + werrstr("key use not confirmed"); + keyclose(kk); + return nil; + } + } + return kk; +} + +void +keyevict(Conv *c, Key *k, char *fmt, ...) +{ + char *msg; + Attr *a, *b, *bp; + va_list arg; + + va_start(arg, fmt); + msg = vsmprint(fmt, arg); + if(msg == nil) + sysfatal("out of memory"); + va_end(arg); + + /* replace prompted values with prompts */ + a = copyattr(k->attr); + bp = parseattr(k->proto->keyprompt); + for(b=bp; b; b=b->next){ + a = delattr(a, b->name); + a = addattr(a, "%q?", b->name); + } + freeattr(bp); + + if(badkey(c, k, msg, nil) < 0) + convbadkey(c, k, msg, nil); + keyclose(k); +} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/log.c /sys/src/cmd/auth/factotum/log.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/log.c Mon Mar 3 01:31:08 2003 +++ /sys/src/cmd/auth/factotum/log.c Tue Jan 1 00:00:00 2013 @@ -1,7 +1,8 @@ +#include "std.h" #include "dat.h" void -logbufproc(Logbuf *lb) +lbkick(Logbuf *lb) { char *s; int n; @@ -41,45 +42,61 @@ } void -logbufread(Logbuf *lb, Req *r) +lbread(Logbuf *lb, Req *r) { if(lb->waitlast == nil) lb->waitlast = &lb->wait; - *(lb->waitlast) = r; - lb->waitlast = &r->aux; + *lb->waitlast = r; + lb->waitlast = (Req**)(void*)&r->aux; r->aux = nil; - logbufproc(lb); + lbkick(lb); } void -logbufflush(Logbuf *lb, Req *r) +lbflush(Logbuf *lb, Req *r) { Req **l; - for(l=&lb->wait; *l; l=&(*l)->aux){ + for(l=&lb->wait; *l; l=(Req**)(void*)&(*l)->aux){ if(*l == r){ *l = r->aux; r->aux = nil; if(*l == nil) lb->waitlast = l; - respond(r, "interrupted"); + r->ofcall.count = 0; + respond(r, nil); + closereq(r); break; } } } void -logbufappend(Logbuf *lb, char *buf) +lbappend(Logbuf *lb, char *fmt, ...) { - if(debug) - fprint(2, "%s\n", buf); + va_list arg; + + va_start(arg, fmt); + lbvappend(lb, fmt, arg); + va_end(arg); +} + +void +lbvappend(Logbuf *lb, char *fmt, va_list arg) +{ + char *s; + s = vsmprint(fmt, arg); + if(s == nil) + sysfatal("out of memory"); + if(0 && debug) + fprint(2, "FACT %s\n", s); if(lb->msg[lb->wp]) free(lb->msg[lb->wp]); - lb->msg[lb->wp] = estrdup9p(buf); + lb->msg[lb->wp] = s; if(++lb->wp == nelem(lb->msg)) lb->wp = 0; - logbufproc(lb); + lbkick(lb); } Logbuf logbuf; @@ -87,24 +104,22 @@ void logread(Req *r) { - logbufread(&logbuf, r); + lbread(&logbuf, r); } void logflush(Req *r) { - logbufflush(&logbuf, r); + lbflush(&logbuf, r); } void flog(char *fmt, ...) { - char buf[1024]; va_list arg; va_start(arg, fmt); - vseprint(buf, buf+sizeof buf, fmt, arg); + lbvappend(&logbuf, fmt, arg); va_end(arg); - logbufappend(&logbuf, buf); } diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/main.c /sys/src/cmd/auth/factotum/main.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/main.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/main.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,265 @@ +#include "std.h" +#include "dat.h" + +int mainstacksize = 64*1024; +int extrafactotumdir; +int debug; +int doprivate = 1; +int trysecstore = 1; +int kflag; +int sflag; +int uflag; +int askforkeys; +char *factname = "factotum"; +char *service; +char *owner; +char *authaddr; +static void private(void); +void gflag(char*); + +void +usage(void) +{ + fprint(2, "usage: factotum [-DSdknpu] [-a authaddr] [-m mtpt] [-s service]\n"); + fprint(2, " or factotum -g keypattern\n"); + fprint(2, " or factotum -g 'badkeyattr\\nmsg\\nkeypattern'\n"); + threadexitsall("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *mtpt, *s; + char *secstorepw; + char err[ERRMAX]; + Dir d; + + mtpt = "/mnt"; + extrafactotumdir = 1; + secstorepw = nil; + quotefmtinstall(); + fmtinstall('A', attrfmt); + fmtinstall('H', encodefmt); + fmtinstall('N', attrnamefmt); + + if(argc == 3 && strcmp(argv[1], "-g") == 0){ + gflag(argv[2]); + threadexitsall(nil); + } + + rfork(RFNOTEG); + + ARGBEGIN{ + default: + usage(); + case 'D': + chatty9p++; + break; + case 'S': /* server: read nvram, no prompting for keys */ + askforkeys = 0; + trysecstore = 0; + sflag = 1; + break; + case 'a': + authaddr = EARGF(usage()); + break; + case 'd': + debug = 1; + doprivate = 0; + break; + case 'g': + usage(); + case 'k': /* reinitialize nvram */ + kflag = 1; + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 'n': + trysecstore = 0; + break; + case 'p': + doprivate = 0; + break; + case 's': + service = EARGF(usage()); + break; + case 'u': /* user: set hostowner */ + uflag = 1; + break; + case 'x': + extrafactotumdir = 0; + break; + }ARGEND + + if(argc != 0) + usage(); + if(doprivate) + private(); + + initcap(); + + if(sflag){ + s = getnvramkey(kflag ? NVwrite : NVwriteonerr, &secstorepw); + if(s == nil) + fprint(2, "factotum warning: cannot read nvram: %r\n"); + else if(ctlwrite(s) < 0) + fprint(2, "factotum warning: cannot add nvram key: %r\n"); + if(secstorepw != nil) + trysecstore = 1; + if (s != nil) { + memset(s, 0, strlen(s)); + free(s); + } + } else if(uflag) + promptforhostowner(); + owner = getuser(); + + if(trysecstore && havesecstore()){ + while(secstorefetch(secstorepw) < 0){ + rerrstr(err, sizeof err); + if(strcmp(err, "cancel") == 0) + break; + fprint(2, "secstorefetch: %r\n"); + fprint(2, "Enter an empty password to quit.\n"); + free(secstorepw); + secstorepw = nil; /* just try nvram pw once */ + } + } + + fsinit0(); + threadpostmountsrv(&fs, service, mtpt, MBEFORE); + if(service){ + nulldir(&d); + d.mode = 0666; + s = emalloc(10+strlen(service)); + strcpy(s, "/srv/"); + strcat(s, service); + if(dirwstat(s, &d) < 0) + fprint(2, "factotum warning: cannot chmod 666 %s: %r\n", s); + free(s); + } + threadexits(nil); +} + +char *pmsg = "Warning! %s can't protect itself from debugging: %r\n"; +char *smsg = "Warning! %s can't turn off swapping: %r\n"; + +/* don't allow other processes to debug us and steal keys */ +static void +private(void) +{ + int fd; + char buf[64]; + + snprint(buf, sizeof(buf), "#p/%d/ctl", getpid()); + fd = open(buf, OWRITE); + if(fd < 0){ + fprint(2, pmsg, argv0); + return; + } + if(fprint(fd, "private") < 0) + fprint(2, pmsg, argv0); + if(fprint(fd, "noswap") < 0) + fprint(2, smsg, argv0); + close(fd); +} + +/* + * prompt user for a key. don't care about memory leaks, runs standalone + */ +static Attr* +promptforkey(int fd, char *params) +{ + char *def, *v; + Attr *a, *attr; + + attr = _parseattr(params); + fprint(fd, "\n!Adding key:"); + for(a=attr; a; a=a->next) + if(a->type != AttrQuery && a->name[0] != '!') + fprint(fd, " %q=%q", a->name, a->val); + fprint(fd, "\n"); + + for(a=attr; a; a=a->next){ + v = a->name; + if(a->type != AttrQuery || v[0]=='!') + continue; + def = nil; + if(strcmp(v, "user") == 0) + def = getuser(); + a->val = readcons(v, def, 0); + if(a->val == nil) + sysfatal("user terminated key input"); + a->type = AttrNameval; + } + for(a=attr; a; a=a->next){ + v = a->name; + if(a->type != AttrQuery || v[0]!='!') + continue; + def = nil; + if(strcmp(v+1, "user") == 0) + def = getuser(); + a->val = readcons(v+1, def, 1); + if(a->val == nil) + sysfatal("user terminated key input"); + a->type = AttrNameval; + } + fprint(fd, "!\n"); + return attr; +} + +/* + * send a key to the mounted factotum + */ +static int +sendkey(Attr *attr) +{ + char buf[8192]; + int rv, fd; + + fd = open("/mnt/factotum/ctl", OWRITE); + if(fd < 0) + sysfatal("opening factotum/ctl: %r"); + snprint(buf, sizeof buf, "key %A\n", attr); + rv = write(fd, buf, strlen(buf)); + close(fd); + return rv; +} + +/* askuser */ +void +askuser(int fd, char *params) +{ + Attr *attr; + + attr = promptforkey(fd, params); + if(attr == nil) + sysfatal("no key supplied"); + if(sendkey(attr) < 0) + sysfatal("sending key to factotum: %r"); +} + +void +gflag(char *s) +{ + char *f[4]; + int nf, fd; + + fd = open("/dev/cons", ORDWR); + if(fd < 0) + sysfatal("opening /dev/cons: %r"); + nf = getfields(s, f, nelem(f), 0, "\n"); + if(nf == 1){ /* needkey or old badkey */ + askuser(fd, s); + threadexitsall(nil); + } + if(nf == 3){ /* new badkey */ + fprint(fd, "\n"); + fprint(fd, "!replace: %s\n", f[0]); + fprint(fd, "!because: %s\n", f[1]); + askuser(fd, f[2]); + threadexitsall(nil); + } + usage(); +} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/mkfile /sys/src/cmd/auth/factotum/mkfile --- /n/sources/plan9/sys/src/cmd/auth/factotum/mkfile Thu Feb 23 11:41:33 2006 +++ /sys/src/cmd/auth/factotum/mkfile Tue Jan 1 00:00:00 2013 @@ -6,6 +6,7 @@ PROTO=\ apop.$O\ chap.$O\ + dsa.$O\ httpdigest.$O\ p9any.$O\ p9cr.$O\ @@ -16,12 +17,20 @@ FOFILES=\ $PROTO\ + attr.$O\ confirm.$O\ + conv.$O\ + ctl.$O\ fs.$O\ + key.$O\ log.$O\ + main.$O\ + pkcs1.$O\ + proto.$O\ rpc.$O\ - util.$O\ secstore.$O\ + util.$O\ + xio.$O\ HFILES=\ dat.h\ @@ -41,4 +50,7 @@ $LD -o $target $prereq $O.fgui: fgui.$O + $LD -o $target $prereq + +$O.test: test.$O $LD -o $target $prereq diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/p9any.c /sys/src/cmd/auth/factotum/p9any.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/p9any.c Sun Feb 6 16:08:23 2005 +++ /sys/src/cmd/auth/factotum/p9any.c Tue Jan 1 00:00:00 2013 @@ -1,424 +1,273 @@ +#include "std.h" +#include "dat.h" + /* - * p9any - protocol negotiator. + * p9any - protocol negotiator * * Protocol: - * Server->Client: list of proto@domain, tokenize separated, nul terminated - * Client->Server: proto domain, tokenize separated (not proto@domain), nul terminated - * - * Server protocol: - * read list of protocols. - * write null-terminated + * S->C: v.2 proto@dom proto@dom proto@dom... NUL + * C->S: proto dom NUL + * [negotiated proto continues] */ + +extern Proto p9sk1, p9sk2, p9cr; -#include "dat.h" - -static Proto *negotiable[] = { - &p9sk1, -}; - -struct State +static Proto* okproto[] = { - Fsstate subfss; - State *substate; /* be very careful; this is not one of our States */ - Proto *subproto; - int keyasked; - String *subdom; - int version; -}; - -enum -{ - CNeedProtos, - CHaveProto, - CNeedOK, - CRelay, - - SHaveProtos, - SNeedProto, - SHaveOK, - SRelay, - - Maxphase, + &p9sk1, + nil }; -static char *phasenames[Maxphase] = +static int +rolecall(Role *r, char *name, Conv *c) { -[CNeedProtos] "CNeedProtos", -[CHaveProto] "CHaveProto", -[CNeedOK] "CNeedOK", -[CRelay] "CRelay", -[SHaveProtos] "SHaveProtos", -[SNeedProto] "SNeedProto", -[SHaveOK] "SHaveOK", -[SRelay] "SRelay", -}; + for(; r->name; r++) + if(strcmp(r->name, name) == 0) + return r->fn(c); + werrstr("unknown role"); + return -1; +} static int -p9anyinit(Proto*, Fsstate *fss) +hasnul(void *v, int n) { - int iscli; - State *s; - - if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) - return failure(fss, nil); + char *c; - s = emalloc(sizeof *s); - fss = fss; - fss->phasename = phasenames; - fss->maxphase = Maxphase; - if(iscli) - fss->phase = CNeedProtos; + c = v; + if(n > 0 && c[n-1] == '\0') + return n; else - fss->phase = SHaveProtos; - s->version = 1; - fss->ps = s; - return RpcOk; + return AuthRpcMax; } -static void -p9anyclose(Fsstate *fss) +static int +p9anyserver(Conv *c) { - State *s; - - s = fss->ps; - if(s->subproto && s->subfss.ps && s->subproto->close) - (*s->subproto->close)(&s->subfss); - s->subproto = nil; - s->substate = nil; - s_free(s->subdom); - s->subdom = nil; - s->keyasked = 0; - memset(&s->subfss, 0, sizeof s->subfss); - free(s); -} + char *s, *p, *dom; + int i, j, n, m, ret; + char *tok[3]; + Attr *attr; + Key *k; -static void -setupfss(Fsstate *fss, State *s, Key *k) -{ - fss->attr = setattr(fss->attr, "proto=%q", s->subproto->name); - fss->attr = setattr(fss->attr, "dom=%q", _strfindattr(k->attr, "dom")); - s->subfss.attr = fss->attr; - s->subfss.phase = Notstarted; - s->subfss.sysuser = fss->sysuser; - s->subfss.seqnum = fss->seqnum; - s->subfss.conf = fss->conf; - s->subfss.nconf = fss->nconf; -} + ret = -1; + s = estrdup("v.2"); + n = 0; + attr = delattr(copyattr(c->attr), "proto"); + + for(i=0; iproto == okproto[j] + && (dom = strfindattr(k->attr, "dom")) != nil + && matchattr(attr, k->attr, k->privattr)){ + s = estrappend(s, " %s@%s", k->proto->name, dom); + n++; + } + } -static int -passret(Fsstate *fss, State *s, int ret) -{ - switch(ret){ - default: - return ret; - case RpcFailure: - if(s->subfss.phase == Broken) - fss->phase = Broken; - memmove(fss->err, s->subfss.err, sizeof fss->err); - return ret; - case RpcNeedkey: - memmove(fss->keyinfo, s->subfss.keyinfo, sizeof fss->keyinfo); - return ret; - case RpcOk: - if(s->subfss.haveai){ - fss->haveai = 1; - fss->ai = s->subfss.ai; - s->subfss.haveai = 0; - } - if(s->subfss.phase == Established) - fss->phase = Established; - return ret; - case RpcToosmall: - fss->rpc.nwant = s->subfss.rpc.nwant; - return ret; - case RpcConfirm: - fss->conf = s->subfss.conf; - fss->nconf = s->subfss.nconf; - return ret; + if(n == 0){ + werrstr("no valid keys"); + goto out; } -} -static int -p9anyread(Fsstate *fss, void *a, uint *n) -{ - int i, m, ophase, ret; - Attr *anew; - Key *k; - Keyinfo ki; - String *negstr; - State *s; - - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "read"); - - case SHaveProtos: - m = 0; - negstr = s_new(); - mkkeyinfo(&ki, fss, nil); - ki.attr = nil; - ki.noconf = 1; - ki.user = nil; - for(i=0; iattr), "proto=%q dom?", negotiable[i]->name); - ki.attr = anew; - for(ki.skip=0; findkey(&k, &ki, nil)==RpcOk; ki.skip++){ - if(m++) - s_append(negstr, " "); - s_append(negstr, negotiable[i]->name); - s_append(negstr, "@"); - s_append(negstr, _strfindattr(k->attr, "dom")); - closekey(k); - } - _freeattr(anew); - } - if(m == 0){ - s_free(negstr); - return failure(fss, Enegotiation); - } - i = s_len(negstr)+1; - if(*n < i){ - s_free(negstr); - return toosmall(fss, i); - } - *n = i; - memmove(a, s_to_c(negstr), i+1); - fss->phase = SNeedProto; - s_free(negstr); - return RpcOk; - - case CHaveProto: - i = strlen(s->subproto->name)+1+s_len(s->subdom)+1; - if(*n < i) - return toosmall(fss, i); - *n = i; - strcpy(a, s->subproto->name); - strcat(a, " "); - strcat(a, s_to_c(s->subdom)); - if(s->version == 1) - fss->phase = CRelay; - else - fss->phase = CNeedOK; - return RpcOk; - - case SHaveOK: - i = 3; - if(*n < i) - return toosmall(fss, i); - *n = i; - strcpy(a, "OK"); - fss->phase = SRelay; - return RpcOk; - - case CRelay: - case SRelay: - ophase = s->subfss.phase; - ret = (*s->subproto->read)(&s->subfss, a, n); - rpcrdwrlog(&s->subfss, "read", *n, ophase, ret); - return passret(fss, s, ret); + c->state = "write offer"; + p = s; + if(convwrite(c, p, strlen(p)+1) < 0) + goto out; + free(s); + s = nil; + + c->state = "read choice"; + if(convreadfn(c, hasnul, &s) < 0) + goto out; + + m = tokenize(s, tok, nelem(tok)); + if(m != 2){ + werrstr("bad protocol message"); + goto out; } -} -static char* -getdom(char *p) -{ - p = strchr(p, '@'); - if(p == nil) - return ""; - return p+1; -} + for(i=0; okproto[i]; i++) + if(strcmp(okproto[i]->name, tok[0]) == 0) + break; + if(!okproto[i]){ + werrstr("bad chosen protocol %q", tok[0]); + goto out; + } -static Proto* -findneg(char *name) -{ - int i, len; - char *p; + c->state = "write ok"; + if(convwrite(c, "OK\0", 3) < 0) + goto out; + + c->state = "start choice"; + attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]); + freeattr(c->attr); + c->attr = attr; + attr = nil; + c->proto = okproto[i]; + + if(rolecall(c->proto->roles, "server", c) < 0){ + werrstr("%s: %r", tok[0]); + goto out; + } - if(p = strchr(name, '@')) - len = p-name; - else - len = strlen(name); + ret = 0; - for(i=0; iname, name, len) == 0 && negotiable[i]->name[len] == 0) - return negotiable[i]; - return nil; +out: + free(s); + freeattr(attr); + return ret; } static int -p9anywrite(Fsstate *fss, void *va, uint n) +p9anyclient(Conv *c) { - char *a, *dom, *user, *token[20]; - int asking, i, m, ophase, ret; - Attr *anew, *anewsf, *attr; + char *s, **f, *tok[20], ok[3], *q, *user, *dom, *choice; + int i, n, ret, version; Key *k; - Keyinfo ki; + Attr *attr; Proto *p; - State *s; - s = fss->ps; - a = va; - switch(fss->phase){ - default: - return phaseerror(fss, "write"); - - case CNeedProtos: - if(n==0 || a[n-1] != '\0') - return toosmall(fss, 2048); - a = estrdup(a); - m = tokenize(a, token, nelem(token)); - if(m > 0 && strncmp(token[0], "v.", 2) == 0){ - s->version = atoi(token[0]+2); - if(s->version != 2){ - free(a); - return failure(fss, "unknown version of p9any"); - } + ret = -1; + s = nil; + k = nil; + + user = strfindattr(c->attr, "user"); + dom = strfindattr(c->attr, "dom"); + + /* + * if the user is the factotum owner, any key will do. + * if not, then if we have a speakfor key, + * we will only vouch for the user's local identity. + * + * this logic is duplicated in p9sk1.c + */ + attr = delattr(copyattr(c->attr), "role"); + attr = delattr(attr, "proto"); + if(strcmp(c->sysuser, owner) == 0) + attr = addattr(attr, "role=client"); + else if(user==nil || strcmp(c->sysuser, user)==0){ + attr = delattr(attr, "user"); + attr = addattr(attr, "role=speakfor"); + }else{ + werrstr("will not authenticate for %q as %q", c->sysuser, user); + goto out; + } + + c->state = "read offer"; + if(convreadfn(c, hasnul, &s) < 0) + goto out; + + c->state = "look for keys"; + n = tokenize(s, tok, nelem(tok)); + f = tok; + version = 1; + if(n > 0 && memcmp(f[0], "v.", 2) == 0){ + version = atoi(f[0]+2); + if(version != 2){ + werrstr("unknown p9any version: %s", f[0]); + goto out; } + f++; + n--; + } + + /* look for keys that don't need confirmation */ + for(i=0; iattr, "confirm") == nil) + goto found; + *--q = '@'; + } + + /* look for any keys at all */ + for(i=0; istate = "ask for keys"; + for(i=0; ikeyprompt == nil){ + *--q = '@'; + continue; + } + if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt)) + goto found; + *--q = '@'; + } + + /* nothing worked */ + werrstr("unable to find common key"); + goto out; + +found: + /* f[i] is the chosen protocol, q the chosen domain */ + attr = addattr(attr, "proto=%q dom=%q", f[i], q); + c->state = "write choice"; - /* - * look for a key - */ - anew = _delattr(_delattr(_copyattr(fss->attr), "proto"), "role"); - anewsf = _delattr(_copyattr(anew), "user"); - user = _strfindattr(anew, "user"); - k = nil; - p = nil; - dom = nil; - for(i=(s->version==1?0:1); isysuser)==0){ - ki.attr = anewsf; - ki.user = nil; - ret = findkey(&k, &ki, "proto=%q dom=%q role=speakfor %s", - p->name, dom, p->keyprompt); - } - if(ret == RpcFailure){ - ki.attr = anew; - ki.user = fss->sysuser; - ret = findkey(&k, &ki, - "proto=%q dom=%q role=client %s", - p->name, dom, p->keyprompt); - } - if(ret == RpcConfirm){ - free(a); - return ret; - } - if(ret == RpcOk) - break; - } - _freeattr(anewsf); + /* have a key: go for it */ + choice = estrappend(nil, "%q %q", f[i], q); + if(convwrite(c, choice, strlen(choice)+1) < 0){ + free(choice); + goto out; + } + free(choice); - /* - * no acceptable key, go through the proto@domains one at a time. - */ - asking = 0; - if(k == nil){ - while(!asking && s->keyasked < m){ - p = findneg(token[s->keyasked]); - if(p == nil){ - s->keyasked++; - continue; - } - dom = getdom(token[s->keyasked]); - mkkeyinfo(&ki, fss, nil); - ki.attr = anew; - ret = findkey(&k, &ki, - "proto=%q dom=%q role=client %s", - p->name, dom, p->keyprompt); - s->keyasked++; - if(ret == RpcNeedkey){ - asking = 1; - break; - } - } - } - if(k == nil){ - free(a); - _freeattr(anew); - if(asking) - return RpcNeedkey; - else if(s->keyasked) - return failure(fss, nil); - else - return failure(fss, Enegotiation); - } - s->subdom = s_copy(dom); - s->subproto = p; - free(a); - _freeattr(anew); - setupfss(fss, s, k); - closekey(k); - ret = (*s->subproto->init)(p, &s->subfss); - rpcstartlog(s->subfss.attr, &s->subfss, ret); - if(ret == RpcOk) - fss->phase = CHaveProto; - return passret(fss, s, ret); - - case SNeedProto: - if(n==0 || a[n-1] != '\0') - return toosmall(fss, n+1); - a = estrdup(a); - m = tokenize(a, token, nelem(token)); - if(m != 2){ - free(a); - return failure(fss, Ebadarg); - } - p = findneg(token[0]); - if(p == nil){ - free(a); - return failure(fss, Enegotiation); - } - attr = _delattr(_copyattr(fss->attr), "proto"); - mkkeyinfo(&ki, fss, nil); - ki.attr = attr; - ki.user = nil; - ret = findkey(&k, &ki, "proto=%q dom=%q role=server", token[0], token[1]); - free(a); - _freeattr(attr); - if(ret == RpcConfirm) - return ret; - if(ret != RpcOk) - return failure(fss, Enegotiation); - s->subproto = p; - setupfss(fss, s, k); - closekey(k); - ret = (*s->subproto->init)(p, &s->subfss); - if(ret == RpcOk){ - if(s->version == 1) - fss->phase = SRelay; - else - fss->phase = SHaveOK; - } - return passret(fss, s, ret); + if(version == 2){ + c->state = "read ok"; + if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0) + goto out; + } - case CNeedOK: - if(n < 3) - return toosmall(fss, 3); - if(strcmp("OK", a) != 0) - return failure(fss, "server gave up"); - fss->phase = CRelay; - return RpcOk; - - case CRelay: - case SRelay: - ophase = s->subfss.phase; - ret = (*s->subproto->write)(&s->subfss, va, n); - rpcrdwrlog(&s->subfss, "write", n, ophase, ret); - return passret(fss, s, ret); + c->state = "start choice"; + c->proto = protolookup(f[i]); + freeattr(c->attr); + c->attr = attr; + attr = nil; + + if(rolecall(c->proto->roles, "client", c) < 0){ + werrstr("%s: %r", c->proto->name); + goto out; } + + ret = 0; + +out: + keyclose(k); + freeattr(attr); + free(s); + return ret; } -Proto p9any = +static Role +p9anyroles[] = { -.name= "p9any", -.init= p9anyinit, -.write= p9anywrite, -.read= p9anyread, -.close= p9anyclose, + "client", p9anyclient, + "server", p9anyserver, + 0 +}; + +Proto p9any = { + "p9any", + p9anyroles }; + diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/p9cr.c /sys/src/cmd/auth/factotum/p9cr.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/p9cr.c Wed Oct 19 22:36:52 2011 +++ /sys/src/cmd/auth/factotum/p9cr.c Tue Jan 1 00:00:00 2013 @@ -1,186 +1,344 @@ /* - * p9cr, vnc - textual challenge/response authentication + * p9cr - one-sided challenge/response authentication * - * Client protocol: [currently unimplemented] - * write challenge - * read response + * Protocol: * - * Server protocol: - * write user - * read challenge - * write response + * 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" -enum +/* shared with auth dialing routines */ +typedef struct ServerState ServerState; +struct ServerState { - Maxchal= 64, -}; - -typedef struct State State; -struct State -{ - Key *key; - int astype; - int asfd; - Ticket t; + int asfd; + Key *k; Ticketreq tr; - char chal[Maxchal]; - int challen; - char resp[Maxchal]; - int resplen; + Ticket t; + char *dom; + char *hostid; }; enum { - CNeedChal, - CHaveResp, - - SHaveChal, - SNeedResp, - - Maxphase, + MAXCHAL = 64, + MAXRESP = 64, }; -static char *phasenames[Maxphase] = -{ -[CNeedChal] "CNeedChal", -[CHaveResp] "CHaveResp", - -[SHaveChal] "SHaveChal", -[SNeedResp] "SNeedResp", -}; +extern Proto p9cr, vnc; +static int p9response(Key*, char*, uchar*, uchar*); +static int vncresponse(Key*, char*, uchar*, uchar*); +static int p9crchal(ServerState *s, int, char*, uchar*, int); +static int p9crresp(ServerState*, uchar*, int); -static void -p9crclose(Fsstate *fss) +static int +p9crcheck(Key *k) { - State *s; - - s = fss->ps; - if(s->asfd >= 0){ - close(s->asfd); - s->asfd = -1; + if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){ + werrstr("need user and !password attributes"); + return -1; } - free(s); + return 0; } -static int getchal(State*, Fsstate*); - static int -p9crinit(Proto *p, Fsstate *fss) +p9crclient(Conv *c) { - int iscli, ret; - char *user; - State *s; + char *pw, *res, *user; + int challen, resplen, ntry, ret; Attr *attr; - Keyinfo ki; + Key *k; + uchar chal[MAXCHAL+1], resp[MAXRESP]; + int (*response)(Key *, char*, uchar*, uchar*); + + k = nil; + res = nil; + ret = -1; + attr = c->attr; + + if(c->proto == &p9cr){ + challen = NETCHLEN; + response = p9response; + attr = _mkattr(AttrNameval, "proto", "p9sk1", _delattr(_copyattr(attr), "proto")); + }else if(c->proto == &vnc){ + challen = MAXCHAL; + response = vncresponse; + }else{ + werrstr("bad proto"); + goto out; + } - if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) - return failure(fss, nil); - - s = emalloc(sizeof(*s)); - s->asfd = -1; - if(p == &p9cr){ - s->astype = AuthChal; - s->challen = NETCHLEN; - }else if(p == &vnc){ - s->astype = AuthVNC; - s->challen = Maxchal; - }else - abort(); - - if(iscli){ - fss->phase = CNeedChal; - if(p == &p9cr) - attr = setattr(_copyattr(fss->attr), "proto=p9sk1"); - else - attr = nil; - ret = findkey(&s->key, mkkeyinfo(&ki, fss, attr), - "role=client %s", p->keyprompt); - _freeattr(attr); - if(ret != RpcOk){ - free(s); - return ret; + 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; } - fss->ps = s; - }else{ - if((ret = findp9authkey(&s->key, fss)) != RpcOk){ - free(s); - return ret; + if((user = strfindattr(k->attr, "user")) == nil){ + werrstr("key has no user (cannot happen)"); + goto out; } - if((user = _strfindattr(fss->attr, "user")) == nil){ - free(s); - return failure(fss, "no user name specified in start msg"); + + if(convprint(c, "%s", user) < 0) + goto out; + + if(convread(c, chal, challen) < 0) + goto out; + chal[challen] = 0; + + if((resplen = (*response)(k, 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; } - if(strlen(user) >= sizeof s->tr.uid){ - free(s); - return failure(fss, "user name too long"); + } + + 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"); + a = delattr(a, "user"); + 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; +*/ + if((user = strfindattr(c->attr, "user")) == nil) + 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; } - fss->ps = s; - strcpy(s->tr.uid, user); - ret = getchal(s, fss); - if(ret != RpcOk){ - p9crclose(fss); /* frees s */ - fss->ps = nil; + + c->state = "authwrite"; + switch(p9crresp(&s, resp, resplen)){ + case -1: + fprint(2, "factotum: p9crresp: %r\n"); + goto out; + case 0: + c->state = "write status"; + if(convprint(c, "bad authentication failed %r") < 0) + goto out; + break; + case 1: + c->state = "write status"; +/* + if(convprint(c, "ok") < 0) + goto out; +*/ + c->done = 1; + c->active = 0; + if(convprint(c, "haveai") < 0) + goto out; + goto ok; } +/* free(user); */ + free(resp); + resp = nil; } - fss->phasename = phasenames; - fss->maxphase = Maxphase; + +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 -p9crread(Fsstate *fss, void *va, uint *n) +p9crchal(ServerState *s, int astype, char *user, uchar *chal, int challen) { - int m; - State *s; + char trbuf[TICKREQLEN]; + Ticketreq tr; + int n; - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "read"); - - case CHaveResp: - if(s->resplen < *n) - *n = s->resplen; - memmove(va, s->resp, *n); - fss->phase = Established; - return RpcOk; - - case SHaveChal: - if(s->astype == AuthChal) - m = strlen(s->chal); /* ascii string */ - else - m = s->challen; /* fixed length binary */ - if(m > *n) - return toosmall(fss, m); - *n = m; - memmove(va, s->chal, m); - fss->phase = SNeedResp; - return RpcOk; + 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 -p9response(Fsstate *fss, State *s) +p9crresp(ServerState *s, uchar *resp, int resplen) { + char tabuf[TICKETLEN+AUTHENTLEN]; + int n; + Authenticator a; + Ticket t; + + if(xiowrite(s->asfd, resp, resplen) != resplen) + return -1; + + n = xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN); + if(n != TICKETLEN+AUTHENTLEN){ + werrstr("short ticket %d; want %d\n", n, TICKETLEN+AUTHENTLEN); + return 0; + } + + convM2T(tabuf, &t, s->k->priv); + if(t.num != AuthTs + || memcmp(t.chal, s->tr.chal, sizeof t.chal) != 0){ + werrstr("key mismatch with auth server"); + return -1; + } + + convM2A(tabuf+TICKETLEN, &a, t.key); + if(a.num != AuthAc + || memcmp(a.chal, s->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(Key*, char *pw, uchar *chal, uchar *resp) +{ char key[DESKEYLEN]; uchar buf[8]; - ulong chal; - char *pw; + ulong x; - pw = _strfindattr(s->key->privattr, "!password"); - if(pw == nil) - return failure(fss, "vncresponse cannot happen"); passtokey(key, pw); memset(buf, 0, 8); - snprint((char*)buf, sizeof buf, "%d", atoi(s->chal)); - if(encrypt(key, buf, 8) < 0) - return failure(fss, "can't encrypt response"); - chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3]; - s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal); - return RpcOk; + 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 uchar tab[256]; @@ -207,157 +365,65 @@ } static int -vncaddkey(Key *k, int before) +vncresponse(Key *k, char */*pw*/, uchar *chal, uchar *resp) +{ + DESstate des; + + memmove(resp, chal, MAXCHAL); + setupDESstate(&des, k->priv, nil); + desECBencrypt(resp, MAXCHAL, &des); + return MAXCHAL; +} + +static int +vnccheck(Key *k) { uchar *p; char *s; - k->priv = emalloc(8+1); - if(s = _strfindattr(k->privattr, "!password")){ + if(!strfindattr(k->attr, "user") || (s = strfindattr(k->privattr, "!password")) == nil){ + werrstr("need user and !password attributes"); + return -1; + } + if(k->priv == nil){ mktab(); + k->priv = emalloc(8+1); memset(k->priv, 0, 8+1); strncpy((char*)k->priv, s, 8); for(p=k->priv; *p; p++) *p = tab[*p]; - }else{ - werrstr("no key data"); - return -1; } - return replacekey(k, before); + return 0; } static void -vncclosekey(Key *k) +vncclose(Key *k) { free(k->priv); + k->priv = nil; } -static int -vncresponse(Fsstate*, State *s) -{ - DESstate des; - - memmove(s->resp, s->chal, sizeof s->chal); - setupDESstate(&des, s->key->priv, nil); - desECBencrypt((uchar*)s->resp, s->challen, &des); - s->resplen = s->challen; - return RpcOk; -} - -static int -p9crwrite(Fsstate *fss, void *va, uint n) +static Role +p9crroles[] = { - char tbuf[TICKETLEN+AUTHENTLEN]; - State *s; - char *data = va; - Authenticator a; - char resp[Maxchal]; - int ret; - - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "write"); - - case CNeedChal: - if(n >= sizeof(s->chal)) - return failure(fss, Ebadarg); - memset(s->chal, 0, sizeof s->chal); - memmove(s->chal, data, n); - s->challen = n; - - if(s->astype == AuthChal) - ret = p9response(fss, s); - else - ret = vncresponse(fss, s); - if(ret != RpcOk) - return ret; - fss->phase = CHaveResp; - return RpcOk; - - case SNeedResp: - /* send response to auth server and get ticket */ - if(n > sizeof(resp)) - return failure(fss, Ebadarg); - memset(resp, 0, sizeof resp); - memmove(resp, data, n); - if(write(s->asfd, resp, s->challen) != s->challen) - return failure(fss, Easproto); - - /* get ticket plus authenticator from auth server */ - if(_asrdresp(s->asfd, tbuf, TICKETLEN+AUTHENTLEN) < 0) - return failure(fss, nil); - - /* check ticket */ - convM2T(tbuf, &s->t, s->key->priv); - if(s->t.num != AuthTs - || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ - if (s->key->successes == 0) - disablekey(s->key); - return failure(fss, Easproto); - } - s->key->successes++; - convM2A(tbuf+TICKETLEN, &a, s->t.key); - if(a.num != AuthAc - || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0 - || a.id != 0) - return failure(fss, Easproto); - - fss->haveai = 1; - fss->ai.cuid = s->t.cuid; - fss->ai.suid = s->t.suid; - fss->ai.nsecret = 0; - fss->ai.secret = nil; - fss->phase = Established; - return RpcOk; - } -} - -static int -getchal(State *s, Fsstate *fss) -{ - char trbuf[TICKREQLEN]; - int n; - - safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof(s->tr.hostid)); - safecpy(s->tr.authdom, _strfindattr(s->key->attr, "dom"), sizeof(s->tr.authdom)); - s->tr.type = s->astype; - convTR2M(&s->tr, trbuf); - - /* get challenge from auth server */ - s->asfd = _authdial(nil, _strfindattr(s->key->attr, "dom")); - if(s->asfd < 0) - return failure(fss, Easproto); - if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) - return failure(fss, Easproto); - n = _asrdresp(s->asfd, s->chal, s->challen); - if(n <= 0){ - if(n == 0) - werrstr("_asrdresp short read"); - return failure(fss, nil); - } - s->challen = n; - fss->phase = SHaveChal; - return RpcOk; -} + "client", p9crclient, + "server", p9crserver, + 0 +}; -Proto p9cr = -{ -.name= "p9cr", -.init= p9crinit, -.write= p9crwrite, -.read= p9crread, -.close= p9crclose, -.keyprompt= "user? !password?", +Proto p9cr = { + "p9cr", + p9crroles, + "user? !password?", + p9crcheck, + nil }; -Proto vnc = -{ -.name= "vnc", -.init= p9crinit, -.write= p9crwrite, -.read= p9crread, -.close= p9crclose, -.keyprompt= "!password?", -.addkey= vncaddkey, +/* still need to implement vnc key generator */ +Proto vnc = { + "vnc", + p9crroles, + "user? !password?", + vnccheck, + vncclose, }; diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/p9sk1.c /sys/src/cmd/auth/factotum/p9sk1.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/p9sk1.c Thu May 17 12:34:49 2007 +++ /sys/src/cmd/auth/factotum/p9sk1.c Tue Jan 1 00:00:00 2013 @@ -15,472 +15,346 @@ * write authenticator[authentlen] */ +#include "std.h" #include "dat.h" -struct State -{ - int vers; - Key *key; - Ticket t; - Ticketreq tr; - char cchal[CHALLEN]; - char tbuf[TICKETLEN+AUTHENTLEN]; - char authkey[DESKEYLEN]; - uchar *secret; - int speakfor; -}; +extern Proto p9sk1, p9sk2; +static int gettickets(Ticketreq*, char*, Key*); +#define max(a, b) ((a) > (b) ? (a) : (b)) enum { - /* client phases */ - CHaveChal, - CNeedTreq, - CHaveTicket, - CNeedAuth, - - /* server phases */ - SNeedChal, - SHaveTreq, - SNeedTicket, - SHaveAuth, - - Maxphase, + MAXAUTH = max(TICKREQLEN, TICKETLEN+max(TICKETLEN, AUTHENTLEN)) }; -static char *phasenames[Maxphase] = -{ -[CHaveChal] "CHaveChal", -[CNeedTreq] "CNeedTreq", -[CHaveTicket] "CHaveTicket", -[CNeedAuth] "CNeedAuth", - -[SNeedChal] "SNeedChal", -[SHaveTreq] "SHaveTreq", -[SNeedTicket] "SNeedTicket", -[SHaveAuth] "SHaveAuth", -}; - -static int gettickets(State*, char*, char*); - static int -p9skinit(Proto *p, Fsstate *fss) +p9skclient(Conv *c) { - State *s; - int iscli, ret; + char *user; + char cchal[CHALLEN]; + uchar secret[8]; + char buf[MAXAUTH]; + int speakfor, ret; + Attr *a; + Authenticator au; Key *k; - Keyinfo ki; - Attr *attr; - - if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) - return failure(fss, nil); + Ticket t; + Ticketreq tr; - s = emalloc(sizeof *s); - fss = fss; - fss->phasename = phasenames; - fss->maxphase = Maxphase; - if(p == &p9sk1) - s->vers = 1; - else if(p == &p9sk2) - s->vers = 2; - else - abort(); - if(iscli){ - switch(s->vers){ - case 1: - fss->phase = CHaveChal; - memrandom(s->cchal, CHALLEN); - break; - case 2: - fss->phase = CNeedTreq; - break; - } - }else{ - s->tr.type = AuthTreq; - attr = setattr(_copyattr(fss->attr), "proto=p9sk1"); - mkkeyinfo(&ki, fss, attr); - ki.user = nil; - ret = findkey(&k, &ki, "user? dom?"); - _freeattr(attr); - if(ret != RpcOk){ - free(s); - return ret; - } - safecpy(s->tr.authid, _strfindattr(k->attr, "user"), sizeof(s->tr.authid)); - safecpy(s->tr.authdom, _strfindattr(k->attr, "dom"), sizeof(s->tr.authdom)); - s->key = k; - memrandom(s->tr.chal, sizeof s->tr.chal); - switch(s->vers){ - case 1: - fss->phase = SNeedChal; - break; - case 2: - fss->phase = SHaveTreq; - memmove(s->cchal, s->tr.chal, CHALLEN); - break; - } + ret = -1; + a = nil; + k = nil; + + /* p9sk1: send client challenge */ + if(c->proto == &p9sk1){ + c->state = "write challenge"; + memrandom(cchal, CHALLEN); + if(convwrite(c, cchal, CHALLEN) < 0) + goto out; } - fss->ps = s; - return RpcOk; -} - -static int -p9skread(Fsstate *fss, void *a, uint *n) -{ - int m; - State *s; - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "read"); - - case CHaveChal: - m = CHALLEN; - if(*n < m) - return toosmall(fss, m); - *n = m; - memmove(a, s->cchal, m); - fss->phase = CNeedTreq; - return RpcOk; - - case SHaveTreq: - m = TICKREQLEN; - if(*n < m) - return toosmall(fss, m); - *n = m; - convTR2M(&s->tr, a); - fss->phase = SNeedTicket; - return RpcOk; - - case CHaveTicket: - m = TICKETLEN+AUTHENTLEN; - if(*n < m) - return toosmall(fss, m); - *n = m; - memmove(a, s->tbuf, m); - fss->phase = CNeedAuth; - return RpcOk; - - case SHaveAuth: - m = AUTHENTLEN; - if(*n < m) - return toosmall(fss, m); - *n = m; - memmove(a, s->tbuf+TICKETLEN, m); - fss->ai.cuid = s->t.cuid; - fss->ai.suid = s->t.suid; - s->secret = emalloc(8); - des56to64((uchar*)s->t.key, s->secret); - fss->ai.secret = s->secret; - fss->ai.nsecret = 8; - fss->haveai = 1; - fss->phase = Established; - return RpcOk; + /* read ticket request */ + c->state = "read tickreq"; + if(convread(c, buf, TICKREQLEN) < 0) + goto out; + convM2TR(buf, &tr); + + /* p9sk2: use server challenge as client challenge */ + if(c->proto == &p9sk2) + memmove(cchal, tr.chal, CHALLEN); + + /* + * find a key. + * + * if the user is the factotum owner, any key will do. + * if not, then if we have a speakfor key, + * we will only vouch for the user's local identity. + * + * this logic is duplicated in p9any.c + */ + user = strfindattr(c->attr, "user"); + a = delattr(copyattr(c->attr), "role"); + a = addattr(a, "proto=p9sk1"); + + if(strcmp(c->sysuser, owner) == 0){ + speakfor = 0; + a = addattr(a, "proto=p9sk1 user? dom=%q", tr.authdom); + }else if(user==nil || strcmp(c->sysuser, user)==0){ + speakfor = 1; + a = delattr(a, "user"); + a = addattr(a, "proto=p9sk1 user? dom=%q role=speakfor", tr.authdom); + }else{ + werrstr("will not authenticate for %q as %q", c->sysuser, user); + goto out; } -} - -static int -p9skwrite(Fsstate *fss, void *a, uint n) -{ - int m, ret, sret; - char tbuf[2*TICKETLEN], trbuf[TICKREQLEN], *user; - Attr *attr; - Authenticator auth; - State *s; - Key *srvkey; - Keyinfo ki; - - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "write"); - - case SNeedChal: - m = CHALLEN; - if(n < m) - return toosmall(fss, m); - memmove(s->cchal, a, m); - fss->phase = SHaveTreq; - return RpcOk; - - case CNeedTreq: - m = TICKREQLEN; - if(n < m) - return toosmall(fss, m); - - /* remember server's chal */ - convM2TR(a, &s->tr); - if(s->vers == 2) - memmove(s->cchal, s->tr.chal, CHALLEN); - - if(s->key != nil) - closekey(s->key); - - attr = _delattr(_delattr(_copyattr(fss->attr), "role"), "user"); - attr = setattr(attr, "proto=p9sk1"); - user = _strfindattr(fss->attr, "user"); - /* - * If our client is the user who started factotum (client==owner), then - * he can use whatever keys we have to speak as whoever he pleases. - * If, on the other hand, we're speaking on behalf of someone else, - * we will only vouch for their name on the local system. - * - * We do the sysuser findkey second so that if we return RpcNeedkey, - * the correct key information gets asked for. - */ - srvkey = nil; - s->speakfor = 0; - sret = RpcFailure; - if(user==nil || strcmp(user, fss->sysuser) == 0){ - mkkeyinfo(&ki, fss, attr); - ki.user = nil; - sret = findkey(&srvkey, &ki, - "role=speakfor dom=%q user?", s->tr.authdom); - } - if(user != nil) - attr = setattr(attr, "user=%q", user); - mkkeyinfo(&ki, fss, attr); - ret = findkey(&s->key, &ki, - "role=client dom=%q %s", s->tr.authdom, p9sk1.keyprompt); - if(ret == RpcOk) - closekey(srvkey); - else if(sret == RpcOk){ - s->key = srvkey; - s->speakfor = 1; - }else if(ret == RpcConfirm || sret == RpcConfirm){ - _freeattr(attr); - return RpcConfirm; - }else{ - _freeattr(attr); - return ret; - } - /* fill in the rest of the request */ - s->tr.type = AuthTreq; - safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof s->tr.hostid); - if(s->speakfor) - safecpy(s->tr.uid, fss->sysuser, sizeof s->tr.uid); + for(;;){ + c->state = "find key"; + k = keyfetch(c, "%A", a); + if(k == nil) + goto out; + + /* relay ticket request to auth server, get tickets */ + strcpy(tr.hostid, strfindattr(k->attr, "user")); + if(speakfor) + strcpy(tr.uid, c->sysuser); else - safecpy(s->tr.uid, s->tr.hostid, sizeof s->tr.uid); + strcpy(tr.uid, tr.hostid); - convTR2M(&s->tr, trbuf); + c->state = "get tickets"; + if(gettickets(&tr, buf, k) < 0) + goto out; - /* get tickets, from auth server or invent if we can */ - if(gettickets(s, trbuf, tbuf) < 0){ - _freeattr(attr); - return failure(fss, nil); - } + convM2T(buf, &t, k->priv); + if(t.num == AuthTc) + break; - convM2T(tbuf, &s->t, (char*)s->key->priv); - if(s->t.num != AuthTc){ - if(s->key->successes == 0 && !s->speakfor) - disablekey(s->key); - if(askforkeys && !s->speakfor){ - snprint(fss->keyinfo, sizeof fss->keyinfo, - "%A %s", attr, p9sk1.keyprompt); - _freeattr(attr); - return RpcNeedkey; - }else{ - _freeattr(attr); - return failure(fss, Ebadkey); - } + /* we don't agree with the auth server about the key; try again */ + c->state = "replace key"; + if((k = keyreplace(c, k, "key mismatch with auth server")) == nil){ + werrstr("key mismatch with auth server"); + goto out; } - s->key->successes++; - _freeattr(attr); - memmove(s->tbuf, tbuf+TICKETLEN, TICKETLEN); - - auth.num = AuthAc; - memmove(auth.chal, s->tr.chal, CHALLEN); - auth.id = 0; - convA2M(&auth, s->tbuf+TICKETLEN, s->t.key); - fss->phase = CHaveTicket; - return RpcOk; - - case SNeedTicket: - m = TICKETLEN+AUTHENTLEN; - if(n < m) - return toosmall(fss, m); - convM2T(a, &s->t, (char*)s->key->priv); - if(s->t.num != AuthTs - || memcmp(s->t.chal, s->tr.chal, CHALLEN) != 0) - return failure(fss, Easproto); - convM2A((char*)a+TICKETLEN, &auth, s->t.key); - if(auth.num != AuthAc - || memcmp(auth.chal, s->tr.chal, CHALLEN) != 0 - || auth.id != 0) - return failure(fss, Easproto); - auth.num = AuthAs; - memmove(auth.chal, s->cchal, CHALLEN); - auth.id = 0; - convA2M(&auth, s->tbuf+TICKETLEN, s->t.key); - fss->phase = SHaveAuth; - return RpcOk; - - case CNeedAuth: - m = AUTHENTLEN; - if(n < m) - return toosmall(fss, m); - convM2A(a, &auth, s->t.key); - if(auth.num != AuthAs - || memcmp(auth.chal, s->cchal, CHALLEN) != 0 - || auth.id != 0) - return failure(fss, Easproto); - fss->ai.cuid = s->t.cuid; - fss->ai.suid = s->t.suid; - s->secret = emalloc(8); - des56to64((uchar*)s->t.key, s->secret); - fss->ai.secret = s->secret; - fss->ai.nsecret = 8; - fss->haveai = 1; - fss->phase = Established; - return RpcOk; } -} - -static void -p9skclose(Fsstate *fss) -{ - State *s; - s = fss->ps; - if(s->secret != nil){ - free(s->secret); - s->secret = nil; + /* send second ticket and authenticator to server */ + c->state = "write ticket+auth"; + memmove(buf, buf+TICKETLEN, TICKETLEN); + au.num = AuthAc; + memmove(au.chal, tr.chal, CHALLEN); + au.id = 0; + convA2M(&au, buf+TICKETLEN, t.key); + if(convwrite(c, buf, TICKETLEN+AUTHENTLEN) < 0) + goto out; + + /* read authenticator from server */ + c->state = "read auth"; + if(convread(c, buf, AUTHENTLEN) < 0) + goto out; + convM2A(buf, &au, t.key); + if(au.num != AuthAs || memcmp(au.chal, cchal, CHALLEN) != 0 || au.id != 0){ + werrstr("server lies through his teeth"); + goto out; } - if(s->key != nil){ - closekey(s->key); - s->key = nil; - } - free(s); -} -static int -unhex(char c) -{ - if('0' <= c && c <= '9') - return c-'0'; - if('a' <= c && c <= 'f') - return c-'a'+10; - if('A' <= c && c <= 'F') - return c-'A'+10; - abort(); - return -1; + /* success */ + c->attr = addcap(c->attr, c->sysuser, &t); + flog("p9skclient success %A", c->attr); /* before adding secret! */ + des56to64((uchar*)t.key, secret); + c->attr = addattr(c->attr, "secret=%.8H", secret); + ret = 0; + +out: + if(ret < 0) + flog("p9skclient: %r"); + freeattr(a); + keyclose(k); + return ret; } static int -hexparse(char *hex, uchar *dat, int ndat) +p9skserver(Conv *c) { - int i; + char cchal[CHALLEN], buf[MAXAUTH]; + uchar secret[8]; + int ret; + Attr *a; + Authenticator au; + Key *k; + Ticketreq tr; + Ticket t; - if(strlen(hex) != 2*ndat) - return -1; - if(hex[strspn(hex, "0123456789abcdefABCDEF")] != '\0') - return -1; - for(i=0; iattr), "user? dom?"); + a = addattr(a, "user? dom? proto=p9sk1"); + if((k = keyfetch(c, "%A", a)) == nil) + goto out; + + /* p9sk1: read client challenge */ + if(c->proto == &p9sk1){ + if(convread(c, cchal, CHALLEN) < 0) + goto out; + } - k->priv = emalloc(DESKEYLEN); - if(s = _strfindattr(k->privattr, "!hex")){ - if(hexparse(s, k->priv, 7) < 0){ - free(k->priv); - k->priv = nil; - werrstr("malformed key data"); - return -1; - } - }else if(s = _strfindattr(k->privattr, "!password")){ - passtokey((char*)k->priv, s); - }else{ - werrstr("no key data"); - free(k->priv); - k->priv = nil; - return -1; + /* send ticket request */ + memset(&tr, 0, sizeof tr); + tr.type = AuthTreq; + strcpy(tr.authid, strfindattr(k->attr, "user")); + strcpy(tr.authdom, strfindattr(k->attr, "dom")); + memrandom(tr.chal, sizeof tr.chal); + convTR2M(&tr, buf); + if(convwrite(c, buf, TICKREQLEN) < 0) + goto out; + + /* p9sk2: use server challenge as client challenge */ + if(c->proto == &p9sk2) + memmove(cchal, tr.chal, sizeof tr.chal); + + /* read ticket+authenticator */ + if(convread(c, buf, TICKETLEN+AUTHENTLEN) < 0) + goto out; + + convM2T(buf, &t, k->priv); + if(t.num != AuthTs || memcmp(t.chal, tr.chal, CHALLEN) != 0){ + /* BUG badkey */ + werrstr("key mismatch with auth server"); + goto out; } - return replacekey(k, before); + + convM2A(buf+TICKETLEN, &au, t.key); + if(au.num != AuthAc || memcmp(au.chal, tr.chal, CHALLEN) != 0 || au.id != 0){ + werrstr("client lies through his teeth"); + goto out; + } + + /* send authenticator */ + au.num = AuthAs; + memmove(au.chal, cchal, CHALLEN); + convA2M(&au, buf, t.key); + if(convwrite(c, buf, AUTHENTLEN) < 0) + goto out; + + /* success */ + c->attr = addcap(c->attr, c->sysuser, &t); + flog("p9skserver success %A", c->attr); /* before adding secret! */ + des56to64((uchar*)t.key, secret); + c->attr = addattr(c->attr, "secret=%.8H", secret); + ret = 0; + +out: + if(ret < 0) + flog("p9skserver: %r"); + freeattr(a); + keyclose(k); + return ret; } -static void -p9skclosekey(Key *k) +int +_asgetticket(int fd, char *trbuf, char *tbuf) { - free(k->priv); + if(write(fd, trbuf, TICKREQLEN) < 0){ + close(fd); + return -1; + } + return _asrdresp(fd, tbuf, 2*TICKETLEN); } - static int -getastickets(State *s, char *trbuf, char *tbuf) +getastickets(Ticketreq *tr, char *buf) { - int asfd, rv; - char *dom; + int asfd; + int ret; - if((dom = _strfindattr(s->key->attr, "dom")) == nil){ - werrstr("auth key has no domain"); - return -1; - } - asfd = _authdial(nil, dom); - if(asfd < 0) + if((asfd = xioauthdial(nil, tr->authdom)) < 0) return -1; - rv = _asgetticket(asfd, trbuf, tbuf); - close(asfd); - return rv; + tr->type = AuthTreq; + convTR2M(tr, buf); + ret = xioasgetticket(asfd, buf, buf); + xioclose(asfd); + return ret; } static int -mkserverticket(State *s, char *tbuf) +mktickets(Ticketreq *tr, char *buf, Key *k) { - Ticketreq *tr = &s->tr; Ticket t; if(strcmp(tr->authid, tr->hostid) != 0) return -1; -/* this keeps creating accounts on martha from working. -- presotto - if(strcmp(tr->uid, "none") == 0) - return -1; -*/ - memset(&t, 0, sizeof(t)); + + memset(&t, 0, sizeof t); memmove(t.chal, tr->chal, CHALLEN); strcpy(t.cuid, tr->uid); strcpy(t.suid, tr->uid); memrandom(t.key, DESKEYLEN); t.num = AuthTc; - convT2M(&t, tbuf, s->key->priv); + convT2M(&t, buf, k->priv); t.num = AuthTs; - convT2M(&t, tbuf+TICKETLEN, s->key->priv); + convT2M(&t, buf+TICKETLEN, k->priv); return 0; } static int -gettickets(State *s, char *trbuf, char *tbuf) +gettickets(Ticketreq *tr, char *buf, Key *k) { -/* - if(mktickets(s, trbuf, tbuf) >= 0) + if(getastickets(tr, buf) == 0) return 0; -*/ - if(getastickets(s, trbuf, tbuf) >= 0) + if(mktickets(tr, buf, k) == 0) return 0; - return mkserverticket(s, tbuf); + werrstr("gettickets: %r"); + return -1; } +static int +p9sk1check(Key *k) +{ + char *user, *dom, *pass; + Ticketreq tr; + + user = strfindattr(k->attr, "user"); + dom = strfindattr(k->attr, "dom"); + if(user==nil || dom==nil){ + werrstr("need user and dom attributes"); + return -1; + } + if(strlen(user) >= sizeof tr.authid){ + werrstr("user name too long"); + return -1; + } + if(strlen(dom) >= sizeof tr.authdom){ + werrstr("auth dom name too long"); + return -1; + } + + k->priv = emalloc(DESKEYLEN); + if(pass = strfindattr(k->privattr, "!hex")){ + if(hexparse(pass, k->priv, 7) < 0){ + werrstr("malformed !hex key data"); + return -1; + } + }else if(pass = strfindattr(k->privattr, "!password")) + passtokey(k->priv, pass); + else{ + werrstr("need !password or !hex attribute"); + return -1; + } + + return 0; +} + +static void +p9sk1close(Key *k) +{ + free(k->priv); + k->priv = nil; +} + +static Role +p9sk1roles[] = +{ + "client", p9skclient, + "server", p9skserver, + 0 +}; + +static Role +p9sk2roles[] = +{ + "client", p9skclient, + "server", p9skserver, + 0 +}; + Proto p9sk1 = { -.name= "p9sk1", -.init= p9skinit, -.write= p9skwrite, -.read= p9skread, -.close= p9skclose, -.addkey= p9skaddkey, -.closekey= p9skclosekey, -.keyprompt= "user? !password?" + "p9sk1", + p9sk1roles, + "user? dom? !password?", + p9sk1check, + p9sk1close }; Proto p9sk2 = { -.name= "p9sk2", -.init= p9skinit, -.write= p9skwrite, -.read= p9skread, -.close= p9skclose, + "p9sk2", + p9sk2roles }; diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/pass.c /sys/src/cmd/auth/factotum/pass.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/pass.c Sun Feb 6 16:08:24 2005 +++ /sys/src/cmd/auth/factotum/pass.c Tue Jan 1 00:00:00 2013 @@ -2,98 +2,40 @@ * This is just a repository for a password. * We don't want to encourage this, there's * no server side. + * + * Client: + * start proto=pass ... + * read password */ +#include "std.h" #include "dat.h" -typedef struct State State; -struct State -{ - Key *key; -}; - -enum -{ - HavePass, - Maxphase, -}; - -static char *phasenames[Maxphase] = -{ -[HavePass] "HavePass", -}; - static int -passinit(Proto *p, Fsstate *fss) +passproto(Conv *c) { - int ret; Key *k; - Keyinfo ki; - State *s; - ret = findkey(&k, mkkeyinfo(&ki, fss, nil), "%s", p->keyprompt); - if(ret != RpcOk) - return ret; - setattrs(fss->attr, k->attr); - s = emalloc(sizeof(*s)); - s->key = k; - fss->ps = s; - fss->phase = HavePass; - return RpcOk; + k = keyfetch(c, "%A", c->attr); + if(k == nil) + return -1; + c->state = "write"; + convprint(c, "%q %q", + strfindattr(k->attr, "user"), + strfindattr(k->privattr, "!password")); + return 0; } -static void -passclose(Fsstate *fss) -{ - State *s; - - s = fss->ps; - if(s->key) - closekey(s->key); - free(s); -} - -static int -passread(Fsstate *fss, void *va, uint *n) -{ - int m; - char buf[500]; - char *pass, *user; - State *s; - - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "read"); - - case HavePass: - user = _strfindattr(s->key->attr, "user"); - pass = _strfindattr(s->key->privattr, "!password"); - if(user==nil || pass==nil) - return failure(fss, "passread cannot happen"); - snprint(buf, sizeof buf, "%q %q", user, pass); - m = strlen(buf); - if(m > *n) - return toosmall(fss, m); - *n = m; - memmove(va, buf, m); - return RpcOk; - } -} - -static int -passwrite(Fsstate *fss, void*, uint) -{ - return phaseerror(fss, "write"); -} +static Role passroles[] = { + "client", passproto, + 0 +}; Proto pass = { -.name= "pass", -.init= passinit, -.write= passwrite, -.read= passread, -.close= passclose, -.addkey= replacekey, -.keyprompt= "user? !password?", + "pass", + passroles, + "user? !password?", + nil, + nil }; diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/pkcs1.c /sys/src/cmd/auth/factotum/pkcs1.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/pkcs1.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/pkcs1.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,230 @@ +#include "std.h" +#include "dat.h" + +/* + * PKCS #1 v2.0 signatures (aka RSASSA-PKCS1-V1_5) + * + * You don't want to read the spec. + * Here is what you need to know. + * + * RSA sign (aka RSASP1) is just an RSA encryption. + * RSA verify (aka RSAVP1) is just an RSA decryption. + * + * We sign hashes of messages instead of the messages + * themselves. + * + * The hashes are encoded in ASN.1 DER to identify + * the signature type, and then prefixed with 0x01 PAD 0x00 + * where PAD is as many 0xFF bytes as desired. + */ + +static int mkasn1(uchar *asn1, DigestAlg *alg, uchar *d, uint dlen); + +int +rsasign(RSApriv *key, DigestAlg *hash, uchar *digest, uint dlen, + uchar *sig, uint siglen) +{ + uchar asn1[64], *buf; + int n, len, pad; + mpint *m, *s; + + /* + * Create ASN.1 + */ + n = mkasn1(asn1, hash, digest, dlen); + + /* + * Create number to sign. + */ + len = (mpsignif(key->pub.n)+7)/8 - 1; + if(len < n+2){ + werrstr("rsa key too short"); + return -1; + } + pad = len - (n+2); + if(siglen < len){ + werrstr("signature buffer too short"); + return -1; + } + buf = malloc(len); + if(buf == nil) + return -1; + buf[0] = 0x01; + memset(buf+1, 0xFF, pad); + buf[1+pad] = 0x00; + memmove(buf+1+pad+1, asn1, n); + m = betomp(buf, len, nil); + free(buf); + if(m == nil) + return -1; + + /* + * Sign it. + */ + s = rsadecrypt(key, m, nil); + mpfree(m); + if(s == nil) + return -1; + mptoberjust(s, sig, len+1); + mpfree(s); + return len+1; +} + +int +rsaverify(RSApub *key, DigestAlg *hash, uchar *digest, uint dlen, + uchar *sig, uint siglen) +{ + uchar asn1[512], *buf; + int n, len, pad; + mpint *m, *mm, *s; + + /* + * Create ASN.1 + */ + n = mkasn1(asn1, hash, digest, dlen); + + /* + * Create number to sign. + */ + len = (mpsignif(key->n)+7)/8 - 1; + if(len < n+2){ + werrstr("rsa key too short"); + return -1; + } + pad = len - (n+2); + if(siglen < len){ + werrstr("signature buffer too short"); + return -1; + } + buf = malloc(len); + if(buf == nil) + return -1; + buf[0] = 0x01; + memset(buf+1, 0xFF, pad); + buf[1+pad] = 0x00; + memmove(buf+1+pad+1, asn1, n); + m = betomp(buf, len, nil); + free(buf); + if(m == nil) + return -1; + + /* + * Extract plaintext of signature. + */ + s = betomp(sig, siglen, nil); + if(s == nil) + return -1; + mm = rsaencrypt(key, s, nil); + mpfree(s); + if(mm == nil) + return -1; + if(mpcmp(m, mm) != 0){ + werrstr("signature did not verify"); + mpfree(mm); + mpfree(m); + return -1; + } + mpfree(mm); + mpfree(m); + return 0; +} + +/* + * Mptobe but shift right to fill buffer. + */ +void +mptoberjust(mpint *b, uchar *buf, uint len) +{ + int n; + + n = mptobe(b, buf, len, nil); + assert(n >= 0); + if(n < len){ + len -= n; + memmove(buf+len, buf, n); + memset(buf, 0, len); + } +} + +/* + * Simple ASN.1 encodings. + * Lengths < 128 are encoded as 1-bytes constants, + * making our life easy. + */ + +/* + * Hash OIDs + * + * SHA1 = 1.3.14.3.2.26 + * MDx = 1.2.840.113549.2.x + */ +#define O0(a,b) ((a)*40+(b)) +#define O2(x) \ + (((x)>>7)&0x7F)|0x80, \ + ((x)&0x7F) +#define O3(x) \ + (((x)>>14)&0x7F)|0x80, \ + (((x)>>7)&0x7F)|0x80, \ + ((x)&0x7F) +uchar oidsha1[] = { O0(1, 3), 14, 3, 2, 26 }; +uchar oidmd2[] = { O0(1, 2), O2(840), O3(113549), 2, 2 }; +uchar oidmd5[] = { O0(1, 2), O2(840), O3(113549), 2, 5 }; + +/* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm AlgorithmIdentifier, + * digest OCTET STRING + * } + * + * except that OpenSSL seems to sign + * + * DigestInfo ::= SEQUENCE { + * SEQUENCE{ digestAlgorithm AlgorithmIdentifier, NULL } + * digest OCTET STRING + * } + * + * instead. Sigh. + */ +static int +mkasn1(uchar *asn1, DigestAlg *alg, uchar *d, uint dlen) +{ + uchar *obj, *p; + uint olen; + + if(alg == sha1){ + obj = oidsha1; + olen = sizeof(oidsha1); + }else if(alg == md5){ + obj = oidmd5; + olen = sizeof(oidmd5); + }else{ + sysfatal("bad alg in mkasn1"); + return -1; + } + + p = asn1; + *p++ = 0x30; /* sequence */ + p++; + + *p++ = 0x30; /* another sequence */ + p++; + + *p++ = 0x06; /* object id */ + *p++ = olen; + memmove(p, obj, olen); + p += olen; + + *p++ = 0x05; /* null */ + *p++ = 0; + + asn1[3] = p - (asn1+4); /* end of inner sequence */ + + *p++ = 0x04; /* octet string */ + *p++ = dlen; + memmove(p, d, dlen); + p += dlen; + + asn1[1] = p - (asn1+2); /* end of outer sequence */ + return p-asn1; +} + diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/proto.c /sys/src/cmd/auth/factotum/proto.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/proto.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/proto.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,44 @@ +#include "std.h" +#include "dat.h" + +extern Proto apop; /* apop.c */ +extern Proto chap; /* chap.c */ +extern Proto cram; /* apop.c */ +extern Proto dsa; /* dsa.c */ +extern Proto httpdigest; /* httpdigest.c */ +extern Proto mschap; /* chap.c */ +extern Proto p9any; /* p9any.c */ +extern Proto p9sk1; /* p9sk1.c */ +extern Proto p9sk2; /* p9sk2.c */ +extern Proto p9cr; +extern Proto pass; /* pass.c */ +extern Proto rsa; /* rsa.c */ +extern Proto vnc; /* p9cr.c */ + +Proto *prototab[] = { + &apop, + &chap, + &cram, + &dsa, + &httpdigest, + &mschap, + &p9any, + &p9cr, + &p9sk1, + &p9sk2, + &pass, + &rsa, + &vnc, + nil +}; + +Proto* +protolookup(char *name) +{ + int i; + + for(i=0; prototab[i]; i++) + if(strcmp(prototab[i]->name, name) == 0) + return prototab[i]; + return nil; +} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/rpc.c /sys/src/cmd/auth/factotum/rpc.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/rpc.c Sat Feb 5 20:03:14 2005 +++ /sys/src/cmd/auth/factotum/rpc.c Tue Jan 1 00:00:00 2013 @@ -1,3 +1,6 @@ +#include "std.h" +#include "dat.h" + /* * Factotum RPC * @@ -14,12 +17,6 @@ * Only authentication protocol messages go here. Configuration * is still via ctl (below). * - * Return values are: - * error message - an error happened. - * ok [data] - success, possible data is request dependent. - * needkey proto domain user - request aborted, get me this key and try again - * badkey proto domain user - request aborted, this key might be bad - * done [haveai] - authentication is done [haveai: you can get an ai with authinfo] * Request RPCs are: * start attrs - initializes protocol for authentication, can fail. * returns "ok read" or "ok write" on success. @@ -27,482 +24,327 @@ * write - execute protocol write * authinfo - if the protocol is finished, return the AI if any * attr - return protocol information + * Return values are: + * error message - an error happened. + * ok [data] - success, possible data is request dependent. + * needkey attrs - request aborted, get me this key and try again + * badkey attrs - request aborted, this key might be bad + * done [haveai] - authentication is done [haveai: you can get an ai with authinfo] */ -#include "dat.h" - -Req *rpcwait; - -typedef struct Verb Verb; -struct Verb { - char *verb; - int iverb; -}; - -enum { - Vunknown = -1, - Vauthinfo = 0, - Vread, - Vstart, - Vwrite, - Vattr, -}; - -Verb rpctab[] = { - "authinfo", Vauthinfo, - "read", Vread, - "start", Vstart, - "write", Vwrite, - "attr", Vattr, +char *rpcname[] = +{ + "unknown", + "authinfo", + "attr", + "read", + "start", + "write", + "readhex", + "writehex" }; static int -classify(char *s, Verb *verbtab, int nverbtab) +classify(char *s) { int i; - for(i=0; iifcall.count >= Maxrpc){ - respond(r, Etoolarge); - return; - } - fss = r->fid->aux; - if(fss->pending){ - respond(r, "rpc already pending; read to clear"); - return; + if(count >= MaxRpc){ + werrstr("rpc too large"); + return -1; } - memmove(fss->rpc.buf, r->ifcall.data, r->ifcall.count); - fss->rpc.buf[r->ifcall.count] = '\0'; - fss->rpc.verb = fss->rpc.buf; - if(fss->rpc.arg = strchr(fss->rpc.buf, ' ')){ - *fss->rpc.arg++ = '\0'; - fss->rpc.narg = r->ifcall.count - (fss->rpc.arg - fss->rpc.buf); + + /* cancel any current rpc */ + c->rpc.op = RpcUnknown; + c->nreply = 0; + + /* parse new rpc */ + memmove(c->rpcbuf, data, count); + c->rpcbuf[count] = 0; + if(p = (uchar*)strchr((char*)c->rpcbuf, ' ')){ + *p++ = '\0'; + c->rpc.data = p; + c->rpc.count = count - (p - (uchar*)c->rpcbuf); }else{ - fss->rpc.arg = ""; - fss->rpc.narg = 0; + c->rpc.data = ""; + c->rpc.count = 0; + } + op = classify(c->rpcbuf); + if(op == RpcUnknown){ + werrstr("bad rpc verb: %s", c->rpcbuf); + return -1; } - fss->rpc.iverb = classify(fss->rpc.verb, rpctab, nelem(rpctab)); - r->ofcall.count = r->ifcall.count; - fss->pending = 1; - respond(r, nil); -} - -static void -retstring(Req *r, Fsstate *fss, char *s) -{ - int n; - n = strlen(s); - if(n > r->ifcall.count) - n = r->ifcall.count; - memmove(r->ofcall.data, s, n); - r->ofcall.count = n; - fss->pending = 0; - respond(r, nil); - return; + c->rpc.op = op; + return 0; } -static void -retrpc(Req *r, int ret, Fsstate *fss) +void +convthread(void *v) { - switch(ret){ - default: - snprint(fss->rpc.buf, Maxrpc, "internal error %d", ret); - retstring(r, fss, fss->rpc.buf); - return; - case RpcErrstr: - snprint(fss->rpc.buf, Maxrpc, "error %r"); - retstring(r, fss, fss->rpc.buf); - return; - case RpcFailure: - snprint(fss->rpc.buf, Maxrpc, "error %s", fss->err); - retstring(r, fss, fss->rpc.buf); - return; - case RpcNeedkey: - if(needkeyqueue(r, fss) < 0){ - snprint(fss->rpc.buf, Maxrpc, "needkey %s", fss->keyinfo); - retstring(r, fss, fss->rpc.buf); - } - return; - case RpcOk: - retstring(r, fss, "ok"); - return; - case RpcToosmall: - snprint(fss->rpc.buf, Maxrpc, "toosmall %d", fss->rpc.nwant); - retstring(r, fss, fss->rpc.buf); - return; - case RpcPhase: - snprint(fss->rpc.buf, Maxrpc, "phase %r"); - retstring(r, fss, fss->rpc.buf); - return; - case RpcConfirm: - confirmqueue(r, fss); - return; + char *role, *proto; + Conv *c; + Attr *a; + Proto *p; + Role *r; + + c = v; + a = parseattr(c->rpc.data); + if(a == nil){ + werrstr("empty attr"); + goto out; + } + c->attr = a; + proto = strfindattr(a, "proto"); + if(proto == nil){ + werrstr("no proto in attrs"); + goto out; + } + p = protolookup(proto); + if(p == nil){ + werrstr("unknown proto %s", proto); + goto out; } -} + c->proto = p; -int -rdwrcheck(Req *r, Fsstate *fss) -{ - if(fss->ps == nil){ - retstring(r, fss, "error no current protocol"); - return -1; - } - if(fss->phase == Notstarted){ - retstring(r, fss, "protocol not started"); - return -1; - } - if(fss->phase == Broken){ - snprint(fss->rpc.buf, Maxrpc, "error %s", fss->err); - retstring(r, fss, fss->rpc.buf); - return -1; - } - if(fss->phase == Established){ - if(fss->haveai) - retstring(r, fss, "done haveai"); - else - retstring(r, fss, "done"); - return -1; + role = strfindattr(a, "role"); + /* + * defeat russ' fix to keys being used as server keys + * when ment only as client auth so as not to break + * most keys on the system. + */ + if(role==nil && 1 /* accept traditional keys */) + if(strcmp(proto, "pass") == 0) + role = "client"; + if(role == nil){ + werrstr("no role in attrs"); + goto out; + } + for(r=p->roles; r->name; r++){ + if(strcmp(r->name, role) != 0) + continue; + rpcrespond(c, "ok"); + c->active = 1; + if((*r->fn)(c) == 0){ + c->done = 1; + werrstr("protocol finished"); + }else + werrstr("%s %s %s: %r", p->name, r->name, c->state); + goto out; } - return 0; + werrstr("unknown role"); + +out: + c->active = 0; + c->state = 0; + rerrstr(c->err, sizeof c->err); + rpcrespond(c, "error %r"); + convclose(c); } -static void -logret(char *pre, Fsstate *fss, int ret) +static uchar* convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex); + +void +rpcexec(Conv *c) { - switch(ret){ + uchar *p; + + c->rpc.hex = 0; + switch(c->rpc.op){ + case RpcWriteHex: + c->rpc.op = RpcWrite; + if(dec16(c->rpc.data, c->rpc.count, c->rpc.data, c->rpc.count) != c->rpc.count/2){ + rpcrespond(c, "bad hex"); + break; + } + c->rpc.count /= 2; + goto Default; + case RpcReadHex: + c->rpc.hex = 1; + c->rpc.op = RpcRead; + /* fall through */ + case RpcRead: + if(c->rpc.count > 0){ + rpcrespond(c, "error read takes no parameters"); + break; + } + /* fall through */ default: - flog("%s: code %d", pre, ret); - break; - case RpcErrstr: - flog("%s: error %r", pre); - break; - case RpcFailure: - flog("%s: failure %s", pre, fss->err); - break; - case RpcNeedkey: - flog("%s: needkey %s", pre, fss->keyinfo); + Default: + if(!c->active){ + if(c->done) + rpcrespond(c, "done"); + else + rpcrespond(c, "error %s", c->err); + break; + } + nbsendp(c->rpcwait, 0); break; - case RpcOk: - flog("%s: ok", pre); + case RpcUnknown: break; - case RpcToosmall: - flog("%s: toosmall %d", pre, fss->rpc.nwant); + case RpcAuthinfo: + /* deprecated */ + if(c->active) + rpcrespond(c, "error conversation still active"); + else if(!c->done) + rpcrespond(c, "error conversation not successful"); + else{ + /* make up an auth info using the attr */ + p = convAI2M((uchar*)c->reply+3, sizeof c->reply-3, + strfindattr(c->attr, "cuid"), + strfindattr(c->attr, "suid"), + strfindattr(c->attr, "cap"), + strfindattr(c->attr, "secret")); + if(p == nil) + rpcrespond(c, "error %r"); + else + rpcrespondn(c, "ok", c->reply+3, p-(uchar*)(c->reply+3)); + } break; - case RpcPhase: - flog("%s: phase: %r", pre); + case RpcAttr: + rpcrespond(c, "ok %A", c->attr); break; - case RpcConfirm: - flog("%s: waiting for confirm", pre); + case RpcStart: + convreset(c); + c->ref++; + threadcreate(convthread, c, STACK); break; } } void -rpcrdwrlog(Fsstate *fss, char *rdwr, uint n, int ophase, int ret) +rpcrespond(Conv *c, char *fmt, ...) { - char buf0[40], buf1[40], pre[300]; + va_list arg; - if(!debug) + if(c->hangup) return; - snprint(pre, sizeof pre, "%d: %s %ud in phase %s yields phase %s", - fss->seqnum, rdwr, n, phasename(fss, ophase, buf0), phasename(fss, fss->phase, buf1)); - logret(pre, fss, ret); -} -void -rpcstartlog(Attr *attr, Fsstate *fss, int ret) -{ - char pre[300], tmp[40]; + if(fmt == nil) + fmt = ""; - if(!debug) - return; - snprint(pre, sizeof pre, "%d: start %A yields phase %s", fss->seqnum, - attr, phasename(fss, fss->phase, tmp)); - logret(pre, fss, ret); + va_start(arg, fmt); + c->nreply = vsnprint(c->reply, sizeof c->reply, fmt, arg); + va_end(arg); + (*c->kickreply)(c); + c->rpc.op = RpcUnknown; } -int seqnum; - void -rpcread(Req *r) +rpcrespondn(Conv *c, char *verb, void *data, int count) { - Attr *attr; char *p; - int ophase, ret; - uchar *e; - uint count; - Fsstate *fss; - Proto *proto; + int need, hex; - if(r->ifcall.count < 64){ - respond(r, "rpc read too small"); + if(c->hangup) return; + + need = strlen(verb)+1+count; + hex = 0; + if(c->rpc.hex && strcmp(verb, "ok") == 0){ + need += count; + hex = 1; } - fss = r->fid->aux; - if(!fss->pending){ - respond(r, "no rpc pending"); + if(need > sizeof c->reply){ + print("RPC response too large; caller %#p", getcallerpc(&c)); return; } - switch(fss->rpc.iverb){ - default: - case Vunknown: - retstring(r, fss, "error unknown verb"); - break; - - case Vstart: - if(fss->phase != Notstarted){ - flog("%d: implicit close due to second start; old attr '%A'", fss->seqnum, fss->attr); - if(fss->proto && fss->ps) - (*fss->proto->close)(fss); - fss->ps = nil; - fss->proto = nil; - _freeattr(fss->attr); - fss->attr = nil; - fss->phase = Notstarted; - } - attr = _parseattr(fss->rpc.arg); - if((p = _strfindattr(attr, "proto")) == nil){ - retstring(r, fss, "error did not specify proto"); - _freeattr(attr); - break; - } - if((proto = findproto(p)) == nil){ - snprint(fss->rpc.buf, Maxrpc, "error unknown protocol %q", p); - retstring(r, fss, fss->rpc.buf); - _freeattr(attr); - break; - } - fss->attr = attr; - fss->proto = proto; - fss->seqnum = ++seqnum; - ret = (*proto->init)(proto, fss); - rpcstartlog(attr, fss, ret); - if(ret != RpcOk){ - _freeattr(fss->attr); - fss->attr = nil; - fss->phase = Notstarted; - } - retrpc(r, ret, fss); - break; - - case Vread: - if(fss->rpc.arg && fss->rpc.arg[0]){ - retstring(r, fss, "error read needs no parameters"); - break; - } - if(rdwrcheck(r, fss) < 0) - break; - count = r->ifcall.count - 3; - ophase = fss->phase; - ret = fss->proto->read(fss, (uchar*)r->ofcall.data+3, &count); - rpcrdwrlog(fss, "read", count, ophase, ret); - if(ret == RpcOk){ - memmove(r->ofcall.data, "ok ", 3); - if(count == 0) - r->ofcall.count = 2; - else - r->ofcall.count = 3+count; - fss->pending = 0; - respond(r, nil); - }else - retrpc(r, ret, fss); - break; - - case Vwrite: - if(rdwrcheck(r, fss) < 0) - break; - ophase = fss->phase; - ret = fss->proto->write(fss, fss->rpc.arg, fss->rpc.narg); - rpcrdwrlog(fss, "write", fss->rpc.narg, ophase, ret); - retrpc(r, ret, fss); - break; - - case Vauthinfo: - if(fss->phase != Established){ - retstring(r, fss, "error authentication unfinished"); - break; - } - if(!fss->haveai){ - retstring(r, fss, "error no authinfo available"); - break; - } - memmove(r->ofcall.data, "ok ", 3); - fss->ai.cap = mkcap(r->fid->uid, fss->ai.suid); - e = convAI2M(&fss->ai, (uchar*)r->ofcall.data+3, r->ifcall.count-3); - free(fss->ai.cap); - fss->ai.cap = nil; - if(e == nil){ - retstring(r, fss, "error read too small"); - break; - } - r->ofcall.count = e - (uchar*)r->ofcall.data; - fss->pending = 0; - respond(r, nil); - break; - case Vattr: - snprint(fss->rpc.buf, Maxrpc, "ok %A", fss->attr); - retstring(r, fss, fss->rpc.buf); - break; + strcpy(c->reply, verb); + p = c->reply + strlen(c->reply); + *p++ = ' '; + if(hex){ + enc16(p, 2*count+1, data, count); + p += 2*count; + }else{ + memmove(p, data, count); + p += count; } + c->nreply = p - c->reply; + (*c->kickreply)(c); + c->rpc.op = RpcUnknown; } -enum { - Vdelkey, - Vaddkey, - Vdebug, -}; - -Verb ctltab[] = { - "delkey", Vdelkey, - "key", Vaddkey, - "debug", Vdebug, -}; - -/* - * key attr=val... - add a key - * the attr=val pairs are protocol-specific. - * for example, both of these are valid: - * key p9sk1 gre cs.bell-labs.com mysecret - * key p9sk1 gre cs.bell-labs.com 11223344556677 fmt=des7hex - * delkey ... - delete a key - * if given, the attr=val pairs are used to narrow the search - * [maybe should require a password?] - */ - -int -ctlwrite(char *a, int atzero) +/* deprecated */ +static uchar* +pstring(uchar *p, uchar *e, char *s) { - char *p; - int i, nmatch, ret; - Attr *attr, **l, **lpriv, **lprotos, *pa, *priv, *protos; - Key *k; - Proto *proto; - - if(a[0] == '#' || a[0] == '\0') - return 0; - - /* - * it would be nice to emit a warning of some sort here. - * we ignore all but the first line of the write. this helps - * both with things like "echo delkey >/mnt/factotum/ctl" - * and writes that (incorrectly) contain multiple key lines. - */ - if(p = strchr(a, '\n')){ - if(p[1] != '\0'){ - werrstr("multiline write not allowed"); - return -1; - } - *p = '\0'; - } + uint n; - if((p = strchr(a, ' ')) == nil) - p = ""; - else - *p++ = '\0'; - switch(classify(a, ctltab, nelem(ctltab))){ - default: - case Vunknown: - werrstr("unknown verb"); - return -1; - case Vdebug: - debug ^= 1; - return 0; - case Vdelkey: - nmatch = 0; - attr = _parseattr(p); - for(pa=attr; pa; pa=pa->next){ - if(pa->type != AttrQuery && pa->name[0]=='!'){ - werrstr("only !private? patterns are allowed for private fields"); - _freeattr(attr); - return -1; - } - } - for(i=0; inkey; ){ - if(matchattr(attr, ring->key[i]->attr, ring->key[i]->privattr)){ - nmatch++; - closekey(ring->key[i]); - ring->nkey--; - memmove(&ring->key[i], &ring->key[i+1], (ring->nkey-i)*sizeof(ring->key[0])); - }else - i++; - } - _freeattr(attr); - if(nmatch == 0){ - werrstr("found no keys to delete"); - return -1; - } - return 0; - case Vaddkey: - attr = _parseattr(p); - /* separate out proto= attributes */ - lprotos = &protos; - for(l=&attr; (*l); ){ - if(strcmp((*l)->name, "proto") == 0){ - *lprotos = *l; - lprotos = &(*l)->next; - *l = (*l)->next; - }else - l = &(*l)->next; - } - *lprotos = nil; - if(protos == nil){ - werrstr("key without protos"); - _freeattr(attr); - return -1; - } + if(p == nil) + return nil; + if(s == nil) + s = ""; + n = strlen(s); + if(p+n+BIT16SZ >= e) + return nil; + PBIT16(p, n); + p += BIT16SZ; + memmove(p, s, n); + p += n; + return p; +} - /* separate out private attributes */ - lpriv = &priv; - for(l=&attr; (*l); ){ - if((*l)->name[0] == '!'){ - *lpriv = *l; - lpriv = &(*l)->next; - *l = (*l)->next; - }else - l = &(*l)->next; - } - *lpriv = nil; +static uchar* +pcarray(uchar *p, uchar *e, uchar *s, uint n) +{ + if(p == nil) + return nil; + if(s == nil){ + if(n > 0) + sysfatal("pcarray"); + s = (uchar*)""; + } + if(p+n+BIT16SZ >= e) + return nil; + PBIT16(p, n); + p += BIT16SZ; + memmove(p, s, n); + p += n; + return p; +} - /* add keys */ - ret = 0; - for(pa=protos; pa; pa=pa->next){ - if((proto = findproto(pa->val)) == nil){ - werrstr("unknown proto %s", pa->val); - ret = -1; - continue; - } - if(proto->addkey == nil){ - werrstr("proto %s doesn't take keys", proto->name); - ret = -1; - continue; - } - k = emalloc(sizeof(Key)); - k->attr = _mkattr(AttrNameval, "proto", proto->name, _copyattr(attr)); - k->privattr = _copyattr(priv); - k->ref = 1; - k->proto = proto; - if(proto->addkey(k, atzero) < 0){ - ret = -1; - closekey(k); - continue; - } - closekey(k); - } - _freeattr(attr); - _freeattr(priv); - _freeattr(protos); - return ret; - } +static uchar* +convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex) +{ + uchar *e = p+n; + uchar *secret; + int nsecret; + + if(cuid == nil) + cuid = ""; + if(suid == nil) + suid = ""; + if(cap == nil) + cap = ""; + if(hex == nil) + hex = ""; + nsecret = strlen(hex)/2; + secret = emalloc(nsecret); + if(hexparse(hex, secret, nsecret) < 0){ + werrstr("hexparse %s failed", hex); /* can't happen */ + free(secret); + return nil; + } + p = pstring(p, e, cuid); + p = pstring(p, e, suid); + p = pstring(p, e, cap); + p = pcarray(p, e, secret, nsecret); + free(secret); + if(p == nil) + werrstr("authinfo too big"); + return p; } + diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/rsa.c /sys/src/cmd/auth/factotum/rsa.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/rsa.c Wed Apr 4 20:12:09 2012 +++ /sys/src/cmd/auth/factotum/rsa.c Tue Jan 1 00:00:00 2013 @@ -1,58 +1,236 @@ +#include "std.h" +#include "dat.h" + /* * RSA authentication. - * - * Old ssh client protocol: - * read public key - * if you don't like it, read another, repeat - * write challenge - * read response - * - * all numbers are hexadecimal biginits parsable with strtomp. + * + * Encrypt/Decrypt: + * start n=xxx ek=xxx + * write msg + * read encrypt/decrypt(msg) * * Sign (PKCS #1 using hash=sha1 or hash=md5) + * start n=xxx ek=xxx * write hash(msg) * read signature(hash(msg)) - * + * * Verify: + * start n=xxx ek=xxx * write hash(msg) * write signature(hash(msg)) * read ok or fail + * + * all numbers are hexadecimal biginits parsable with strtomp. + * must be lower case for attribute matching in start. */ -#include "dat.h" +static int +convwritemp(Conv *c, mpint *m) +{ + char *s; + int n; -enum { - CHavePub, - CHaveResp, - VNeedHash, - VNeedSig, - VHaveResp, - SNeedHash, - SHaveResp, - Maxphase, -}; + s = smprint("%B", m); + n = convwrite(c, s, strlen(s)); + free(s); + return n; +} -static char *phasenames[] = { -[CHavePub] "CHavePub", -[CHaveResp] "CHaveResp", -[VNeedHash] "VNeedHash", -[VNeedSig] "VNeedSig", -[VHaveResp] "VHaveResp", -[SNeedHash] "SNeedHash", -[SHaveResp] "SHaveResp", -}; +static int +xrsaclient(Conv *c) +{ + char *s; + int n, ret; + mpint *m; + Key *k; + RSApriv *key; + + flog("rsa client: warning depricated ssh1 client"); + ret = -1; + m = nil; + + /* fetch key */ + c->state = "keylookup"; + k = keylookup("%A", c->attr); + if(k == nil) + goto out; + key = k->priv; + + /* send response */ + c->state = "write pub"; + convwritemp(c, key->pub.n); + + c->state = "read chal"; + n = convreadm(c, &s); + if(n == -1) + goto out; + m = strtomp(s, nil, 16, nil); + if(m == nil){ + werrstr("invalid challenge value"); + goto out; + } + m = rsadecrypt(key, m, m); + + c->state = "established"; + convwritemp(c, m); + ret = 0; + +out: + keyclose(k); + mpfree(m); + return ret; +} -struct State +static int +xrsadecrypt(Conv *c) { - RSApriv *priv; - mpint *resp; - int off; - Key *key; - mpint *digest; - int sigresp; -}; + char *txt, *be, *role; + int n, ret; + mpint *m, *mm; + Key *k; + RSApriv *key; + + ret = -1; + txt = nil; + m = nil; + mm = nil; + be = nil; + + /* fetch key */ + c->state = "keylookup"; + k = keylookup("%A", c->attr); + if(k == nil) + goto out; + key = k->priv; + + /* make sure have private half if needed */ + role = strfindattr(c->attr, "role"); + if(strcmp(role, "decrypt") == 0 && !key->c2){ + werrstr("missing private half of key -- cannot decrypt"); + goto out; + } + + /* read text */ + c->state = "read"; + if((n=convreadm(c, &txt)) < 0) + goto out; + if(n < 32){ + convprint(c, "data too short"); + goto out; + } + + /* encrypt/decrypt */ + m = betomp((uchar*)txt, n, nil); + if(m == nil) + goto out; + if(strcmp(role, "decrypt") == 0) + mm = rsadecrypt(key, m, nil); + else + mm = rsaencrypt(&key->pub, m, nil); + if(mm == nil) + goto out; + be = malloc(4096); + n = mptobe(mm, (uchar*)be, 4096, nil); + + /* send response */ + c->state = "write"; + convwrite(c, be, n); + ret = 0; + +out: + free(be); + mpfree(m); + mpfree(mm); + keyclose(k); + free(txt); + return ret; +} + +static int +xrsasign(Conv *c) +{ + char *hash, *role; + int dlen, n, ret; + DigestAlg *hashfn; + Key *k; + RSApriv *key; + uchar sig[1024], digest[64]; + char *sig2; + + ret = -1; + + /* fetch key */ + c->state = "keylookup"; + k = keylookup("%A", c->attr); + if(k == nil) + goto out; + + /* make sure have private half if needed */ + key = k->priv; + role = strfindattr(c->attr, "role"); + if(strcmp(role, "sign") == 0 && !key->c2){ + werrstr("missing private half of key -- cannot sign"); + goto out; + } + + /* get hash type from key */ + hash = strfindattr(k->attr, "hash"); + if(hash == nil) + hash = "sha1"; + if(strcmp(hash, "sha1") == 0){ + hashfn = sha1; + dlen = SHA1dlen; + }else if(strcmp(hash, "md5") == 0){ + hashfn = md5; + dlen = MD5dlen; + }else{ + werrstr("unknown hash function %s", hash); + goto out; + } + + /* read hash */ + c->state = "read hash"; + if(convread(c, digest, dlen) < 0) + goto out; + + if(strcmp(role, "sign") == 0){ + /* sign */ + if((n=rsasign(key, hashfn, digest, dlen, sig, sizeof sig)) < 0) + goto out; + + /* write */ + convwrite(c, sig, n); + }else{ + /* read signature */ + if((n = convreadm(c, &sig2)) < 0) + goto out; + + /* verify */ + if(rsaverify(&key->pub, hashfn, digest, dlen, (uchar*)sig2, n) == 0) + convprint(c, "ok"); + else + convprint(c, "signature does not verify"); + free(sig2); + } + ret = 0; + +out: + keyclose(k); + return ret; +} -static mpint* mkdigest(RSApub *key, char *hashalg, uchar *hash, uint dlen); +/* + * convert to canonical form (lower case) + * for use in attribute matches. + */ +static void +strlwr(char *a) +{ + for(; *a; a++){ + if('A' <= *a && *a <= 'Z') + *a += 'a' - 'A'; + } +} static RSApriv* readrsapriv(Key *k) @@ -62,24 +240,41 @@ priv = rsaprivalloc(); - if((a=_strfindattr(k->attr, "ek"))==nil || (priv->pub.ek=strtomp(a, nil, 16, nil))==nil) + if((a=strfindattr(k->attr, "ek"))==nil + || (priv->pub.ek=strtomp(a, nil, 16, nil))==nil) goto Error; - if((a=_strfindattr(k->attr, "n"))==nil || (priv->pub.n=strtomp(a, nil, 16, nil))==nil) + strlwr(a); + if((a=strfindattr(k->attr, "n"))==nil + || (priv->pub.n=strtomp(a, nil, 16, nil))==nil) goto Error; - if(k->privattr == nil) /* only public half */ + strlwr(a); + if(k->privattr == nil) /* only public half */ return priv; - if((a=_strfindattr(k->privattr, "!p"))==nil || (priv->p=strtomp(a, nil, 16, nil))==nil) + + if((a=strfindattr(k->privattr, "!p"))==nil + || (priv->p=strtomp(a, nil, 16, nil))==nil) goto Error; - if((a=_strfindattr(k->privattr, "!q"))==nil || (priv->q=strtomp(a, nil, 16, nil))==nil) + strlwr(a); + if((a=strfindattr(k->privattr, "!q"))==nil + || (priv->q=strtomp(a, nil, 16, nil))==nil) goto Error; - if((a=_strfindattr(k->privattr, "!kp"))==nil || (priv->kp=strtomp(a, nil, 16, nil))==nil) + strlwr(a); + if((a=strfindattr(k->privattr, "!kp"))==nil + || (priv->kp=strtomp(a, nil, 16, nil))==nil) goto Error; - if((a=_strfindattr(k->privattr, "!kq"))==nil || (priv->kq=strtomp(a, nil, 16, nil))==nil) + strlwr(a); + if((a=strfindattr(k->privattr, "!kq"))==nil + || (priv->kq=strtomp(a, nil, 16, nil))==nil) goto Error; - if((a=_strfindattr(k->privattr, "!c2"))==nil || (priv->c2=strtomp(a, nil, 16, nil))==nil) + strlwr(a); + if((a=strfindattr(k->privattr, "!c2"))==nil + || (priv->c2=strtomp(a, nil, 16, nil))==nil) goto Error; - if((a=_strfindattr(k->privattr, "!dk"))==nil || (priv->dk=strtomp(a, nil, 16, nil))==nil) + strlwr(a); + if((a=strfindattr(k->privattr, "!dk"))==nil + || (priv->dk=strtomp(a, nil, 16, nil))==nil) goto Error; + strlwr(a); return priv; Error: @@ -88,315 +283,44 @@ } static int -rsainit(Proto*, Fsstate *fss) -{ - Keyinfo ki; - State *s; - char *role; - - if((role = _strfindattr(fss->attr, "role")) == nil) - return failure(fss, "rsa role not specified"); - if(strcmp(role, "client") == 0) - fss->phase = CHavePub; - else if(strcmp(role, "sign") == 0) - fss->phase = SNeedHash; - else if(strcmp(role, "verify") == 0) - fss->phase = VNeedHash; - else - return failure(fss, "rsa role %s unimplemented", role); - - s = emalloc(sizeof *s); - fss->phasename = phasenames; - fss->maxphase = Maxphase; - fss->ps = s; - - switch(fss->phase){ - case SNeedHash: - case VNeedHash: - mkkeyinfo(&ki, fss, nil); - if(findkey(&s->key, &ki, nil) != RpcOk) - return failure(fss, nil); - /* signing needs private key */ - if(fss->phase == SNeedHash && s->key->privattr == nil) - return failure(fss, - "missing private half of key -- cannot sign"); - } - return RpcOk; -} - -static int -rsaread(Fsstate *fss, void *va, uint *n) -{ - RSApriv *priv; - State *s; - mpint *m; - Keyinfo ki; - int len, r; - - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "read"); - case CHavePub: - if(s->key){ - closekey(s->key); - s->key = nil; - } - mkkeyinfo(&ki, fss, nil); - ki.skip = s->off; - ki.noconf = 1; - if(findkey(&s->key, &ki, nil) != RpcOk) - return failure(fss, nil); - s->off++; - priv = s->key->priv; - *n = snprint(va, *n, "%B", priv->pub.n); - return RpcOk; - case CHaveResp: - *n = snprint(va, *n, "%B", s->resp); - fss->phase = Established; - return RpcOk; - case SHaveResp: - priv = s->key->priv; - len = (mpsignif(priv->pub.n)+7)/8; - if(len > *n) - return failure(fss, "signature buffer too short"); - m = rsadecrypt(priv, s->digest, nil); - r = mptobe(m, (uchar*)va, len, nil); - if(r < len){ - memmove((uchar*)va+len-r, va, r); - memset(va, 0, len-r); - } - *n = len; - mpfree(m); - fss->phase = Established; - return RpcOk; - case VHaveResp: - *n = snprint(va, *n, "%s", s->sigresp == 0? "ok": - "signature does not verify"); - fss->phase = Established; - return RpcOk; - } -} - -static int -rsawrite(Fsstate *fss, void *va, uint n) +rsacheck(Key *k) { - RSApriv *priv; - mpint *m, *mm; - State *s; - char *hash; - int dlen; - - s = fss->ps; - switch(fss->phase){ - default: - return phaseerror(fss, "write"); - case CHavePub: - if(s->key == nil) - return failure(fss, "no current key"); - switch(canusekey(fss, s->key)){ - case -1: - return RpcConfirm; - case 0: - return failure(fss, "confirmation denied"); - case 1: - break; - } - m = strtomp(va, nil, 16, nil); - if(m == nil) - return failure(fss, "invalid challenge value"); - m = rsadecrypt(s->key->priv, m, m); - s->resp = m; - fss->phase = CHaveResp; - return RpcOk; - case SNeedHash: - case VNeedHash: - /* get hash type from key */ - hash = _strfindattr(s->key->attr, "hash"); - if(hash == nil) - hash = "sha1"; - if(strcmp(hash, "sha1") == 0) - dlen = SHA1dlen; - else if(strcmp(hash, "md5") == 0) - dlen = MD5dlen; - else - return failure(fss, "unknown hash function %s", hash); - if(n != dlen) - return failure(fss, "hash length %d should be %d", - n, dlen); - priv = s->key->priv; - s->digest = mkdigest(&priv->pub, hash, (uchar *)va, n); - if(s->digest == nil) - return failure(fss, nil); - if(fss->phase == VNeedHash) - fss->phase = VNeedSig; - else - fss->phase = SHaveResp; - return RpcOk; - case VNeedSig: - priv = s->key->priv; - m = betomp((uchar*)va, n, nil); - mm = rsaencrypt(&priv->pub, m, nil); - s->sigresp = mpcmp(s->digest, mm); - mpfree(m); - mpfree(mm); - fss->phase = VHaveResp; - return RpcOk; + static int first = 1; + + if(first){ + fmtinstall('B', mpfmt); + first = 0; } -} - -static void -rsaclose(Fsstate *fss) -{ - State *s; - - s = fss->ps; - if(s->key) - closekey(s->key); - if(s->resp) - mpfree(s->resp); - if(s->digest) - mpfree(s->digest); - free(s); -} - -static int -rsaaddkey(Key *k, int before) -{ - fmtinstall('B', mpfmt); if((k->priv = readrsapriv(k)) == nil){ werrstr("malformed key data"); return -1; } - return replacekey(k, before); + return 0; } static void -rsaclosekey(Key *k) +rsaclose(Key *k) { rsaprivfree(k->priv); + k->priv = nil; } -Proto rsa = { -.name= "rsa", -.init= rsainit, -.write= rsawrite, -.read= rsaread, -.close= rsaclose, -.addkey= rsaaddkey, -.closekey= rsaclosekey, -}; - -/* - * Simple ASN.1 encodings. - * Lengths < 128 are encoded as 1-bytes constants, - * making our life easy. - */ - -/* - * Hash OIDs - * - * SHA1 = 1.3.14.3.2.26 - * MDx = 1.2.840.113549.2.x - */ -#define O0(a,b) ((a)*40+(b)) -#define O2(x) \ - (((x)>> 7)&0x7F)|0x80, \ - ((x)&0x7F) -#define O3(x) \ - (((x)>>14)&0x7F)|0x80, \ - (((x)>> 7)&0x7F)|0x80, \ - ((x)&0x7F) -uchar oidsha1[] = { O0(1, 3), 14, 3, 2, 26 }; -uchar oidmd2[] = { O0(1, 2), O2(840), O3(113549), 2, 2 }; -uchar oidmd5[] = { O0(1, 2), O2(840), O3(113549), 2, 5 }; - -/* - * DigestInfo ::= SEQUENCE { - * digestAlgorithm AlgorithmIdentifier, - * digest OCTET STRING - * } - * - * except that OpenSSL seems to sign - * - * DigestInfo ::= SEQUENCE { - * SEQUENCE{ digestAlgorithm AlgorithmIdentifier, NULL } - * digest OCTET STRING - * } - * - * instead. Sigh. - */ -static int -mkasn1(uchar *asn1, char *alg, uchar *d, uint dlen) -{ - uchar *obj, *p; - uint olen; - - if(strcmp(alg, "sha1") == 0){ - obj = oidsha1; - olen = sizeof(oidsha1); - }else if(strcmp(alg, "md5") == 0){ - obj = oidmd5; - olen = sizeof(oidmd5); - }else{ - sysfatal("bad alg in mkasn1"); - return -1; - } - - p = asn1; - *p++ = 0x30; /* sequence */ - p++; - - *p++ = 0x30; /* another sequence */ - p++; - - *p++ = 0x06; /* object id */ - *p++ = olen; - memmove(p, obj, olen); - p += olen; - - *p++ = 0x05; /* null */ - *p++ = 0; - - asn1[3] = p - (asn1+4); /* end of inner sequence */ - - *p++ = 0x04; /* octet string */ - *p++ = dlen; - memmove(p, d, dlen); - p += dlen; - - asn1[1] = p - (asn1+2); /* end of outer sequence */ - return p - asn1; -} - -static mpint* -mkdigest(RSApub *key, char *hashalg, uchar *hash, uint dlen) +static Role +rsaroles[] = { - mpint *m; - uchar asn1[512], *buf; - int len, n, pad; + "client", xrsaclient, /* old crap. support old ssh */ + "sign", xrsasign, + "verify", xrsasign, /* public operation */ + "decrypt", xrsadecrypt, + "encrypt", xrsadecrypt, /* public operation */ + 0 +}; - /* - * Create ASN.1 - */ - n = mkasn1(asn1, hashalg, hash, dlen); - - /* - * PKCS#1 padding - */ - len = (mpsignif(key->n)+7)/8 - 1; - if(len < n+2){ - werrstr("rsa key too short"); - return nil; - } - pad = len - (n+2); - buf = emalloc(len); - buf[0] = 0x01; - memset(buf+1, 0xFF, pad); - buf[1+pad] = 0x00; - memmove(buf+1+pad+1, asn1, n); - m = betomp(buf, len, nil); - free(buf); - return m; -} +Proto rsa = { + "rsa", + rsaroles, + nil, + rsacheck, + rsaclose +}; diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/secstore.c /sys/src/cmd/auth/factotum/secstore.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/secstore.c Sat Feb 18 14:29:01 2006 +++ /sys/src/cmd/auth/factotum/secstore.c Tue Jan 1 00:00:00 2013 @@ -3,18 +3,20 @@ * to download a file at boot time. */ +#include "std.h" #include "dat.h" #include enum{ CHK = 16}; enum{ MAXFILESIZE = 10*1024*1024 }; -enum{// PW status bits +enum{/* PW status bits */ Enabled = (1<<0), - STA = (1<<1), // extra SecurID step + STA = (1<<1) /* extra SecurID step */ }; static char testmess[] = "__secstore\tPAK\nC=%s\nm=0\n"; +char *secstore; int havesecstore(void) @@ -26,14 +28,20 @@ hnputs(buf, 0x8000+n-2); fd = secdial(); - if(fd < 0) + if(fd < 0){ + if(debug) + fprint(2, "factotum: secdial: %r\n"); + flog("secdial: %r"); return 0; + } if(write(fd, buf, n) != n || readn(fd, buf, 2) != 2){ + flog("secstore: no count"); close(fd); return 0; } n = ((buf[0]&0x7f)<<8) + buf[1]; if(n+1 > sizeof buf){ + flog("secstore: bad count"); werrstr("implausibly large count %d", n); close(fd); return 0; @@ -41,40 +49,47 @@ m = readn(fd, buf, n); close(fd); if(m != n){ + flog("secstore: unexpected eof"); if(m >= 0) werrstr("short read from secstore"); return 0; } buf[n] = 0; if(strcmp((char*)buf, "!account expired") == 0){ + flog("secstore: account expired"); werrstr("account expired"); return 0; } - return strcmp((char*)buf, "!account exists") == 0; + if(strcmp((char*)buf, "!account exists") == 0){ + flog("secstore: account exists"); + return 1; + } + flog("secstore: %s", (char*)buf); + return 0; } -// delimited, authenticated, encrypted connection -enum{ Maxmsg=4096 }; // messages > Maxmsg bytes are truncated +/* delimited, authenticated, encrypted connection */ +enum{ Maxmsg=4096 }; /* messages > Maxmsg bytes are truncated */ typedef struct SConn SConn; -extern SConn* newSConn(int); // arg is open file descriptor +extern SConn* newSConn(int); /* arg is open file descriptor */ struct SConn{ void *chan; int secretlen; - int (*secret)(SConn*, uchar*, int);// - int (*read)(SConn*, uchar*, int); // <0 if error; errmess in buffer + int (*secret)(SConn*, uchar*, int);/* */ + int (*read)(SConn*, uchar*, int); /* <0 if error; errmess in buffer */ int (*write)(SConn*, uchar*, int); - void (*free)(SConn*); // also closes file descriptor + void (*free)(SConn*); /* also closes file descriptor */ }; -// secret(s,b,dir) sets secret for digest, encrypt, using the secretlen -// bytes in b to form keys for the two directions; -// set dir=0 in client, dir=1 in server +/* secret(s,b,dir) sets secret for digest, encrypt, using the secretlen */ +/* bytes in b to form keys for the two directions; */ +/* set dir=0 in client, dir=1 in server */ -// error convention: write !message in-band +/* error convention: write !message in-band */ #define readstr secstore_readstr static void writerr(SConn*, char*); -static int readstr(SConn*, char*); // call with buf of size Maxmsg+1 - // returns -1 upon error, with error message in buf +static int readstr(SConn*, char*); /* call with buf of size Maxmsg+1 */ + /* returns -1 upon error, with error message in buf */ typedef struct ConnState { uchar secret[SHA1dlen]; @@ -82,9 +97,10 @@ RC4state rc4; } ConnState; -typedef struct SS{ - int fd; // file descriptor for read/write of encrypted data - int alg; // if nonzero, "alg sha rc4_128" +#undef SS +typedef struct SS { + int fd; /* file descriptor for read/write of encrypted data */ + int alg; /* if nonzero, "alg sha rc4_128" */ ConnState in, out; } SS; @@ -101,7 +117,7 @@ hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->out.secret, nil); hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->in.secret, nil); } - setupRC4state(&ss->in.rc4, ss->in.secret, 16); // restrict to 128 bits + setupRC4state(&ss->in.rc4, ss->in.secret, 16); /* restrict to 128 bits */ setupRC4state(&ss->out.rc4, ss->out.secret, 16); ss->alg = 1; return 0; @@ -152,7 +168,7 @@ werrstr("!SC_read invalid count"); return -1; } - len = (count[0]&0x7f)<<8 | count[1]; // SSL-style count; no pad + len = (count[0]&0x7f)<<8 | count[1]; /* SSL-style count; no pad */ if(ss->alg){ len -= SHA1dlen; if(len <= 0 || readn(ss->fd, digest, SHA1dlen) != SHA1dlen){ @@ -187,7 +203,7 @@ SC_write(SConn *conn, uchar *buf, int n) { SS *ss = (SS*)(conn->chan); - uchar count[2], digest[SHA1dlen], enc[Maxmsg+1]; + uchar count[2], digest[SHA1dlen], *enc; int len; if(n <= 0 || n > Maxmsg+1){ @@ -206,13 +222,16 @@ if(ss->alg){ hash(ss->out.secret, buf, n, ss->out.seqno, digest); rc4(&ss->out.rc4, digest, SHA1dlen); + enc = emalloc(Maxmsg+1); memcpy(enc, buf, n); rc4(&ss->out.rc4, enc, n); if(write(ss->fd, digest, SHA1dlen) != SHA1dlen || write(ss->fd, enc, n) != n){ werrstr("!SC_write error on send"); + free(enc); return -1; } + free(enc); }else{ if(write(ss->fd, buf, n) != n){ werrstr("!SC_write error on send"); @@ -257,10 +276,11 @@ static void writerr(SConn *conn, char *s) { - char buf[Maxmsg]; + char *t; - snprint(buf, Maxmsg, "!%s", s); - conn->write(conn, (uchar*)buf, strlen(buf)); + t = smprint("!%s", s); + conn->write(conn, (uchar*)t, strlen(t)); + free(t); } static int @@ -286,7 +306,7 @@ { char *buf; int nbuf, n, nr, len; - char s[Maxmsg+1], *gf, *p, *q; + char *s, *gf, *p, *q; uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw; AESstate aes; DigestState *sha; @@ -294,6 +314,7 @@ gf = "factotum"; memset(&aes, 0, sizeof aes); + s = (char*)ib; snprint(s, Maxmsg, "GET %s\n", gf); conn->write(conn, (uchar*)s, strlen(s)); @@ -306,7 +327,7 @@ if((len = atoi(s)) < 0){ werrstr("secstore: remote file %s does not exist", gf); return -1; - }else if(len > MAXFILESIZE){//assert + }else if(len > MAXFILESIZE){/*assert */ werrstr("secstore: implausible file size %d for %s", len, gf); return -1; } @@ -362,8 +383,10 @@ if(q = strchr(p, '\n')) *q++ = '\0'; n++; - if(ctlwrite(p, 0) < 0) + if(ctlwrite(p) < 0){ + flog("secstore %s:%d: %r", gf, n); fprint(2, "factotum: secstore(%s) line %d: %r\n", gf, n); + } p = q; } free(buf); @@ -378,7 +401,7 @@ static PAKparams *pak; -// This group was generated by the seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E. +/* This group was generated by the seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E. */ static void initPAKparams(void) { @@ -400,8 +423,8 @@ "2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", nil, 16, nil); } -// H = (sha(ver,C,sha(passphrase)))^r mod p, -// a hash function expensive to attack by brute force. +/* H = (sha(ver,C,sha(passphrase)))^r mod p, */ +/* a hash function expensive to attack by brute force. */ static void longhash(char *ver, char *C, uchar *passwd, mpint *H) { @@ -427,7 +450,7 @@ mpexp(H, pak->r, pak->p, H); } -// Hi = H^-1 mod p +/* Hi = H^-1 mod p */ static char * PAK_Hi(char *C, char *passphrase, mpint *H, mpint *Hi) { @@ -440,8 +463,8 @@ return mptoa(Hi, 64, nil, 0); } -// another, faster, hash function for each party to -// confirm that the other has the right secrets. +/* another, faster, hash function for each party to */ +/* confirm that the other has the right secrets. */ static void shorthash(char *mess, char *C, char *S, char *m, char *mu, char *sigma, char *Hi, uchar *digest) { @@ -463,12 +486,12 @@ sha1((uchar*)Hi, strlen(Hi), digest, state); } -// On input, conn provides an open channel to the server; -// C is the name this client calls itself; -// pass is the user's passphrase -// On output, session secret has been set in conn -// (unless return code is negative, which means failure). -// If pS is not nil, it is set to the (alloc'd) name the server calls itself. +/* On input, conn provides an open channel to the server; */ +/* C is the name this client calls itself; */ +/* pass is the user's passphrase */ +/* On output, session secret has been set in conn */ +/* (unless return code is negative, which means failure). */ +/* If pS is not nil, it is set to the (alloc'd) name the server calls itself. */ static int PAKclient(SConn *conn, char *C, char *pass, char **pS) { @@ -481,7 +504,7 @@ hexHi = PAK_Hi(C, pass, H, Hi); - // random 1<=x<=q-1; send C, m=g**x H + /* random 1<=x<=q-1; send C, m=g**x H */ x = mprand(164, genrandom, nil); mpmod(x, pak->q, x); if(mpcmp(x, mpzero) == 0) @@ -495,9 +518,9 @@ snprint(mess, Maxmsg, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm); conn->write(conn, (uchar*)mess, strlen(mess)); - // recv g**y, S, check hash1(g**xy) + /* recv g**y, S, check hash1(g**xy) */ if(readstr(conn, mess) < 0){ - fprint(2, "factotum: error: %s\n", mess); + fprint(2, "factotum: PAKclient: error: %s\n", mess); writerr(conn, "couldn't read g**y"); goto done; } @@ -534,18 +557,18 @@ goto done; } - // send hash2(g**xy) + /* send hash2(g**xy) */ shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest); enc64(kc, sizeof kc, digest, SHA1dlen); snprint(mess2, Maxmsg, "k'=%s\n", kc); conn->write(conn, (uchar*)mess2, strlen(mess2)); - // set session key + /* set session key */ shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest); memset(hexsigma, 0, strlen(hexsigma)); n = conn->secret(conn, digest, 0); memset(digest, 0, SHA1dlen); - if(n < 0){//assert + if(n < 0){/*assert */ writerr(conn, "can't set secret"); goto done; } @@ -568,13 +591,14 @@ int secstorefetch(char *password) { - int rv = -1, fd; - char s[Maxmsg+1]; + char *s, *pass, *sta; + int rv, fd; SConn *conn; - char *pass, *sta; sta = nil; conn = nil; + s = nil; + rv = -1; if(password != nil && *password) pass = estrdup(password); else @@ -591,6 +615,7 @@ werrstr("password mistyped?"); goto Out; } + s = emalloc(Maxmsg+1); if(readstr(conn, s) < 0) goto Out; if(strcmp(s, "STA") == 0){ @@ -599,7 +624,7 @@ werrstr("cancel"); goto Out; } - if(strlen(sta) >= sizeof s - 3){ + if(strlen(sta) >= Maxmsg - 3){ werrstr("STA response too long"); goto Out; } @@ -617,12 +642,13 @@ rv = 0; Out: + if(rv < 0) + flog("secstorefetch: %r"); if(conn) conn->free(conn); - if(pass) - free(pass); - if(sta) - free(sta); + free(s); + free(pass); + free(sta); return rv; } diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/std.h /sys/src/cmd/auth/factotum/std.h --- /n/sources/plan9/sys/src/cmd/auth/factotum/std.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/std.h Tue Jan 1 00:00:00 2013 @@ -0,0 +1,10 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include <9p.h> + diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/test.c /sys/src/cmd/auth/factotum/test.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/test.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/test.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,121 @@ +#include +#include +#include + +typedef struct Test Test; + +struct Test +{ + char *name; + int (*server)(Test*, AuthRpc*, int); + int (*client)(Test*, int); +}; + +int +ai2status(AuthInfo *ai) +{ + if(ai == nil) + return -1; + auth_freeAI(ai); + return 0; +} + +int +proxyserver(Test *t, AuthRpc *rpc, int fd) +{ + char buf[1024]; + + sprint(buf, "proto=%q role=server", t->name); + return ai2status(fauth_proxy(fd, rpc, nil, buf)); +} + +int +proxyclient(Test *t, int fd) +{ + return ai2status(auth_proxy(fd, auth_getkey, "proto=%q role=client", t->name)); +} + +Test test[] = +{ + "apop", proxyserver, proxyclient, + "cram", proxyserver, proxyclient, + "p9sk1", proxyserver, proxyclient, + "p9sk2", proxyserver, proxyclient, + "p9any", proxyserver, proxyclient +}; + +void +usage(void) +{ + fprint(2, "usage: test [name]...\n"); + exits("usage"); +} + +void +runtest(AuthRpc *srpc, Test *t) +{ + int p[2], bad; + Waitmsg *w; + + if(pipe(p) < 0) + sysfatal("pipe: %r"); + + print("%s...", t->name); + + switch(fork()){ + case -1: + sysfatal("fork: %r"); + + case 0: + close(p[0]); + if((*t->server)(t, srpc, p[1]) < 0){ + print("\n\tserver: %r"); + _exits("oops"); + } + close(p[1]); + _exits(nil); + default: + close(p[1]); + if((*t->client)(t, p[0]) < 0){ + print("\n\tclient: %r"); + bad = 1; + } + close(p[0]); + break; + } + w = wait(); + if(w->msg[0]) + bad = 1; + print("\n"); +} + +void +main(int argc, char **argv) +{ + int i, j; + int afd; + AuthRpc *srpc; + + ARGBEGIN{ + default: + usage(); + }ARGEND + + quotefmtinstall(); + afd = open("/n/kremvax/factotum/rpc", ORDWR); + if(afd < 0) + sysfatal("open /n/kremvax/factotum/rpc: %r"); + srpc = auth_allocrpc(afd); + if(srpc == nil) + sysfatal("auth_allocrpc: %r"); + + if(argc == 0) + for(i=0; i ndat) + return -1; + if(hex[strspn(hex, "0123456789abcdefABCDEF")] != '\0') + return -1; + for(i=0; i=0) - return authdial(net, authdom); - - /* - * If we failed to mount /srv/cs, assume that - * we're still bootstrapping the system and dial - * the one auth server passed to us on the command line. - * In normal operation, it is important *not* to do this, - * because the bootstrap auth server is only good for - * a single auth domain. - * - * The ticket request code should really check the - * remote authentication domain too. - */ - - /* use the auth server passed to us as an arg */ - if(authaddr == nil) - return -1; - fd = dial(netmkaddr(authaddr, "tcp", "567"), 0, 0, 0); - if(fd >= 0) - return fd; - return dial(netmkaddr(authaddr, "il", "566"), 0, 0, 0); + memset(to, 0, n); + if(n == 1) + return to; + if(from==nil) + sysfatal("safecpy called with from==nil, pc=%#p", + getcallerpc(&to)); + strncpy(to, from, n-1); + return to; } int @@ -93,483 +126,36 @@ return fd; return -1; } -/* - * prompt user for a key. don't care about memory leaks, runs standalone - */ -static Attr* -promptforkey(char *params) -{ - char *v; - int fd; - Attr *a, *attr; - char *def; - - fd = open("/dev/cons", ORDWR); - if(fd < 0) - sysfatal("opening /dev/cons: %r"); - - attr = _parseattr(params); - fprint(fd, "\n!Adding key:"); - for(a=attr; a; a=a->next) - if(a->type != AttrQuery && a->name[0] != '!') - fprint(fd, " %q=%q", a->name, a->val); - fprint(fd, "\n"); - - for(a=attr; a; a=a->next){ - v = a->name; - if(a->type != AttrQuery || v[0]=='!') - continue; - def = nil; - if(strcmp(v, "user") == 0) - def = getuser(); - a->val = readcons(v, def, 0); - if(a->val == nil) - sysfatal("user terminated key input"); - a->type = AttrNameval; - } - for(a=attr; a; a=a->next){ - v = a->name; - if(a->type != AttrQuery || v[0]!='!') - continue; - def = nil; - if(strcmp(v+1, "user") == 0) - def = getuser(); - a->val = readcons(v+1, def, 1); - if(a->val == nil) - sysfatal("user terminated key input"); - a->type = AttrNameval; - } - fprint(fd, "!\n"); - close(fd); - return attr; -} - -/* - * send a key to the mounted factotum - */ -static int -sendkey(Attr *attr) -{ - int fd, rv; - char buf[1024]; - - fd = open("/mnt/factotum/ctl", ORDWR); - if(fd < 0) - sysfatal("opening /mnt/factotum/ctl: %r"); - rv = fprint(fd, "key %A\n", attr); - read(fd, buf, sizeof buf); - close(fd); - return rv; -} - -/* askuser */ -void -askuser(char *params) -{ - Attr *attr; - - attr = promptforkey(params); - if(attr == nil) - sysfatal("no key supplied"); - if(sendkey(attr) < 0) - sysfatal("sending key to factotum: %r"); -} - -ulong conftaggen; -int -canusekey(Fsstate *fss, Key *k) -{ - int i; - - if(_strfindattr(k->attr, "confirm")){ - for(i=0; inconf; i++) - if(fss->conf[i].key == k) - return fss->conf[i].canuse; - if(fss->nconf%16 == 0) - fss->conf = erealloc(fss->conf, (fss->nconf+16)*(sizeof(fss->conf[0]))); - fss->conf[fss->nconf].key = k; - k->ref++; - fss->conf[fss->nconf].canuse = -1; - fss->conf[fss->nconf].tag = conftaggen++; - fss->nconf++; - return -1; - } - return 1; -} - -/* closekey */ -void -closekey(Key *k) -{ - if(k == nil) - return; - if(--k->ref != 0) - return; - if(k->proto && k->proto->closekey) - (*k->proto->closekey)(k); - _freeattr(k->attr); - _freeattr(k->privattr); - k->attr = (void*)~1; - k->privattr = (void*)~1; - k->proto = nil; - free(k); -} - -static uchar* -pstring(uchar *p, uchar *e, char *s) -{ - uint n; - - if(p == nil) - return nil; - if(s == nil) - s = ""; - n = strlen(s); - if(p+n+BIT16SZ >= e) - return nil; - PBIT16(p, n); - p += BIT16SZ; - memmove(p, s, n); - p += n; - return p; -} - -static uchar* -pcarray(uchar *p, uchar *e, uchar *s, uint n) -{ - if(p == nil) - return nil; - if(s == nil){ - if(n > 0) - sysfatal("pcarray"); - s = (uchar*)""; - } - if(p+n+BIT16SZ >= e) - return nil; - PBIT16(p, n); - p += BIT16SZ; - memmove(p, s, n); - p += n; - return p; -} - -uchar* -convAI2M(AuthInfo *ai, uchar *p, int n) -{ - uchar *e = p+n; - - p = pstring(p, e, ai->cuid); - p = pstring(p, e, ai->suid); - p = pstring(p, e, ai->cap); - p = pcarray(p, e, ai->secret, ai->nsecret); - return p; -} int -failure(Fsstate *s, char *fmt, ...) -{ - char e[ERRMAX]; - va_list arg; - - if(fmt == nil) - rerrstr(s->err, sizeof(s->err)); - else { - va_start(arg, fmt); - vsnprint(e, sizeof e, fmt, arg); - va_end(arg); - strecpy(s->err, s->err+sizeof(s->err), e); - werrstr(e); - } - flog("%d: failure %s", s->seqnum, s->err); - return RpcFailure; -} - -static int -hasqueries(Attr *a) -{ - for(; a; a=a->next) - if(a->type == AttrQuery) - return 1; - return 0; -} - -char *ignored[] = { - "role", - "disabled", -}; - -static int -ignoreattr(char *s) -{ - int i; - - for(i=0; ifss = fss; - k->user = fss->sysuser; - if(attr) - k->attr = attr; - else - k->attr = fss->attr; - return k; -} - -int -findkey(Key **ret, Keyinfo *ki, char *fmt, ...) -{ - int i, s, nmatch; - char buf[1024], *p, *who; - va_list arg; - Attr *a, *attr0, *attr1, *attr2, *attr3, **l; - Key *k; - - *ret = nil; - - who = ki->user; - attr0 = ki->attr; - if(fmt){ - va_start(arg, fmt); - vseprint(buf, buf+sizeof buf, fmt, arg); - va_end(arg); - attr1 = _parseattr(buf); - }else - attr1 = nil; - - if(who && strcmp(who, owner) == 0) - who = nil; - - if(who){ - snprint(buf, sizeof buf, "owner=%q", who); - attr2 = _parseattr(buf); - attr3 = _parseattr("owner=*"); - }else - attr2 = attr3 = nil; - - p = _strfindattr(attr0, "proto"); - if(p == nil) - p = _strfindattr(attr1, "proto"); - if(p && findproto(p) == nil){ - werrstr("unknown protocol %s", p); - _freeattr(attr1); - _freeattr(attr2); - _freeattr(attr3); - return failure(ki->fss, nil); - } - - nmatch = 0; - for(i=0; inkey; i++){ - k = ring->key[i]; - if(_strfindattr(k->attr, "disabled") && !ki->usedisabled) - continue; - if(matchattr(attr0, k->attr, k->privattr) && matchattr(attr1, k->attr, k->privattr)){ - /* check ownership */ - if(!matchattr(attr2, k->attr, nil) && !matchattr(attr3, k->attr, nil)) - continue; - if(nmatch++ < ki->skip) - continue; - if(!ki->noconf){ - switch(canusekey(ki->fss, k)){ - case -1: - _freeattr(attr1); - return RpcConfirm; - case 0: - continue; - case 1: - break; - } - } - _freeattr(attr1); - _freeattr(attr2); - _freeattr(attr3); - k->ref++; - *ret = k; - return RpcOk; - } - } - flog("%d: no key matches %A %A %A %A", ki->fss->seqnum, attr0, attr1, attr2, attr3); - werrstr("no key matches %A %A", attr0, attr1); - _freeattr(attr2); - _freeattr(attr3); - s = RpcFailure; - if(askforkeys && who==nil && (hasqueries(attr0) || hasqueries(attr1))){ - if(nmatch == 0){ - attr0 = _copyattr(attr0); - for(l=&attr0; *l; l=&(*l)->next) - ; - *l = attr1; - for(l=&attr0; *l; ){ - if(ignoreattr((*l)->name)){ - a = *l; - *l = (*l)->next; - a->next = nil; - _freeattr(a); - }else - l = &(*l)->next; - } - attr0 = sortattr(attr0); - snprint(ki->fss->keyinfo, sizeof ki->fss->keyinfo, "%A", attr0); - _freeattr(attr0); - attr1 = nil; /* attr1 was linked to attr0 */ - }else - ki->fss->keyinfo[0] = '\0'; - s = RpcNeedkey; - } - _freeattr(attr1); - if(s == RpcFailure) - return failure(ki->fss, nil); /* loads error string */ - return s; -} - -int -findp9authkey(Key **k, Fsstate *fss) -{ - char *dom; - Keyinfo ki; - - /* - * We don't use fss->attr here because we don't - * care about what the user name is set to, for instance. - */ - mkkeyinfo(&ki, fss, nil); - ki.attr = nil; - ki.user = nil; - if(dom = _strfindattr(fss->attr, "dom")) - return findkey(k, &ki, "proto=p9sk1 dom=%q role=server user?", dom); - else - return findkey(k, &ki, "proto=p9sk1 role=server dom? user?"); -} - -Proto* -findproto(char *name) -{ - int i; - - for(i=0; prototab[i]; i++) - if(strcmp(name, prototab[i]->name) == 0) - return prototab[i]; - return nil; -} - -char* -getnvramkey(int flag, char **secstorepw) +_authdial(char *net, char *authdom) { - char *s; - Nvrsafe safe; - char spw[CONFIGLEN+1]; - int i; + int fd, vanilla; - memset(&safe, 0, sizeof safe); - /* - * readnvram can return -1 meaning nvram wasn't written, - * but safe still holds good data. - */ - if(readnvram(&safe, flag)<0 && safe.authid[0]==0) - return nil; + vanilla = net==nil || strcmp(net, "/net")==0; - /* - * we're using the config area to hold the secstore - * password. if there's anything there, return it. - */ - memmove(spw, safe.config, CONFIGLEN); - spw[CONFIGLEN] = 0; - if(spw[0] != 0) - *secstorepw = estrdup(spw); + if(!vanilla || bindnetcs()>=0) + return authdial(net, authdom); /* - * only use nvram key if it is non-zero + * If we failed to mount /srv/cs, assume that + * we're still bootstrapping the system and dial + * the one auth server passed to us on the command line. + * In normal operation, it is important *not* to do this, + * because the bootstrap auth server is only good for + * a single auth domain. + * + * The ticket request code should really check the + * remote authentication domain too. */ - for(i = 0; i < DESKEYLEN; i++) - if(safe.machkey[i] != 0) - break; - if(i == DESKEYLEN) - return nil; - - s = emalloc(512); - fmtinstall('H', encodefmt); - snprint(s, 512, "key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______", - safe.authid, safe.authdom, DESKEYLEN, safe.machkey); - writehostowner(safe.authid); - - return s; -} -int -isclient(char *role) -{ - if(role == nil){ - werrstr("role not specified"); + /* use the auth server passed to us as an arg */ + if(authaddr == nil) return -1; - } - if(strcmp(role, "server") == 0) - return 0; - if(strcmp(role, "client") == 0) - return 1; - werrstr("unknown role %q", role); - return -1; -} - -static int -hasname(Attr *a0, Attr *a1, char *name) -{ - return _findattr(a0, name) || _findattr(a1, name); -} - -static int -hasnameval(Attr *a0, Attr *a1, char *name, char *val) -{ - Attr *a; - - for(a=_findattr(a0, name); a; a=_findattr(a->next, name)) - if(strcmp(a->val, val) == 0) - return 1; - for(a=_findattr(a1, name); a; a=_findattr(a->next, name)) - if(strcmp(a->val, val) == 0) - return 1; - return 0; -} - -int -matchattr(Attr *pat, Attr *a0, Attr *a1) -{ - int type; - - for(; pat; pat=pat->next){ - type = pat->type; - if(ignoreattr(pat->name)) - type = AttrDefault; - switch(type){ - case AttrQuery: /* name=something be present */ - if(!hasname(a0, a1, pat->name)) - return 0; - break; - case AttrNameval: /* name=val must be present */ - if(!hasnameval(a0, a1, pat->name, pat->val)) - return 0; - break; - case AttrDefault: /* name=val must be present if name=anything is present */ - if(hasname(a0, a1, pat->name) && !hasnameval(a0, a1, pat->name, pat->val)) - return 0; - break; - } - } - return 1; -} - -void -memrandom(void *p, int n) -{ - uchar *cp; - - for(cp = (uchar*)p; n > 0; n--) - *cp++ = fastrand(); + fd = dial(netmkaddr(authaddr, "il", "566"), 0, 0, 0); + if(fd >= 0) + return fd; + return dial(netmkaddr(authaddr, "tcp", "567"), 0, 0, 0); } /* @@ -581,8 +167,6 @@ initcap(void) { caphashfd = open("#¤/caphash", OWRITE); -// if(caphashfd < 0) -// fprint(2, "%s: opening #¤/caphash: %r\n", argv0); } /* @@ -594,7 +178,7 @@ uchar rand[20]; char *cap; char *key; - int nfrom, nto, ncap; + int nfrom, nto; uchar hash[SHA1dlen]; if(caphashfd < 0) @@ -603,9 +187,8 @@ /* create the capability */ nto = strlen(to); nfrom = strlen(from); - ncap = nfrom + 1 + nto + 1 + sizeof(rand)*3 + 1; - cap = emalloc(ncap); - snprint(cap, ncap, "%s@%s", from, to); + cap = emalloc(nfrom+1+nto+1+sizeof(rand)*3+1); + sprint(cap, "%s@%s", from, to); memrandom(rand, sizeof(rand)); key = cap+nfrom+1+nto+1; enc64(key, sizeof(rand)*3, rand, sizeof(rand)); @@ -623,101 +206,22 @@ return cap; } -int -phaseerror(Fsstate *s, char *op) -{ - char tmp[32]; - - werrstr("protocol phase error: %s in state %s", op, phasename(s, s->phase, tmp)); - return RpcPhase; -} - -char* -phasename(Fsstate *fss, int phase, char *tmp) -{ - char *name; - - if(fss->phase == Broken) - name = "Broken"; - else if(phase == Established) - name = "Established"; - else if(phase == Notstarted) - name = "Notstarted"; - else if(phase < 0 || phase >= fss->maxphase - || (name = fss->phasename[phase]) == nil){ - sprint(tmp, "%d", phase); - name = tmp; - } - return name; -} - -static int -outin(char *prompt, char *def, int len) -{ - char *s; - - s = readcons(prompt, def, 0); - if(s == nil) - return -1; - if(s == nil) - sysfatal("s==nil???"); - strncpy(def, s, len); - def[len-1] = 0; - free(s); - return strlen(def); -} - -/* - * get host owner and set it - */ -void -promptforhostowner(void) -{ - char owner[64], *p; - - /* hack for bitsy; can't prompt during boot */ - if(p = getenv("user")){ - writehostowner(p); - free(p); - return; - } - free(p); - - strcpy(owner, "none"); - do{ - outin("user", owner, sizeof(owner)); - } while(*owner == 0); - writehostowner(owner); -} - -char* -estrappend(char *s, char *fmt, ...) -{ - char *t; - va_list arg; - - va_start(arg, fmt); - t = vsmprint(fmt, arg); - if(t == nil) - sysfatal("out of memory"); - va_end(arg); - s = erealloc(s, strlen(s)+strlen(t)+1); - strcat(s, t); - free(t); - return s; -} - - /* * prompt for a string with a possible default response */ char* readcons(char *prompt, char *def, int raw) { - int fdin, fdout, ctl, n; + int fdin, fdout, ctl, i, n; char line[10]; char *s; + if(raw){ + ctl = open("/dev/consctl", OWRITE); + if(ctl >= 0) + write(ctl, "rawon", 5); + } else + ctl = -1; fdin = open("/dev/cons", OREAD); if(fdin < 0) fdin = 0; @@ -728,12 +232,6 @@ fprint(fdout, "%s[%s]: ", prompt, def); else fprint(fdout, "%s: ", prompt); - if(raw){ - ctl = open("/dev/consctl", OWRITE); - if(ctl >= 0) - write(ctl, "rawon", 5); - } else - ctl = -1; s = estrdup(""); for(;;){ n = read(fdin, line, 1); @@ -763,8 +261,8 @@ return s; } if(line[0] == '\b'){ - if(strlen(s) > 0) - s[strlen(s)-1] = 0; + for(i = strlen(s); i>0 && (s[i]&0xc0) == 0x80; i--) + s[i] = 0; } else if(line[0] == 0x15) { /* ^U: line kill */ if(def != nil) fprint(fdout, "\n%s[%s]: ", prompt, def); @@ -778,173 +276,128 @@ } } -/* - * Insert a key into the keyring. - * If the public attributes are identical to some other key, replace that one. - */ int -replacekey(Key *kn, int before) +memrandom(void *p, int n) { - int i; - Key *k; + uchar *cp; - for(i=0; inkey; i++){ - k = ring->key[i]; - if(matchattr(kn->attr, k->attr, nil) && matchattr(k->attr, kn->attr, nil)){ - closekey(k); - kn->ref++; - ring->key[i] = kn; - return 0; - } - } - if(ring->nkey%16 == 0) - ring->key = erealloc(ring->key, (ring->nkey+16)*sizeof(ring->key[0])); - kn->ref++; - if(before){ - memmove(ring->key+1, ring->key, ring->nkey*sizeof ring->key[0]); - ring->key[0] = kn; - ring->nkey++; - }else - ring->key[ring->nkey++] = kn; + for(cp = (uchar*)p; n > 0; n--) + *cp++ = fastrand(); return 0; } -char* -safecpy(char *to, char *from, int n) +Key* +plan9authkey(Attr *a) { - memset(to, 0, n); - if(n == 1) - return to; - if(from==nil) - sysfatal("safecpy called with from==nil, pc=%#p", - getcallerpc(&to)); - strncpy(to, from, n-1); - return to; + char *dom; + Key *k; + + /* + * The only important part of a is dom. + * We don't care, for example, about user name. + */ + dom = strfindattr(a, "dom"); + if(dom) + k = keylookup("proto=p9sk1 role=server user? dom=%q", dom); + else + k = keylookup("proto=p9sk1 role=server user? dom?"); + if(k == nil) + werrstr("could not find plan 9 auth key dom %q", dom); + return k; } Attr* -setattr(Attr *a, char *fmt, ...) +addcap(Attr *a, char *sysuser, Ticket *t) { - char buf[1024]; - va_list arg; - Attr *b; + Attr *newa; + char *c; - va_start(arg, fmt); - vseprint(buf, buf+sizeof buf, fmt, arg); - va_end(arg); - b = _parseattr(buf); - a = setattrs(a, b); - setmalloctag(a, getcallerpc(&a)); - _freeattr(b); - return a; +// c = mkcap(t->cuid, t->suid); + c = mkcap(sysuser, t->suid); + newa = addattr(a, "cuid=%q suid=%q cap=%s", t->cuid, t->suid, c); + free(c); + return newa; } -/* - * add attributes in list b to list a. If any attributes are in - * both lists, replace those in a by those in b. - */ -Attr* -setattrs(Attr *a, Attr *b) +char* +getnvramkey(int flag, char **secstorepw) { - int found; - Attr **l, *freea; + char *s; + Nvrsafe safe; + char spw[CONFIGLEN+1]; + int i; - for(; b; b=b->next){ - found = 0; - for(l=&a; *l; ){ - if(strcmp(b->name, (*l)->name) == 0){ - switch(b->type){ - case AttrNameval: - if(!found){ - found = 1; - free((*l)->val); - (*l)->val = estrdup(b->val); - (*l)->type = AttrNameval; - l = &(*l)->next; - }else{ - freea = *l; - *l = (*l)->next; - freea->next = nil; - _freeattr(freea); - } - break; - case AttrQuery: - goto continue2; - } - }else - l = &(*l)->next; - } - if(found == 0){ - *l = _mkattr(b->type, b->name, b->val, nil); - setmalloctag(*l, getcallerpc(&a)); - } -continue2:; - } - return a; -} + memset(&safe, 0, sizeof safe); + /* + * readnvram can return -1 meaning nvram wasn't written, + * but safe still holds good data. + */ + if(readnvram(&safe, flag)<0 && safe.authid[0]==0) + return nil; -void -setmalloctaghere(void *v) -{ - setmalloctag(v, getcallerpc(&v)); + /* + * we're using the config area to hold the secstore + * password. if there's anything there, return it. + */ + memmove(spw, safe.config, CONFIGLEN); + spw[CONFIGLEN] = 0; + if(spw[0] != 0) + *secstorepw = estrdup(spw); + + /* + * only use nvram key if it is non-zero + */ + for(i = 0; i < DESKEYLEN; i++) + if(safe.machkey[i] != 0) + break; + if(i == DESKEYLEN) + return nil; + + s = emalloc(512); + sprint(s, "key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______", + safe.authid, safe.authdom, DESKEYLEN, safe.machkey); + writehostowner(safe.authid); + + return s; } -Attr* -sortattr(Attr *a) +static int +outin(char *prompt, char *def, int len) { - int i; - Attr *anext, *a0, *a1, **l; + char *s; - if(a == nil || a->next == nil) - return a; + s = readcons(prompt, def, 0); + if(s == nil) + return -1; + if(s == nil) + sysfatal("s==nil???"); + strncpy(def, s, len); + def[len-1] = 0; + free(s); + return strlen(def); +} - /* cut list in halves */ - a0 = nil; - a1 = nil; - i = 0; - for(; a; a=anext){ - anext = a->next; - if(i++%2){ - a->next = a0; - a0 = a; - }else{ - a->next = a1; - a1 = a; - } - } +/* + * get host owner and set it + */ +void +promptforhostowner(void) +{ + char owner[64], *p; - /* sort */ - a0 = sortattr(a0); - a1 = sortattr(a1); - - /* merge */ - l = &a; - while(a0 || a1){ - if(a1==nil){ - anext = a0; - a0 = a0->next; - }else if(a0==nil){ - anext = a1; - a1 = a1->next; - }else if(strcmp(a0->name, a1->name) < 0){ - anext = a0; - a0 = a0->next; - }else{ - anext = a1; - a1 = a1->next; - } - *l = anext; - l = &(*l)->next; + /* hack for bitsy; can't prompt during boot */ + if(p = getenv("user")){ + writehostowner(p); + free(p); + return; } - *l = nil; - return a; -} + free(p); -int -toosmall(Fsstate *fss, uint n) -{ - fss->rpc.nwant = n; - return RpcToosmall; + strcpy(owner, "none"); + do{ + outin("user", owner, sizeof(owner)); + } while(*owner == 0); + writehostowner(owner); } void @@ -966,38 +419,3 @@ } } -int -attrnamefmt(Fmt *fmt) -{ - char *b, buf[1024], *ebuf; - Attr *a; - - ebuf = buf+sizeof buf; - b = buf; - strcpy(buf, " "); - for(a=va_arg(fmt->args, Attr*); a; a=a->next){ - if(a->name == nil) - continue; - b = seprint(b, ebuf, " %q?", a->name); - } - return fmtstrcpy(fmt, buf+1); -} - -void -disablekey(Key *k) -{ - Attr *a; - - if(sflag) /* not on servers */ - return; - for(a=k->attr; a; a=a->next){ - if(a->type==AttrNameval && strcmp(a->name, "disabled") == 0) - return; - if(a->next == nil) - break; - } - if(a) - a->next = _mkattr(AttrNameval, "disabled", "by.factotum", nil); - else - k->attr = _mkattr(AttrNameval, "disabled", "by.factotum", nil); /* not reached: always a proto attribute */ -} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/util2.c /sys/src/cmd/auth/factotum/util2.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/util2.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/util2.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,71 @@ +#include "std.h" +#include "dat.h" + +/* +ulong conftaggen; +int +canusekey(Fsstate *fss, Key *k) +{ + int i; + + if(_strfindattr(k->attr, "confirm")){ + for(i=0; inconf; i++) + if(fss->conf[i].key == k) + return fss->conf[i].canuse; + if(fss->nconf%16 == 0) + fss->conf = erealloc(fss->conf, (fss->nconf+16)*(sizeof(fss->conf[0]))); + fss->conf[fss->nconf].key = k; + k->ref++; + fss->conf[fss->nconf].canuse = -1; + fss->conf[fss->nconf].tag = conftaggen++; + fss->nconf++; + return -1; + } + return 1; +} +*/ + +#ifdef UNDEF +#endif + +/* +char* +phasename(Fsstate *fss, int phase, char *tmp) +{ + char *name; + + if(fss->phase == Broken) + name = "Broken"; + else if(phase == Established) + name = "Established"; + else if(phase == Notstarted) + name = "Notstarted"; + else if(phase < 0 || phase >= fss->maxphase + || (name = fss->phasename[phase]) == nil){ + sprint(tmp, "%d", phase); + name = tmp; + } + return name; +} +*/ + +#ifdef UNDEF +void +disablekey(Key *k) +{ + Attr *a; + + if(sflag) /* not on servers */ + return; + for(a=k->attr; a; a=a->next){ + if(a->type==AttrNameval && strcmp(a->name, "disabled") == 0) + return; + if(a->next == nil) + break; + } + if(a) + a->next = _mkattr(AttrNameval, "disabled", "by.factotum", nil); + else + k->attr = _mkattr(AttrNameval, "disabled", "by.factotum", nil); /* not reached: always a proto attribute */ +} +#endif diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/wep.c /sys/src/cmd/auth/factotum/wep.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/wep.c Sun Feb 6 16:08:24 2005 +++ /sys/src/cmd/auth/factotum/wep.c Tue Jan 1 00:00:00 2013 @@ -1,128 +1,83 @@ /* - * The caller supplies the device, we do the flavoring. There - * are no phases, everything happens in the init routine. + * Copy WEP key to ethernet device. */ +#include "std.h" #include "dat.h" -typedef struct State State; -struct State -{ - Key *key; -}; - -enum -{ - HavePass, -}; - static int -wepinit(Proto*, Fsstate *fss) +wepclient(Conv *c) { - int ret; + char *dev, buf[128], *p, *kp; Key *k; - Keyinfo ki; - State *s; - - /* find a key with at least one password */ - mkkeyinfo(&ki, fss, nil); - ret = findkey(&k, &ki, "!key1?"); - if(ret != RpcOk) - ret = findkey(&k, &ki, "!key2?"); - if(ret != RpcOk) - ret = findkey(&k, &ki, "!key3?"); - if(ret != RpcOk) - return ret; - - setattrs(fss->attr, k->attr); - s = emalloc(sizeof(*s)); - s->key = k; - fss->ps = s; - fss->phase = HavePass; - - return RpcOk; -} - -static void -wepclose(Fsstate *fss) -{ - State *s; - - s = fss->ps; - if(s->key) - closekey(s->key); - free(s); -} - -static int -wepread(Fsstate *fss, void*, uint*) -{ - return phaseerror(fss, "read"); -} - -static int -wepwrite(Fsstate *fss, void *va, uint n) -{ - char *data = va; - State *s; - char dev[64]; - int fd, cfd; - int rv; - char *p; - - /* get the device */ - if(n > sizeof(dev)-5){ - werrstr("device too long"); - return RpcErrstr; + int ret, fd, cfd; + + fd = cfd = -1; + ret = -1; + dev = nil; + + if((k = keylookup("%A !key1?", c->attr)) == nil + && (k = keylookup("%A !key2?", c->attr)) == nil + && (k = keylookup("%A !key3?", c->attr)) == nil){ + werrstr("cannot find wep keys"); + goto out; } - memmove(dev, data, n); - dev[n] = 0; - s = fss->ps; - - /* legal? */ + if(convreadm(c, &dev) < 0) + return -1; if(dev[0] != '#' || dev[1] != 'l'){ - werrstr("%s not an ether device", dev); - return RpcErrstr; + werrstr("not an ethernet device: %s", dev); + goto out; } - strcat(dev, "!0"); - fd = dial(dev, 0, 0, &cfd); - if(fd < 0) - return RpcErrstr; - - /* flavor it with passwords, essid, and turn on wep */ - rv = RpcErrstr; - p = _strfindattr(s->key->privattr, "!key1"); - if(p != nil) - if(fprint(cfd, "key1 %s", p) < 0) - goto out; - p = _strfindattr(s->key->privattr, "!key2"); - if(p != nil) - if(fprint(cfd, "key2 %s", p) < 0) - goto out; - p = _strfindattr(s->key->privattr, "!key3"); - if(p != nil) - if(fprint(cfd, "key3 %s", p) < 0) - goto out; - p = _strfindattr(fss->attr, "essid"); - if(p != nil) - if(fprint(cfd, "essid %s", p) < 0) - goto out; + snprint(buf, sizeof buf, "%s!0", dev); + if((fd = dial(buf, 0, 0, &cfd)) < 0) + goto out; + if(!(p = strfindattr(k->privattr, kp="!key1")) + && !(p = strfindattr(k->privattr, kp="key2")) + && !(p = strfindattr(k->privattr, kp="key3"))){ + werrstr("lost key"); + goto out; + } + if(fprint(cfd, "%s %q", kp+1, p) < 0) + goto out; + if((p = strfindattr(k->attr, "essid")) != nil + && fprint(cfd, "essid %q", p) < 0) + goto out; if(fprint(cfd, "crypt on") < 0) goto out; - rv = RpcOk; + ret = 0; + out: - close(fd); - close(cfd); - return rv; + free(dev); + if(cfd >= 0) + close(cfd); + if(fd >= 0) + close(fd); + keyclose(k); + return ret; } -Proto wep = +static int +wepcheck(Key *k) +{ + if(strfindattr(k->privattr, "!key1") == nil + && strfindattr(k->privattr, "!key2") == nil + && strfindattr(k->privattr, "!key3") == nil){ + werrstr("need !key1, !key2, or !key3 attribute"); + return -1; + } + return 0; +} + +static Role weproles[] = { + "client", wepclient, + 0 +}; + +Proto wep = { -.name= "wep", -.init= wepinit, -.write= wepwrite, -.read= wepread, -.close= wepclose, -.addkey= replacekey, -.keyprompt= "!key1? !key2? !key3? essid?", + "wep", + weproles, + nil, + wepcheck, + nil }; diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/x.c /sys/src/cmd/auth/factotum/x.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/x.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/x.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,15 @@ +#include +#include +#include + +void +f(void*) +{ +} + +void +main(void) +{ + f(auth_challenge); + f(auth_response); +} diff -Nru /n/sources/plan9/sys/src/cmd/auth/factotum/xio.c /sys/src/cmd/auth/factotum/xio.c --- /n/sources/plan9/sys/src/cmd/auth/factotum/xio.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/auth/factotum/xio.c Tue Jan 1 00:00:00 2013 @@ -0,0 +1,165 @@ +#include "std.h" +#include "dat.h" + +static Ioproc *cache[5]; +static int ncache; + +static Ioproc* +xioproc(void) +{ + Ioproc *c; + int i; + + for(i=0; i