From 6e527fbc4d8f404a7eec934e5c9efaaaa92ffdff Mon Sep 17 00:00:00 2001 From: rsc Date: Sun, 13 Feb 2005 05:59:29 +0000 Subject: new auth --- src/cmd/auth/factotum/p9sk1.c | 353 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 src/cmd/auth/factotum/p9sk1.c (limited to 'src/cmd/auth/factotum/p9sk1.c') diff --git a/src/cmd/auth/factotum/p9sk1.c b/src/cmd/auth/factotum/p9sk1.c new file mode 100644 index 00000000..92b055d0 --- /dev/null +++ b/src/cmd/auth/factotum/p9sk1.c @@ -0,0 +1,353 @@ +/* + * 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 "std.h" +#include "dat.h" + +extern Proto p9sk1, p9sk2; +static int gettickets(Ticketreq*, char*, Key*); + +#define max(a, b) ((a) > (b) ? (a) : (b)) +enum +{ + MAXAUTH = max(TICKREQLEN, TICKETLEN+max(TICKETLEN, AUTHENTLEN)) +}; + +static int +p9skclient(Conv *c) +{ + char *user; + char cchal[CHALLEN]; + uchar secret[8]; + char buf[MAXAUTH]; + int speakfor, ret; + Attr *a; + Authenticator au; + Key *k; + Ticket t; + Ticketreq tr; + + 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; + } + + /* 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; + } + + 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 + strcpy(tr.uid, tr.hostid); + + c->state = "get tickets"; + if(gettickets(&tr, buf, k) < 0) + goto out; + + convM2T(buf, &t, k->priv); + if(t.num == AuthTc) + break; + + /* 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; + } + } + + /* 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; + } + + /* success */ + c->attr = addcap(c->attr, c->sysuser, &t); + des56to64((uchar*)t.key, secret); + c->attr = addattr(c->attr, "secret=%.8H", secret); + ret = 0; + +out: + freeattr(a); + keyclose(k); + return ret; +} + +static int +p9skserver(Conv *c) +{ + char cchal[CHALLEN], buf[MAXAUTH]; + uchar secret[8]; + int ret; + Attr *a; + Authenticator au; + Key *k; + Ticketreq tr; + Ticket t; + + ret = -1; + + a = addattr(copyattr(c->attr), "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; + } + + /* 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; + } + + 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); + des56to64((uchar*)t.key, secret); + c->attr = addattr(c->attr, "secret=%.8H", secret); + ret = 0; + +out: + freeattr(a); + keyclose(k); + return ret; +} + +int +_asgetticket(int fd, char *trbuf, char *tbuf) +{ + if(write(fd, trbuf, TICKREQLEN) < 0){ + close(fd); + return -1; + } + return _asrdresp(fd, tbuf, 2*TICKETLEN); +} +static int +getastickets(Ticketreq *tr, char *buf) +{ + int asfd; + int ret; + + if((asfd = xioauthdial(nil, tr->authdom)) < 0) + return -1; + convTR2M(tr, buf); + ret = xioasgetticket(asfd, buf, buf); + xioclose(asfd); + return ret; +} + +static int +mktickets(Ticketreq *tr, char *buf, Key *k) +{ + Ticket t; + + if(strcmp(tr->authid, tr->hostid) != 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, buf, k->priv); + t.num = AuthTs; + convT2M(&t, buf+TICKETLEN, k->priv); + return 0; +} + +static int +gettickets(Ticketreq *tr, char *buf, Key *k) +{ + if(getastickets(tr, buf) == 0) + return 0; + if(mktickets(tr, buf, k) == 0) + return 0; + 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, "!password")) + passtokey(k->priv, pass); + else if(pass = strfindattr(k->privattr, "!hex")){ + if(hexparse(pass, k->priv, 7) < 0){ + werrstr("malformed !hex key data"); + return -1; + } + }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", +.roles= p9sk1roles, +.checkkey= p9sk1check, +.closekey= p9sk1close, +.keyprompt= "user? dom? !password?", +}; + +Proto p9sk2 = { +.name= "p9sk2", +.roles= p9sk2roles, +}; + -- cgit v1.2.3