implement Factotum; # # client interface to factotum # # this is a near transliteration of Plan 9 code, subject to the Lucent Public License 1.02 # include "sys.m"; sys: Sys; include "string.m"; include "factotum.m"; debug := 0; init() { sys = load Sys Sys->PATH; } setdebug(i: int) { debug = i; } getaia(a: array of byte, n: int): (int, array of byte) { if(len a - n < 2) return (-1, nil); c := (int a[n+1]<<8) | int a[n+0]; n += 2; if(len a - n < c) return (-1, nil); b := array[c] of byte; # could avoid copy if known not to alias b[0:] = a[n: n+c]; return (n+c, b); } getais(a: array of byte, n: int): (int, string) { (n, a) = getaia(a, n); return (n, string a); } Authinfo.unpack(a: array of byte): (int, ref Authinfo) { ai := ref Authinfo; n: int; (n, ai.cuid) = getais(a, 0); (n, ai.suid) = getais(a, n); (n, ai.cap) = getais(a, n); (n, ai.secret) = getaia(a, n); if(n < 0) return (-1, nil); return (n, ai); } mount(fd: ref Sys->FD, mnt: string, flags: int, aname: string, keyspec: string): (int, ref Authinfo) { ai: ref Authinfo; afd := sys->fauth(fd, aname); if(afd != nil){ ai = proxy(afd, sys->open("/mnt/factotum/rpc", Sys->ORDWR), "proto=p9any role=client "+keyspec); if(debug && ai == nil){ sys->print("proxy failed: %r\n"); return (-1, nil); } } return (sys->mount(fd, afd, mnt, flags, aname), ai); } dump(a: array of byte): string { s := sys->sprint("[%d]", len a); for(i := 0; i < len a; i++){ c := int a[i]; if(c >= ' ' && c <= 16r7E) s += sys->sprint("%c", c); else s += sys->sprint("\\x%.2ux", c); } return s; } verbof(buf: array of byte): (string, array of byte) { n := len buf; for(i:=0; i return (s, buf); * => sys->werrstr(sys->sprint("malformed rpc response: %q", s)); return ("rpc failure", buf); } } dorpc(fd: ref Sys->FD, verb: string, val: array of byte): (string, array of byte) { (o, a) := rpc(fd, verb, val); if(o != "needkey" && o != "badkey") return (o, a); return ("no key", a); # don't know how to get key } rpc(afd: ref Sys->FD, verb: string, a: array of byte): (string, array of byte) { va := array of byte verb; l := len va; na := len a; if(na+l+1 > AuthRpcMax){ sys->werrstr("rpc too big"); return ("toobig", nil); } buf := array[na+l+1] of byte; buf[0:] = va; buf[l] = byte ' '; buf[l+1:] = a; if(debug) sys->print("rpc: ->%s %s\n", verb, dump(a)); if((n:=sys->write(afd, buf, len buf)) != len buf){ if(n >= 0) sys->werrstr("rpc short write"); return ("rpc failure", nil); } buf = array[AuthRpcMax] of byte; if((n=sys->read(afd, buf, len buf)) < 0){ if(debug) sys->print("<- (readerr) %r\n"); return ("rpc failure", nil); } if(n < len buf) buf[n] = byte 0; buf = buf[0:n]; # # Set error string for good default behavior. # s: string; (t, r) := verbof(buf); if(debug) sys->print("<- %s %#q\n", t, dump(r)); case t { "ok" or "rpc failure" => ; # don't touch "error" => if(len r == 0) s = "unspecified rpc error"; else s = sys->sprint("%s", string r); "needkey" => s = sys->sprint("needkey %s", string r); "badkey" => (nf, flds) := sys->tokenize(string r, "\n"); if(nf < 2) s = sys->sprint("badkey %q", string r); else s = sys->sprint("badkey %q", hd tl flds); break; "phase" => s = sys->sprint("phase error: %q", string r); * => s = sys->sprint("unknown rpc type %q (bug in rpc.c)", t); } if(s != nil) sys->werrstr(s); return (t, r); } Authinfo.read(fd: ref Sys->FD): ref Authinfo { (o, a) := rpc(fd, "authinfo", nil); if(o != "ok") return nil; (n, ai) := Authinfo.unpack(a); if(n <= 0) sys->werrstr("bad auth info from factotum"); return ai; } proxy(fd: ref Sys->FD, afd: ref Sys->FD, params: string): ref Authinfo { readc := chan of (array of byte, chan of (int, string)); writec := chan of (array of byte, chan of (int, string)); donec := chan of (ref Authinfo, string); spawn genproxy(readc, writec, donec, afd, params); for(;;)alt{ (buf, reply) := <-readc => n := sys->read(fd, buf, len buf); if(n == -1) reply <-= (-1, sys->sprint("%r")); else reply <-= (n, nil); (buf, reply) := <-writec => n := sys->write(fd, buf, len buf); if(n == -1) reply <-= (-1, sys->sprint("%r")); else reply <-= (n, nil); (authinfo, err) := <-donec => if(authinfo == nil) sys->werrstr(err); return authinfo; } } # # do what factotum says # genproxy( readc: chan of (array of byte, chan of (int, string)), writec: chan of (array of byte, chan of (int, string)), donec: chan of (ref Authinfo, string), afd: ref Sys->FD, params: string) { if(afd == nil){ donec <-= (nil, "no authentication fd"); return; } pa := array of byte params; (o, a) := dorpc(afd, "start", pa); if(o != "ok"){ donec <-= (nil, sys->sprint("proxy start: %r")); return; } ai: ref Authinfo; err: string; done: for(;;){ (o, a) = dorpc(afd, "read", nil); case o { "done" => if(len a > 0 && a[0] == byte 'h' && string a == "haveai") ai = Authinfo.read(afd); else ai = ref Authinfo; # auth succeeded but empty authinfo break done; "ok" => writec <-= (a[0:len a], reply := chan of (int, string)); (n, e) := <-reply; if(n != len a){ err = "proxy write fd: "+e; break done; } "phase" => buf := array[AuthRpcMax] of {* => byte 0}; n := 0; for(;;){ (o, a) = dorpc(afd, "write", buf[0:n]); if(o != "toosmall") break; c := int string a; if(c > AuthRpcMax) break; readc <-= (buf[n:c], reply := chan of (int, string)); (m, e) := <-reply; if(m <= 0){ err = e; if(m == 0) err = sys->sprint("proxy short read"); break done; } n += m; } if(o != "ok"){ err = sys->sprint("proxy rpc write: %r"); break done; } * => err = sys->sprint("proxy rpc: %r"); break done; } } donec <-= (ai, err); } getuserpasswd(keyspec: string): (string, string) { str := load String String->PATH; if(str == nil) return (nil, nil); fd := sys->open("/mnt/factotum/rpc", Sys->ORDWR); if(fd == nil) return (nil, nil); if(((o, a) := dorpc(fd, "start", array of byte keyspec)).t0 != "ok" || ((o, a) = dorpc(fd, "read", nil)).t0 != "ok"){ sys->werrstr("factotum: "+o); return (nil, nil); } flds := str->unquoted(string a); if(len flds != 2){ sys->werrstr("odd response from factotum"); return (nil, nil); } return (hd flds, hd tl flds); }