diff options
Diffstat (limited to 'appl/cmd/auth/factotum/proto/p9any.b')
| -rw-r--r-- | appl/cmd/auth/factotum/proto/p9any.b | 232 |
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; +} |
