aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/auth/factotum/p9sk1.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/auth/factotum/p9sk1.c')
-rw-r--r--src/cmd/auth/factotum/p9sk1.c353
1 files changed, 353 insertions, 0 deletions
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,
+};
+