123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- /*
- * p9sk1, p9sk2 - Plan 9 secret (private) key authentication.
- * p9sk2 is an incomplete flawed variant of p9sk1.
- *
- * Client protocol:
- * write challenge[challen] (p9sk1 only)
- * read tickreq[tickreqlen]
- * write ticket[ticketlen]
- * read authenticator[authentlen]
- *
- * Server protocol:
- * read challenge[challen] (p9sk1 only)
- * write tickreq[tickreqlen]
- * read ticket[ticketlen]
- * write authenticator[authentlen]
- */
- #include "dat.h"
- struct State
- {
- int vers;
- Key *key;
- Ticket t;
- Ticketreq tr;
- char cchal[CHALLEN];
- char tbuf[TICKETLEN+AUTHENTLEN];
- char authkey[DESKEYLEN];
- uint8_t *secret;
- int speakfor;
- };
- enum
- {
- /* client phases */
- CHaveChal,
- CNeedTreq,
- CHaveTicket,
- CNeedAuth,
- /* server phases */
- SNeedChal,
- SHaveTreq,
- SNeedTicket,
- SHaveAuth,
- Maxphase,
- };
- 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)
- {
- State *s;
- int iscli, ret;
- Key *k;
- Keyinfo ki;
- Attr *attr;
- if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
- return failure(fss, nil);
- s = emalloc(sizeof *s);
- 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;
- }
- }
- 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((uint8_t*)s->t.key, s->secret);
- fss->ai.secret = s->secret;
- fss->ai.nsecret = 8;
- fss->haveai = 1;
- fss->phase = Established;
- return RpcOk;
- }
- }
- 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);
- else
- safecpy(s->tr.uid, s->tr.hostid, sizeof s->tr.uid);
- convTR2M(&s->tr, trbuf);
- /* get tickets, from auth server or invent if we can */
- if(gettickets(s, trbuf, tbuf) < 0){
- _freeattr(attr);
- return failure(fss, nil);
- }
- 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);
- }
- }
- 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((uint8_t*)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;
- }
- 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;
- }
- static int
- hexparse(char *hex, uint8_t *dat, int ndat)
- {
- int i;
- if(strlen(hex) != 2*ndat)
- return -1;
- if(hex[strspn(hex, "0123456789abcdefABCDEF")] != '\0')
- return -1;
- for(i=0; i<ndat; i++)
- dat[i] = (unhex(hex[2*i])<<4)|unhex(hex[2*i+1]);
- return 0;
- }
- static int
- p9skaddkey(Key *k, int before)
- {
- char *s;
- k->priv = emalloc(DESKEYLEN);
- if((s = _strfindattr(k->privattr, "!hex")) != nil){
- 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")) != nil){
- passtokey((char*)k->priv, s);
- }else{
- werrstr("no key data");
- free(k->priv);
- k->priv = nil;
- return -1;
- }
- return replacekey(k, before);
- }
- static void
- p9skclosekey(Key *k)
- {
- free(k->priv);
- }
- static int
- getastickets(State *s, char *trbuf, char *tbuf)
- {
- int asfd, rv;
- char *dom;
- if((dom = _strfindattr(s->key->attr, "dom")) == nil){
- werrstr("auth key has no domain");
- return -1;
- }
- asfd = _authdial(nil, dom);
- if(asfd < 0)
- return -1;
- rv = _asgetticket(asfd, trbuf, tbuf);
- close(asfd);
- return rv;
- }
- static int
- mkserverticket(State *s, char *tbuf)
- {
- 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));
- 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);
- t.num = AuthTs;
- convT2M(&t, tbuf+TICKETLEN, s->key->priv);
- return 0;
- }
- static int
- gettickets(State *s, char *trbuf, char *tbuf)
- {
- /*
- if(mktickets(s, trbuf, tbuf) >= 0)
- return 0;
- */
- if(getastickets(s, trbuf, tbuf) >= 0)
- return 0;
- return mkserverticket(s, tbuf);
- }
- Proto p9sk1 = {
- .name= "p9sk1",
- .init= p9skinit,
- .write= p9skwrite,
- .read= p9skread,
- .close= p9skclose,
- .addkey= p9skaddkey,
- .closekey= p9skclosekey,
- .keyprompt= "user? !password?"
- };
- Proto p9sk2 = {
- .name= "p9sk2",
- .init= p9skinit,
- .write= p9skwrite,
- .read= p9skread,
- .close= p9skclose,
- };
|