summaryrefslogtreecommitdiff
path: root/appl/lib/factotum.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/factotum.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/lib/factotum.b')
-rw-r--r--appl/lib/factotum.b308
1 files changed, 308 insertions, 0 deletions
diff --git a/appl/lib/factotum.b b/appl/lib/factotum.b
new file mode 100644
index 00000000..a2cec879
--- /dev/null
+++ b/appl/lib/factotum.b
@@ -0,0 +1,308 @@
+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<n && buf[i] != byte ' '; i++)
+ ;
+ s := string buf[0:i];
+ if(i < n)
+ i++;
+ buf = buf[i:];
+ case s {
+ "ok" or "error" or "done" or "phase" or
+ "protocol" or "needkey" or "toosmall" or "internal" =>
+ 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);
+}