summaryrefslogtreecommitdiff
path: root/appl/cmd/auth/factotum/proto/p9any.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/auth/factotum/proto/p9any.b')
-rw-r--r--appl/cmd/auth/factotum/proto/p9any.b232
1 files changed, 232 insertions, 0 deletions
diff --git a/appl/cmd/auth/factotum/proto/p9any.b b/appl/cmd/auth/factotum/proto/p9any.b
new file mode 100644
index 00000000..1668a701
--- /dev/null
+++ b/appl/cmd/auth/factotum/proto/p9any.b
@@ -0,0 +1,232 @@
+implement Authproto;
+
+# currently includes p9sk1
+
+include "sys.m";
+ sys: Sys;
+ Rread, Rwrite: import Sys;
+
+include "draw.m";
+
+include "keyring.m";
+ kr: Keyring;
+
+include "auth9.m";
+ auth9: Auth9;
+ ANAMELEN, AERRLEN, DOMLEN, DESKEYLEN, CHALLEN, SECRETLEN: import Auth9;
+ TICKREQLEN, TICKETLEN, AUTHENTLEN: import Auth9;
+ Ticketreq, Ticket, Authenticator: import auth9;
+
+include "../authio.m";
+ authio: Authio;
+ Aattr, Aval, Aquery: import Authio;
+ Attr, IO, Key, Authinfo: import authio;
+ netmkaddr, eqbytes, memrandom: import authio;
+
+include "encoding.m";
+ base16: Encoding;
+
+Debug: con 0;
+
+# init, addkey, closekey, write, read, close, keyprompt
+
+init(f: Authio): string
+{
+ authio = f;
+ sys = load Sys Sys->PATH;
+ kr = load Keyring Keyring->PATH;
+ auth9 = load Auth9 Auth9->PATH;
+ auth9->init();
+ base16 = load Encoding Encoding->BASE16PATH;
+ return nil;
+}
+
+version := 1;
+
+interaction(attrs: list of ref Attr, io: ref IO): string
+{
+ return p9any(io);
+}
+
+p9any(io: ref IO): string
+{
+ while((buf := io.read()) == nil || (n := len buf) == 0 || buf[n-1] != byte 0)
+ io.toosmall(2048);
+ s := string buf[0:n-1];
+ if(Debug)
+ sys->print("s: %q\n", s);
+ (nil, flds) := sys->tokenize(s, " \t");
+ if(flds != nil && len hd flds >= 2 && (hd flds)[0:2] == "v."){
+ if(hd flds == "v.2"){
+ version = 2;
+ flds = tl flds;
+ if(Debug)
+ sys->print("version 2\n");
+ }else
+ return "p9any: unknown version";
+ }
+ doms: list of string;
+ for(; flds != nil; flds = tl flds){
+ (nf, subf) := sys->tokenize(hd flds, "@");
+ if(nf == 2 && hd subf == "p9sk1")
+ doms = hd tl subf :: doms;
+ }
+ if(doms == nil)
+ return "p9any: unsupported protocol";
+ if(Debug){
+ for(l := doms; l != nil; l = tl l)
+ sys->print("dom: %q\n", hd l);
+ }
+ r := array of byte ("p9sk1 "+hd doms);
+ buf[0:] = r;
+ buf[len r] = byte 0;
+ io.write(buf, len r + 1);
+ if(version == 2){
+ b := io.readn(3);
+ if(b == nil || b[0] != byte 'O' || b[1] != byte 'K' || b[2] != byte 0)
+ return "p9any: AS protocol botch: not OK";
+ if(Debug)
+ sys->print("OK\n");
+ }
+ return p9sk1client(io, hd doms);
+}
+
+#p9sk1:
+# C->S: nonce-C
+# S->C: nonce-S, uid-S, domain-S
+# C->A: nonce-S, uid-S, domain-S, uid-C, factotum-C
+# A->C: Kc{nonce-S, uid-C, uid-S, Kn}, Ks{nonce-S, uid-C, uid-S, K-n}
+# C->S: Ks{nonce-S, uid-C, uid-S, K-n}, Kn{nonce-S, counter}
+# S->C: Kn{nonce-C, counter}
+
+#asserts that uid-S and uid-C share new secret Kn
+#increment the counter to reuse the ticket.
+
+p9sk1client(io: ref IO, udom: string): string
+{
+
+ # C->S: nonce-C
+ cchal := array[CHALLEN] of byte;
+ memrandom(cchal, CHALLEN);
+ if(io.write(cchal, len cchal) != len cchal)
+ return sys->sprint("p9sk1: can't write cchal: %r");
+
+ # S->C: nonce-S, uid-S, domain-S
+ trbuf := io.readn(TICKREQLEN);
+ if(trbuf == nil)
+ return sys->sprint("p9sk1: can't read ticketreq: %r");
+
+ (nil, tr) := Ticketreq.unpack(trbuf);
+ if(tr == nil)
+ return "p9sk1: can't unpack ticket request";
+ if(Debug)
+ sys->print("ticketreq: type=%d authid=%q authdom=%q chal= hostid=%q uid=%q\n",
+ tr.rtype, tr.authid, tr.authdom, tr.hostid, tr.uid);
+
+ (mykey, diag) := io.findkey(nil, sys->sprint("dom=%q proto=p9sk1 user? !password?", udom));
+ if(mykey == nil)
+ return "can't find key: "+diag;
+ ukey: array of byte;
+ if((a := authio->lookattrval(mykey.secrets, "!hex")) != nil){
+ ukey = base16->dec(a);
+ if(len ukey != DESKEYLEN)
+ return "p9sk1: invalid !hex key";
+ }else if((a = authio->lookattrval(mykey.secrets, "!password")) != nil)
+ ukey = auth9->passtokey(a);
+ else
+ return "no !password (or !hex) in key";
+
+ # A->C: Kc{nonce-S, uid-C, uid-S, Kn}, Ks{nonce-S, uid-C, uid-S, K-n}
+ user := authio->lookattrval(mykey.attrs, "user");
+ if(user == nil)
+ user = authio->user(); # shouldn't happen
+ tr.rtype = Auth9->AuthTreq;
+ tr.hostid = user;
+ tr.uid = tr.hostid; # not speaking for anyone else
+ (tick, serverbits) := getastickets(tr, ukey);
+ if(tick == nil)
+ return sys->sprint("p9sk1: getasticket failed: %r");
+ if(tick.num != Auth9->AuthTc)
+ return "p9sk1: getasticket: failed: wrong key?";
+ if(Debug)
+ sys->print("ticket: num=%d chal= cuid=%q suid=%q key=\n", tick.num, tick.cuid, tick.suid);
+
+ # C->S: Ks{nonce-S, uid-C, uid-S, K-n}, Kn{nonce-S, counter}
+ ar := ref Authenticator;
+ ar.num = Auth9->AuthAc;
+ ar.chal = tick.chal;
+ ar.id = 0;
+ obuf := array[TICKETLEN+AUTHENTLEN] of byte;
+ obuf[0:] = serverbits;
+ obuf[TICKETLEN:] = ar.pack(tick.key);
+ if(io.write(obuf, len obuf) != len obuf)
+ return "p9sk1: error writing authenticator: %r";
+
+ # S->C: Kn{nonce-C, counter}
+ sbuf := io.readn(AUTHENTLEN);
+ if(sbuf == nil)
+ return sys->sprint("p9sk1: can't read server's authenticator: %r");
+ (nil, ar) = Authenticator.unpack(sbuf, tick.key);
+ if(ar.num != Auth9->AuthAs || !eqbytes(ar.chal, cchal) || ar.id != 0)
+ return "invalid authenticator from server";
+
+ ai := ref Authinfo(tick.cuid, tick.suid, nil, auth9->des56to64(tick.key));
+ io.done(ai);
+
+ return nil;
+}
+
+getastickets(tr: ref Ticketreq, key: array of byte): (ref Ticket, array of byte)
+{
+ afd := authdial(nil, tr.authdom);
+ if(afd == nil)
+ return (nil, nil);
+ return auth9->_asgetticket(afd, tr, key);
+}
+
+#
+# where to put the following functions?
+#
+
+csgetvalue(netroot: string, keytag: string, keyval: string, needtag: string): string
+{
+ cs := "/net/cs";
+ if(netroot != nil)
+ cs = netroot+"/cs";
+ fd := sys->open(cs, Sys->ORDWR); # TO DO: choice of root
+ if(fd == nil)
+ return nil;
+ if(sys->fprint(fd, "!%s=%s %s=*", keytag, keyval, needtag) < 0)
+ return nil;
+ sys->seek(fd, big 0, 0);
+ buf := array[1024] of byte;
+ while((n := sys->read(fd, buf, len buf)) > 0){
+ al := authio->parseline(string buf[0:n]); # assume the conventions match factotum's
+ for(; al != nil; al = tl al)
+ if((hd al).name == needtag)
+ return (hd al).val;
+ }
+ return nil;
+}
+
+authdial(netroot: string, dom: string): ref Sys->FD
+{
+ p: string;
+ if(dom != nil){
+ # look up an auth server in an authentication domain
+ p = csgetvalue(netroot, "authdom", dom, "auth");
+
+ # if that didn't work, just try the IP domain
+ if(p == nil)
+ p = csgetvalue(netroot, "dom", dom, "auth");
+ if(p == nil)
+ p = "$auth"; # temporary ...
+ if(p == nil){
+ sys->werrstr("no auth server found for "+dom);
+ return nil;
+ }
+ }else
+ p = "$auth"; # look for one relative to my machine
+ (nil, conn) := sys->dial(netmkaddr(p, netroot, "ticket"), nil);
+ return conn.dfd;
+}