aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-03-21 04:33:13 +0000
committerrsc <devnull@localhost>2004-03-21 04:33:13 +0000
commit2277c5d7bbe1f9595fad512d8f790708473a9bf1 (patch)
tree4d653e13906f1971d3170dba6dbe0fbf92eb48d6 /src/cmd
parenta770daa795754cb600ad3fab2fdd2961147006c4 (diff)
downloadplan9port-2277c5d7bbe1f9595fad512d8f790708473a9bf1.tar.gz
plan9port-2277c5d7bbe1f9595fad512d8f790708473a9bf1.tar.bz2
plan9port-2277c5d7bbe1f9595fad512d8f790708473a9bf1.zip
Small tweaks
Lots of new code imported.
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/acme/acme.c9
-rw-r--r--src/cmd/acme/dat.h2
-rw-r--r--src/cmd/acme/elog.c2
-rw-r--r--src/cmd/acme/exec.c1
-rw-r--r--src/cmd/acme/fns.h1
-rw-r--r--src/cmd/acme/look.c39
-rw-r--r--src/cmd/factotum/BUGS1
-rw-r--r--src/cmd/factotum/apop.c348
-rw-r--r--src/cmd/factotum/attr.c228
-rw-r--r--src/cmd/factotum/chap.c424
-rw-r--r--src/cmd/factotum/confirm.c139
-rw-r--r--src/cmd/factotum/conv.c254
-rw-r--r--src/cmd/factotum/cpu.c1117
-rw-r--r--src/cmd/factotum/ctl.c158
-rw-r--r--src/cmd/factotum/dat.h224
-rw-r--r--src/cmd/factotum/fs.acid1686
-rw-r--r--src/cmd/factotum/fs.c524
-rwxr-xr-xsrc/cmd/factotum/guide3
-rw-r--r--src/cmd/factotum/guide26
-rw-r--r--src/cmd/factotum/key.c190
-rw-r--r--src/cmd/factotum/log.c121
-rw-r--r--src/cmd/factotum/main.c163
-rw-r--r--src/cmd/factotum/mkfile34
-rw-r--r--src/cmd/factotum/p9any.c266
-rw-r--r--src/cmd/factotum/p9cr.c545
-rw-r--r--src/cmd/factotum/p9sk1.c352
-rw-r--r--src/cmd/factotum/pass.c100
-rw-r--r--src/cmd/factotum/plan9.c189
-rw-r--r--src/cmd/factotum/privattr0
-rw-r--r--src/cmd/factotum/proto.c22
-rw-r--r--src/cmd/factotum/rpc.c315
-rw-r--r--src/cmd/factotum/ssh.c135
-rw-r--r--src/cmd/factotum/sshrsa.c172
-rw-r--r--src/cmd/factotum/std.h10
-rw-r--r--src/cmd/factotum/test.c121
-rwxr-xr-xsrc/cmd/factotum/testsetup11
-rw-r--r--src/cmd/factotum/util.c52
-rw-r--r--src/cmd/factotum/x.c15
-rw-r--r--src/cmd/factotum/xio.c165
-rw-r--r--src/cmd/mkfile2
-rw-r--r--src/cmd/plumb/plumber.c2
-rw-r--r--src/cmd/rc/plan9ish.c2
-rw-r--r--src/cmd/rc/rc.h3
-rw-r--r--src/cmd/samterm/plan9.c36
-rw-r--r--src/cmd/vac/vac.c28
45 files changed, 8181 insertions, 36 deletions
diff --git a/src/cmd/acme/acme.c b/src/cmd/acme/acme.c
index fbfb9f14..d4b4cad8 100644
--- a/src/cmd/acme/acme.c
+++ b/src/cmd/acme/acme.c
@@ -27,8 +27,6 @@ char wdir[512] = ".";
Reffont *reffonts[2];
int snarffd = -1;
int mainpid;
-int plumbsendfd;
-int plumbeditfd;
enum{
NSnarf = 1000 /* less than 1024, I/O buffer size */
@@ -180,6 +178,8 @@ threadmain(int argc, char *argv[])
exits("keyboard");
}
mainpid = getpid();
+ startplumbing();
+/*
plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
if(plumbeditfd < 0)
fprint(2, "acme: can't initialize plumber: %r\n");
@@ -188,6 +188,7 @@ threadmain(int argc, char *argv[])
threadcreate(plumbproc, nil, STACK);
}
plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
+*/
fsysinit();
@@ -355,6 +356,7 @@ acmeerrorinit(void)
threadcreate(acmeerrorproc, nil, STACK);
}
+/*
void
plumbproc(void *v)
{
@@ -369,6 +371,7 @@ plumbproc(void *v)
sendp(cplumb, m);
}
}
+*/
void
keyboardthread(void *v)
@@ -674,7 +677,7 @@ waitthread(void *v)
textsetselect(t, 0, 0);
}
if(w->msg[0])
- warning(c->md, "%s: %s\n", c->name, w->msg);
+ warning(c->md, "%S: %s\n", c->name, w->msg);
flushimage(display, 1);
}
qunlock(&row.lk);
diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h
index 9101ca34..94cfa383 100644
--- a/src/cmd/acme/dat.h
+++ b/src/cmd/acme/dat.h
@@ -524,8 +524,6 @@ char *home;
char *fontnames[2];
Image *tagcols[NCOL];
Image *textcols[NCOL];
-int plumbsendfd;
-int plumbeditfd;
extern char wdir[]; /* must use extern because no dimension given */
int editing;
int erroutfd;
diff --git a/src/cmd/acme/elog.c b/src/cmd/acme/elog.c
index e86af6ec..d7c9a9b0 100644
--- a/src/cmd/acme/elog.c
+++ b/src/cmd/acme/elog.c
@@ -170,7 +170,7 @@ eloginsert(File *f, int q0, Rune *r, int nr)
elogflush(f);
}
/* try to merge with previous */
- if(f->elog.type==Insert && q0==f->elog.q0 && (q0+nr)-f->elog.q0<Maxstring){
+ if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){
runemove(f->elog.r+f->elog.nr, r, nr);
f->elog.nr += nr;
return;
diff --git a/src/cmd/acme/exec.c b/src/cmd/acme/exec.c
index d81153ec..74f9f47c 100644
--- a/src/cmd/acme/exec.c
+++ b/src/cmd/acme/exec.c
@@ -1472,6 +1472,7 @@ Hard:
}
}
threadexecl(cpid, sfd, "rc", "rc", "-c", t, nil);
+ warning(nil, "exec rc: %r\n");
Fail:
/* threadexec hasn't happened, so send a zero */
diff --git a/src/cmd/acme/fns.h b/src/cmd/acme/fns.h
index a73a7ec0..9fba7d7a 100644
--- a/src/cmd/acme/fns.h
+++ b/src/cmd/acme/fns.h
@@ -87,6 +87,7 @@ Rune* skipbl(Rune*, int, int*);
Rune* findbl(Rune*, int, int*);
char* edittext(Window*, int, Rune*, int);
void flushwarnings(int);
+void startplumbing(void);
#define runemalloc(a) (Rune*)emalloc((a)*sizeof(Rune))
#define runerealloc(a, b) (Rune*)erealloc((a), (b)*sizeof(Rune))
diff --git a/src/cmd/acme/look.c b/src/cmd/acme/look.c
index 6b259288..f6c4d4ee 100644
--- a/src/cmd/acme/look.c
+++ b/src/cmd/acme/look.c
@@ -8,15 +8,50 @@
#include <frame.h>
#include <fcall.h>
#include <regexp.h>
+#define Fid FsFid
+#include <fs.h>
#include <plumb.h>
+#undef Fid
#include "dat.h"
#include "fns.h"
+FsFid *plumbsendfid;
+FsFid *plumbeditfid;
+
Window* openfile(Text*, Expand*);
int nuntitled;
void
+plumbproc(void *v)
+{
+ Plumbmsg *m;
+
+ USED(v);
+ threadsetname("plumbproc");
+ for(;;){
+ m = plumbrecvfid(plumbeditfid);
+ if(m == nil)
+ threadexits(nil);
+ sendp(cplumb, m);
+ }
+}
+
+void
+startplumbing(void)
+{
+ plumbeditfid = plumbopenfid("edit", OREAD|OCEXEC);
+ if(plumbeditfid == nil)
+ fprint(2, "acme: can't initialize plumber: %r\n");
+ else{
+ cplumb = chancreate(sizeof(Plumbmsg*), 0);
+ threadcreate(plumbproc, nil, STACK);
+ }
+ plumbsendfid = plumbopenfid("send", OWRITE|OCEXEC);
+}
+
+
+void
look3(Text *t, uint q0, uint q1, int external)
{
int n, c, f, expanded;
@@ -79,7 +114,7 @@ look3(Text *t, uint q0, uint q1, int external)
free(r);
goto Return;
}
- if(plumbsendfd >= 0){
+ if(plumbsendfid != nil){
/* send whitespace-delimited word to plumber */
m = emalloc(sizeof(Plumbmsg));
m->src = estrdup("acme");
@@ -121,7 +156,7 @@ look3(Text *t, uint q0, uint q1, int external)
m->data = runetobyte(r, q1-q0);
m->ndata = strlen(m->data);
free(r);
- if(m->ndata<messagesize-1024 && plumbsend(plumbsendfd, m) >= 0){
+ if(m->ndata<messagesize-1024 && plumbsendtofid(plumbsendfid, m) >= 0){
plumbfree(m);
goto Return;
}
diff --git a/src/cmd/factotum/BUGS b/src/cmd/factotum/BUGS
new file mode 100644
index 00000000..7aed917f
--- /dev/null
+++ b/src/cmd/factotum/BUGS
@@ -0,0 +1 @@
+key, delkey, wipe should be in ctl not rpc.
diff --git a/src/cmd/factotum/apop.c b/src/cmd/factotum/apop.c
new file mode 100644
index 00000000..f555c394
--- /dev/null
+++ b/src/cmd/factotum/apop.c
@@ -0,0 +1,348 @@
+/*
+ * APOP, CRAM - MD5 challenge/response authentication
+ *
+ * The client does not authenticate the server, hence no CAI.
+ *
+ * Protocol:
+ *
+ * S -> C: random@domain
+ * C -> S: hex-response
+ * S -> C: ok
+ *
+ * Note that this is the protocol between factotum and the local
+ * program, not between the two factotums. The information
+ * exchanged here is wrapped in the APOP protocol by the local
+ * programs.
+ *
+ * If S sends "bad [msg]" instead of "ok", that is a hint that the key is bad.
+ * The protocol goes back to "C -> S: user".
+ */
+
+#include "std.h"
+#include "dat.h"
+
+static int
+apopcheck(Key *k)
+{
+ if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
+ werrstr("need user and !password attributes");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+apopclient(Conv *c)
+{
+ char *chal, *pw, *res;
+ int astype, nchal, npw, ntry, ret;
+ uchar resp[MD5dlen];
+ Attr *attr;
+ DigestState *ds;
+ Key *k;
+
+ chal = nil;
+ k = nil;
+ res = nil;
+ ret = -1;
+ attr = c->attr;
+
+ if(c->proto == &apop)
+ astype = AuthApop;
+ else if(c->proto == &cram)
+ astype = AuthCram;
+ else{
+ werrstr("bad proto");
+ goto out;
+ }
+
+ c->state = "find key";
+ k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
+ if(k == nil)
+ goto out;
+
+ c->state = "read challenge";
+ if((nchal = convreadm(c, &chal)) < 0)
+ goto out;
+
+ for(ntry=1;; ntry++){
+ if(c->attr != attr)
+ freeattr(c->attr);
+ c->attr = addattrs(copyattr(attr), k->attr);
+ if((pw = strfindattr(k->privattr, "!password")) == nil){
+ werrstr("key has no password (cannot happen?)");
+ goto out;
+ }
+ npw = strlen(pw);
+
+ switch(astype){
+ case AuthApop:
+ ds = md5((uchar*)chal, nchal, nil, nil);
+ md5((uchar*)pw, npw, resp, ds);
+ break;
+ case AuthCram:
+ hmac_md5((uchar*)chal, nchal, (uchar*)pw, npw, resp, nil);
+ break;
+ }
+
+ /* C->S: APOP user hex-response\n */
+ if(ntry == 1)
+ c->state = "write user";
+ else{
+ sprint(c->statebuf, "write user (auth attempt #%d)", ntry);
+ c->state = c->statebuf;
+ }
+ if(convprint(c, "%s", strfindattr(k->attr, "user")) < 0)
+ goto out;
+
+ c->state = "write response";
+ if(convprint(c, "%.*H", sizeof resp, resp) < 0)
+ goto out;
+
+ c->state = "read result";
+ if(convreadm(c, &res) < 0)
+ goto out;
+
+ if(strcmp(res, "ok") == 0)
+ break;
+
+ if(strncmp(res, "bad ", 4) != 0){
+ werrstr("bad result: %s", res);
+ goto out;
+ }
+
+ c->state = "replace key";
+ if((k = keyreplace(c, k, "%s", res+4)) == nil){
+ c->state = "auth failed";
+ werrstr("%s", res+4);
+ goto out;
+ }
+ free(res);
+ res = nil;
+ }
+
+ werrstr("succeeded");
+ ret = 0;
+
+out:
+ keyclose(k);
+ free(chal);
+ if(c->attr != attr)
+ freeattr(attr);
+ return ret;
+}
+
+/* shared with auth dialing routines */
+typedef struct ServerState ServerState;
+struct ServerState
+{
+ int asfd;
+ Key *k;
+ Ticketreq tr;
+ Ticket t;
+ char *dom;
+ char *hostid;
+};
+
+enum
+{
+ APOPCHALLEN = 128,
+};
+
+static int apopchal(ServerState*, int, char[APOPCHALLEN]);
+static int apopresp(ServerState*, char*, char*);
+
+static int
+apopserver(Conv *c)
+{
+ char chal[APOPCHALLEN], *user, *resp;
+ ServerState s;
+ int astype, ret;
+ Attr *a;
+
+ ret = -1;
+ user = nil;
+ resp = nil;
+ memset(&s, 0, sizeof s);
+ s.asfd = -1;
+
+ if(c->proto == &apop)
+ astype = AuthApop;
+ else if(c->proto == &cram)
+ astype = AuthCram;
+ else{
+ werrstr("bad proto");
+ goto out;
+ }
+
+ c->state = "find key";
+ if((s.k = plan9authkey(c->attr)) == nil)
+ goto out;
+
+ a = copyattr(s.k->attr);
+ a = delattr(a, "proto");
+ c->attr = addattrs(c->attr, a);
+ freeattr(a);
+
+ c->state = "authdial";
+ s.hostid = strfindattr(s.k->attr, "user");
+ s.dom = strfindattr(s.k->attr, "dom");
+ if((s.asfd = xioauthdial(nil, s.dom)) < 0){
+ werrstr("authdial %s: %r", s.dom);
+ goto out;
+ }
+
+ c->state = "authchal";
+ if(apopchal(&s, astype, chal) < 0)
+ goto out;
+
+ c->state = "write challenge";
+ if(convprint(c, "%s", chal) < 0)
+ goto out;
+
+ for(;;){
+ c->state = "read user";
+ if(convreadm(c, &user) < 0)
+ goto out;
+
+ c->state = "read response";
+ if(convreadm(c, &resp) < 0)
+ goto out;
+
+ c->state = "authwrite";
+ switch(apopresp(&s, user, resp)){
+ case -1:
+ goto out;
+ case 0:
+ c->state = "write status";
+ if(convprint(c, "bad authentication failed") < 0)
+ goto out;
+ break;
+ case 1:
+ c->state = "write status";
+ if(convprint(c, "ok") < 0)
+ goto out;
+ goto ok;
+ }
+ free(user);
+ free(resp);
+ user = nil;
+ resp = nil;
+ }
+
+ok:
+ ret = 0;
+ c->attr = addcap(c->attr, c->sysuser, &s.t);
+
+out:
+ keyclose(s.k);
+ free(user);
+ free(resp);
+// xioclose(s.asfd);
+ return ret;
+}
+
+static int
+apopchal(ServerState *s, int astype, char chal[APOPCHALLEN])
+{
+ char trbuf[TICKREQLEN];
+ Ticketreq tr;
+
+ memset(&tr, 0, sizeof tr);
+
+ tr.type = astype;
+
+ if(strlen(s->hostid) >= sizeof tr.hostid){
+ werrstr("hostid too long");
+ return -1;
+ }
+ strcpy(tr.hostid, s->hostid);
+
+ if(strlen(s->dom) >= sizeof tr.authdom){
+ werrstr("domain too long");
+ return -1;
+ }
+ strcpy(tr.authdom, s->dom);
+
+ convTR2M(&tr, trbuf);
+ if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
+ return -1;
+
+ if(xioasrdresp(s->asfd, chal, APOPCHALLEN) <= 5)
+ return -1;
+
+ s->tr = tr;
+ return 0;
+}
+
+static int
+apopresp(ServerState *s, char *user, char *resp)
+{
+ char tabuf[TICKETLEN+AUTHENTLEN];
+ char trbuf[TICKREQLEN];
+ int len;
+ Authenticator a;
+ Ticket t;
+ Ticketreq tr;
+
+ tr = s->tr;
+ if(memrandom(tr.chal, CHALLEN) < 0)
+ return -1;
+
+ if(strlen(user) >= sizeof tr.uid){
+ werrstr("uid too long");
+ return -1;
+ }
+ strcpy(tr.uid, user);
+
+ convTR2M(&tr, trbuf);
+ if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
+ return -1;
+
+ len = strlen(resp);
+ if(xiowrite(s->asfd, resp, len) != len)
+ return -1;
+
+ if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
+ return 0;
+
+ convM2T(tabuf, &t, s->k->priv);
+ if(t.num != AuthTs
+ || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
+ werrstr("key mismatch with auth server");
+ return -1;
+ }
+
+ convM2A(tabuf+TICKETLEN, &a, t.key);
+ if(a.num != AuthAc
+ || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
+ || a.id != 0){
+ werrstr("key2 mismatch with auth server");
+ return -1;
+ }
+
+ s->t = t;
+ return 1;
+}
+
+static Role
+apoproles[] =
+{
+ "client", apopclient,
+ "server", apopserver,
+ 0
+};
+
+Proto apop = {
+.name= "apop",
+.roles= apoproles,
+.checkkey= apopcheck,
+.keyprompt= "user? !password?",
+};
+
+Proto cram = {
+.name= "cram",
+.roles= apoproles,
+.checkkey= apopcheck,
+.keyprompt= "user? !password?",
+};
diff --git a/src/cmd/factotum/attr.c b/src/cmd/factotum/attr.c
new file mode 100644
index 00000000..94483364
--- /dev/null
+++ b/src/cmd/factotum/attr.c
@@ -0,0 +1,228 @@
+#include "std.h"
+#include "dat.h"
+
+Attr*
+addattr(Attr *a, char *fmt, ...)
+{
+ char buf[1024];
+ va_list arg;
+ Attr *b;
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof buf, fmt, arg);
+ va_end(arg);
+ b = _parseattr(buf);
+ a = addattrs(a, b);
+ setmalloctag(a, getcallerpc(&a));
+ _freeattr(b);
+ return a;
+}
+
+/*
+ * add attributes in list b to list a. If any attributes are in
+ * both lists, replace those in a by those in b.
+ */
+Attr*
+addattrs(Attr *a, Attr *b)
+{
+ int found;
+ Attr **l, *aa;
+
+ for(; b; b=b->next){
+ switch(b->type){
+ case AttrNameval:
+ for(l=&a; *l; ){
+ if(strcmp((*l)->name, b->name) != 0){
+ l=&(*l)->next;
+ continue;
+ }
+ aa = *l;
+ *l = aa->next;
+ aa->next = nil;
+ freeattr(aa);
+ }
+ *l = mkattr(AttrNameval, b->name, b->val, nil);
+ break;
+ case AttrQuery:
+ found = 0;
+ for(l=&a; *l; l=&(*l)->next)
+ if((*l)->type==AttrNameval && strcmp((*l)->name, b->name) == 0)
+ found++;
+ if(!found)
+ *l = mkattr(AttrQuery, b->name, b->val, nil);
+ break;
+ }
+ }
+ return a;
+}
+
+void
+setmalloctaghere(void *v)
+{
+ setmalloctag(v, getcallerpc(&v));
+}
+
+Attr*
+sortattr(Attr *a)
+{
+ int i;
+ Attr *anext, *a0, *a1, **l;
+
+ if(a == nil || a->next == nil)
+ return a;
+
+ /* cut list in halves */
+ a0 = nil;
+ a1 = nil;
+ i = 0;
+ for(; a; a=anext){
+ anext = a->next;
+ if(i++%2){
+ a->next = a0;
+ a0 = a;
+ }else{
+ a->next = a1;
+ a1 = a;
+ }
+ }
+
+ /* sort */
+ a0 = sortattr(a0);
+ a1 = sortattr(a1);
+
+ /* merge */
+ l = &a;
+ while(a0 || a1){
+ if(a1==nil){
+ anext = a0;
+ a0 = a0->next;
+ }else if(a0==nil){
+ anext = a1;
+ a1 = a1->next;
+ }else if(strcmp(a0->name, a1->name) < 0){
+ anext = a0;
+ a0 = a0->next;
+ }else{
+ anext = a1;
+ a1 = a1->next;
+ }
+ *l = anext;
+ l = &(*l)->next;
+ }
+ *l = nil;
+ return a;
+}
+
+int
+attrnamefmt(Fmt *fmt)
+{
+ char *b, buf[1024], *ebuf;
+ Attr *a;
+
+ ebuf = buf+sizeof buf;
+ b = buf;
+ strcpy(buf, " ");
+ for(a=va_arg(fmt->args, Attr*); a; a=a->next){
+ if(a->name == nil)
+ continue;
+ b = seprint(b, ebuf, " %q?", a->name);
+ }
+ return fmtstrcpy(fmt, buf+1);
+}
+
+static int
+hasqueries(Attr *a)
+{
+ for(; a; a=a->next)
+ if(a->type == AttrQuery)
+ return 1;
+ return 0;
+}
+
+char *ignored[] = {
+ "role",
+};
+
+static int
+ignoreattr(char *s)
+{
+ int i;
+
+ for(i=0; i<nelem(ignored); i++)
+ if(strcmp(ignored[i], s)==0)
+ return 1;
+ return 0;
+}
+
+static int
+hasname(Attr *a0, Attr *a1, char *name)
+{
+ return _findattr(a0, name) || _findattr(a1, name);
+}
+
+static int
+hasnameval(Attr *a0, Attr *a1, char *name, char *val)
+{
+ Attr *a;
+
+ for(a=_findattr(a0, name); a; a=_findattr(a->next, name))
+ if(strcmp(a->val, val) == 0)
+ return 1;
+ for(a=_findattr(a1, name); a; a=_findattr(a->next, name))
+ if(strcmp(a->val, val) == 0)
+ return 1;
+ return 0;
+}
+
+int
+matchattr(Attr *pat, Attr *a0, Attr *a1)
+{
+ int type;
+
+ for(; pat; pat=pat->next){
+ type = pat->type;
+ if(ignoreattr(pat->name))
+ type = AttrDefault;
+ switch(type){
+ case AttrQuery: /* name=something be present */
+ if(!hasname(a0, a1, pat->name))
+ return 0;
+ break;
+ case AttrNameval: /* name=val must be present */
+ if(!hasnameval(a0, a1, pat->name, pat->val))
+ return 0;
+ break;
+ case AttrDefault: /* name=val must be present if name=anything is present */
+ if(hasname(a0, a1, pat->name) && !hasnameval(a0, a1, pat->name, pat->val))
+ return 0;
+ break;
+ }
+ }
+ return 1;
+}
+
+Attr*
+parseattrfmtv(char *fmt, va_list arg)
+{
+ char *s;
+ Attr *a;
+
+ s = vsmprint(fmt, arg);
+ if(s == nil)
+ sysfatal("vsmprint: out of memory");
+ a = parseattr(s);
+ free(s);
+ return a;
+}
+
+Attr*
+parseattrfmt(char *fmt, ...)
+{
+ va_list arg;
+ Attr *a;
+
+ va_start(arg, fmt);
+ a = parseattrfmtv(fmt, arg);
+ va_end(arg);
+ return a;
+}
diff --git a/src/cmd/factotum/chap.c b/src/cmd/factotum/chap.c
new file mode 100644
index 00000000..dcc411ce
--- /dev/null
+++ b/src/cmd/factotum/chap.c
@@ -0,0 +1,424 @@
+/*
+ * CHAP, MSCHAP
+ *
+ * The client does not authenticate the server, hence no CAI
+ *
+ * Protocol:
+ *
+ * S -> C: random 8-byte challenge
+ * C -> S: user in UTF-8
+ * C -> S: Chapreply or MSchapreply structure
+ * S -> C: ok or 'bad why'
+ *
+ * The chap protocol requires the client to give it id=%d, the id of
+ * the PPP message containing the challenge, which is used
+ * as part of the response. Because the client protocol is message-id
+ * specific, there is no point in looping to try multiple keys.
+ *
+ * The MS chap protocol actually uses two different hashes, an
+ * older insecure one called the LM (Lan Manager) hash, and a newer
+ * more secure one called the NT hash. By default we send back only
+ * the NT hash, because the LM hash can help an eavesdropper run
+ * a brute force attack. If the key has an lm attribute, then we send only the
+ * LM hash.
+ */
+
+#include "std.h"
+#include "dat.h"
+
+enum {
+ ChapChallen = 8,
+
+ MShashlen = 16,
+ MSchallen = 8,
+ MSresplen = 24,
+};
+
+static int
+chapcheck(Key *k)
+{
+ if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
+ werrstr("need user and !password attributes");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+nthash(uchar hash[MShashlen], char *passwd)
+{
+ uchar buf[512];
+ int i;
+
+ for(i=0; *passwd && i<sizeof(buf); passwd++) {
+ buf[i++] = *passwd;
+ buf[i++] = 0;
+ }
+
+ memset(hash, 0, 16);
+
+ md4(buf, i, hash, 0);
+}
+
+static void
+desencrypt(uchar data[8], uchar key[7])
+{
+ ulong ekey[32];
+
+ key_setup(key, ekey);
+ block_cipher(ekey, data, 0);
+}
+
+static void
+lmhash(uchar hash[MShashlen], char *passwd)
+{
+ uchar buf[14];
+ char *stdtext = "KGS!@#$%";
+ int i;
+
+ strncpy((char*)buf, passwd, sizeof(buf));
+ for(i=0; i<sizeof(buf); i++)
+ if(buf[i] >= 'a' && buf[i] <= 'z')
+ buf[i] += 'A' - 'a';
+
+ memset(hash, 0, 16);
+ memcpy(hash, stdtext, 8);
+ memcpy(hash+8, stdtext, 8);
+
+ desencrypt(hash, buf);
+ desencrypt(hash+8, buf+7);
+}
+
+static void
+mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
+{
+ int i;
+ uchar buf[21];
+
+ memset(buf, 0, sizeof(buf));
+ memcpy(buf, hash, MShashlen);
+
+ for(i=0; i<3; i++) {
+ memmove(resp+i*MSchallen, chal, MSchallen);
+ desencrypt(resp+i*MSchallen, buf+i*7);
+ }
+}
+
+static int
+chapclient(Conv *c)
+{
+ int id, astype, nchal, npw, ret;
+ uchar *chal;
+ char *s, *pw, *user, *res;
+ Attr *attr;
+ Key *k;
+ Chapreply cr;
+ MSchapreply mscr;
+ DigestState *ds;
+
+ ret = -1;
+ chal = nil;
+ k = nil;
+ attr = c->attr;
+
+ if(c->proto == &chap){
+ astype = AuthChap;
+ s = strfindattr(attr, "id");
+ if(s == nil || *s == 0){
+ werrstr("need id=n attr in start message");
+ goto out;
+ }
+ id = strtol(s, &s, 10);
+ if(*s != 0 || id < 0 || id >= 256){
+ werrstr("bad id=n attr in start message");
+ goto out;
+ }
+ cr.id = id;
+ }else if(c->proto == &mschap)
+ astype = AuthMSchap;
+ else{
+ werrstr("bad proto");
+ goto out;
+ }
+
+ c->state = "find key";
+ k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
+ if(k == nil)
+ goto out;
+
+ c->attr = addattrs(copyattr(attr), k->attr);
+
+ c->state = "read challenge";
+ if((nchal = convreadm(c, (char**)&chal)) < 0)
+ goto out;
+ if(astype == AuthMSchap && nchal != MSchallen)
+ c->state = "write user";
+ if((user = strfindattr(k->attr, "user")) == nil){
+ werrstr("key has no user (cannot happen?)");
+ goto out;
+ }
+ if(convprint(c, "%s", user) < 0)
+ goto out;
+
+ c->state = "write response";
+ if((pw = strfindattr(k->privattr, "!password")) == nil){
+ werrstr("key has no password (cannot happen?)");
+ goto out;
+ }
+ npw = strlen(pw);
+
+ if(astype == AuthChap){
+ ds = md5(&cr.id, 1, 0, 0);
+ md5((uchar*)pw, npw, 0, ds);
+ md5(chal, nchal, (uchar*)cr.resp, ds);
+ if(convwrite(c, &cr, sizeof cr) < 0)
+ goto out;
+ }else{
+ uchar hash[MShashlen];
+
+ memset(&mscr, 0, sizeof mscr);
+ if(strfindattr(k->attr, "lm")){
+ lmhash(hash, pw);
+ mschalresp((uchar*)mscr.LMresp, hash, chal);
+ }else{
+ nthash(hash, pw);
+ mschalresp((uchar*)mscr.NTresp, hash, chal);
+ }
+ if(convwrite(c, &mscr, sizeof mscr) < 0)
+ goto out;
+ }
+
+ c->state = "read result";
+ if(convreadm(c, &res) < 0)
+ goto out;
+ if(strcmp(res, "ok") == 0){
+ ret = 0;
+ werrstr("succeeded");
+ goto out;
+ }
+ if(strncmp(res, "bad ", 4) != 0){
+ werrstr("bad result: %s", res);
+ goto out;
+ }
+
+ c->state = "replace key";
+ keyevict(c, k, "%s", res+4);
+ werrstr("%s", res+4);
+
+out:
+ free(res);
+ keyclose(k);
+ free(chal);
+ if(c->attr != attr)
+ freeattr(attr);
+ return ret;
+}
+
+/* shared with auth dialing routines */
+typedef struct ServerState ServerState;
+struct ServerState
+{
+ int asfd;
+ Key *k;
+ Ticketreq tr;
+ Ticket t;
+ char *dom;
+ char *hostid;
+};
+
+static int chapchal(ServerState*, int, char[ChapChallen]);
+static int chapresp(ServerState*, char*, char*);
+
+static int
+chapserver(Conv *c)
+{
+ char chal[ChapChallen], *user, *resp;
+ ServerState s;
+ int astype, ret;
+ Attr *a;
+
+ ret = -1;
+ user = nil;
+ resp = nil;
+ memset(&s, 0, sizeof s);
+ s.asfd = -1;
+
+ if(c->proto == &chap)
+ astype = AuthChap;
+ else if(c->proto == &mschap)
+ astype = AuthMSchap;
+ else{
+ werrstr("bad proto");
+ goto out;
+ }
+
+ c->state = "find key";
+ if((s.k = plan9authkey(c->attr)) == nil)
+ goto out;
+
+ a = copyattr(s.k->attr);
+ a = delattr(a, "proto");
+ c->attr = addattrs(c->attr, a);
+ freeattr(a);
+
+ c->state = "authdial";
+ s.hostid = strfindattr(s.k->attr, "user");
+ s.dom = strfindattr(s.k->attr, "dom");
+ if((s.asfd = xioauthdial(nil, s.dom)) < 0){
+ werrstr("authdial %s: %r", s.dom);
+ goto out;
+ }
+
+ c->state = "authchal";
+ if(chapchal(&s, astype, chal) < 0)
+ goto out;
+
+ c->state = "write challenge";
+ if(convprint(c, "%s", chal) < 0)
+ goto out;
+
+ c->state = "read user";
+ if(convreadm(c, &user) < 0)
+ goto out;
+
+ c->state = "read response";
+ if(convreadm(c, &resp) < 0)
+ goto out;
+
+ c->state = "authwrite";
+ switch(chapresp(&s, user, resp)){
+ default:
+ fprint(2, "factotum: bad result from chapresp\n");
+ goto out;
+ case -1:
+ goto out;
+ case 0:
+ c->state = "write status";
+ if(convprint(c, "bad authentication failed") < 0)
+ goto out;
+ goto out;
+
+ case 1:
+ c->state = "write status";
+ if(convprint(c, "ok") < 0)
+ goto out;
+ goto ok;
+ }
+
+ok:
+ ret = 0;
+ c->attr = addcap(c->attr, c->sysuser, &s.t);
+
+out:
+ keyclose(s.k);
+ free(user);
+ free(resp);
+// xioclose(s.asfd);
+ return ret;
+}
+
+static int
+chapchal(ServerState *s, int astype, char chal[ChapChallen])
+{
+ char trbuf[TICKREQLEN];
+ Ticketreq tr;
+
+ memset(&tr, 0, sizeof tr);
+
+ tr.type = astype;
+
+ if(strlen(s->hostid) >= sizeof tr.hostid){
+ werrstr("hostid too long");
+ return -1;
+ }
+ strcpy(tr.hostid, s->hostid);
+
+ if(strlen(s->dom) >= sizeof tr.authdom){
+ werrstr("domain too long");
+ return -1;
+ }
+ strcpy(tr.authdom, s->dom);
+
+ convTR2M(&tr, trbuf);
+ if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
+ return -1;
+
+ if(xioasrdresp(s->asfd, chal, ChapChallen) <= 5)
+ return -1;
+
+ s->tr = tr;
+ return 0;
+}
+
+static int
+chapresp(ServerState *s, char *user, char *resp)
+{
+ char tabuf[TICKETLEN+AUTHENTLEN];
+ char trbuf[TICKREQLEN];
+ int len;
+ Authenticator a;
+ Ticket t;
+ Ticketreq tr;
+
+ tr = s->tr;
+ if(memrandom(tr.chal, CHALLEN) < 0)
+ return -1;
+
+ if(strlen(user) >= sizeof tr.uid){
+ werrstr("uid too long");
+ return -1;
+ }
+ strcpy(tr.uid, user);
+
+ convTR2M(&tr, trbuf);
+ if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
+ return -1;
+
+ len = strlen(resp);
+ if(xiowrite(s->asfd, resp, len) != len)
+ return -1;
+
+ if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
+ return 0;
+
+ convM2T(tabuf, &t, s->k->priv);
+ if(t.num != AuthTs
+ || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
+ werrstr("key mismatch with auth server");
+ return -1;
+ }
+
+ convM2A(tabuf+TICKETLEN, &a, t.key);
+ if(a.num != AuthAc
+ || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
+ || a.id != 0){
+ werrstr("key2 mismatch with auth server");
+ return -1;
+ }
+
+ s->t = t;
+ return 1;
+}
+
+static Role
+chaproles[] =
+{
+ "client", chapclient,
+ "server", chapserver,
+ 0
+};
+
+Proto chap = {
+.name= "chap",
+.roles= chaproles,
+.checkkey= chapcheck,
+.keyprompt= "user? !password?",
+};
+
+Proto mschap = {
+.name= "mschap",
+.roles= chaproles,
+.checkkey= chapcheck,
+.keyprompt= "user? !password?",
+};
+
diff --git a/src/cmd/factotum/confirm.c b/src/cmd/factotum/confirm.c
new file mode 100644
index 00000000..8f492450
--- /dev/null
+++ b/src/cmd/factotum/confirm.c
@@ -0,0 +1,139 @@
+#include "std.h"
+#include "dat.h"
+
+Logbuf confbuf;
+
+void
+confirmread(Req *r)
+{
+ lbread(&confbuf, r);
+}
+
+void
+confirmflush(Req *r)
+{
+ lbflush(&confbuf, r);
+}
+
+int
+confirmwrite(char *s)
+{
+ char *t, *ans;
+ int allow;
+ ulong tag;
+ Attr *a;
+ Conv *c;
+
+ a = _parseattr(s);
+ if(a == nil){
+ werrstr("bad attr");
+ return -1;
+ }
+ if((t = _strfindattr(a, "tag")) == nil){
+ werrstr("no tag");
+ return -1;
+ }
+ tag = strtoul(t, 0, 0);
+ if((ans = _strfindattr(a, "answer")) == nil){
+ werrstr("no answer");
+ return -1;
+ }
+ if(strcmp(ans, "yes") == 0)
+ allow = 1;
+ else if(strcmp(ans, "no") == 0)
+ allow = 0;
+ else{
+ werrstr("bad answer");
+ return -1;
+ }
+ for(c=conv; c; c=c->next){
+ if(tag == c->tag){
+ nbsendul(c->keywait, allow);
+ break;
+ }
+ }
+ if(c == nil){
+ werrstr("tag not found");
+ return -1;
+ }
+ return 0;
+}
+
+int
+confirmkey(Conv *c, Key *k)
+{
+ if(*confirminuse == 0)
+ return -1;
+
+ lbappend(&confbuf, "confirm tag=%lud %A %N", c->tag, k->attr, k->privattr);
+ c->state = "keyconfirm";
+ return recvul(c->keywait);
+}
+
+Logbuf needkeybuf;
+
+void
+needkeyread(Req *r)
+{
+ lbread(&needkeybuf, r);
+}
+
+void
+needkeyflush(Req *r)
+{
+ lbflush(&needkeybuf, r);
+}
+
+int
+needkeywrite(char *s)
+{
+ char *t;
+ ulong tag;
+ Attr *a;
+ Conv *c;
+
+ a = _parseattr(s);
+ if(a == nil){
+ werrstr("empty write");
+ return -1;
+ }
+ if((t = _strfindattr(a, "tag")) == nil){
+ werrstr("no tag");
+ freeattr(a);
+ return -1;
+ }
+ tag = strtoul(t, 0, 0);
+ for(c=conv; c; c=c->next)
+ if(c->tag == tag){
+ nbsendul(c->keywait, 0);
+ break;
+ }
+ if(c == nil){
+ werrstr("tag not found");
+ freeattr(a);
+ return -1;
+ }
+ freeattr(a);
+ return 0;
+}
+
+int
+needkey(Conv *c, Attr *a)
+{
+ if(c == nil || *needkeyinuse == 0)
+ return -1;
+
+ lbappend(&needkeybuf, "needkey tag=%lud %A", c->tag, a);
+ return nbrecvul(c->keywait);
+}
+
+int
+badkey(Conv *c, Key *k, char *msg, Attr *a)
+{
+ if(c == nil || *needkeyinuse == 0)
+ return -1;
+
+ lbappend(&needkeybuf, "badkey tag=%lud %A %N\n%s\n%A",
+ c->tag, k->attr, k->privattr, msg, a);
+ return nbrecvul(c->keywait);
+}
diff --git a/src/cmd/factotum/conv.c b/src/cmd/factotum/conv.c
new file mode 100644
index 00000000..862993f9
--- /dev/null
+++ b/src/cmd/factotum/conv.c
@@ -0,0 +1,254 @@
+#include "std.h"
+#include "dat.h"
+
+Conv *conv;
+
+ulong taggen = 1;
+
+Conv*
+convalloc(char *sysuser)
+{
+ Conv *c;
+
+ c = mallocz(sizeof(Conv), 1);
+ if(c == nil)
+ return nil;
+ c->ref = 1;
+ c->tag = taggen++;
+ c->next = conv;
+ c->sysuser = estrdup(sysuser);
+ c->state = "nascent";
+ c->rpcwait = chancreate(sizeof(void*), 0);
+ c->keywait = chancreate(sizeof(void*), 0);
+ strcpy(c->err, "protocol has not started");
+ conv = c;
+ convreset(c);
+ return c;
+}
+
+void
+convreset(Conv *c)
+{
+ if(c->ref != 1){
+ c->hangup = 1;
+ nbsendp(c->rpcwait, 0);
+ while(c->ref > 1)
+ yield();
+ c->hangup = 0;
+ }
+ c->state = "nascent";
+ c->err[0] = '\0';
+ freeattr(c->attr);
+ c->attr = nil;
+ c->proto = nil;
+ c->rpc.op = 0;
+ c->active = 0;
+ c->done = 0;
+ c->hangup = 0;
+}
+
+void
+convhangup(Conv *c)
+{
+ c->hangup = 1;
+ c->rpc.op = 0;
+ (*c->kickreply)(c);
+ nbsendp(c->rpcwait, 0);
+}
+
+void
+convclose(Conv *c)
+{
+ Conv *p;
+
+ if(c == nil)
+ return;
+
+ if(--c->ref > 0)
+ return;
+
+ if(c == conv){
+ conv = c->next;
+ goto free;
+ }
+ for(p=conv; p && p->next!=c; p=p->next)
+ ;
+ if(p == nil){
+ print("cannot find conv in list\n");
+ return;
+ }
+ p->next = c->next;
+
+free:
+ c->next = nil;
+ free(c);
+}
+
+static Rpc*
+convgetrpc(Conv *c, int want)
+{
+ for(;;){
+ if(c->hangup){
+ werrstr("hangup");
+ return nil;
+ }
+ if(c->rpc.op == RpcUnknown){
+ recvp(c->rpcwait);
+ if(c->hangup){
+ werrstr("hangup");
+ return nil;
+ }
+ if(c->rpc.op == RpcUnknown)
+ continue;
+ }
+ if(want < 0 || c->rpc.op == want)
+ return &c->rpc;
+ rpcrespond(c, "phase in state '%s' want '%s'", c->state, rpcname[want]);
+ }
+ return nil; /* not reached */
+}
+
+/* read until the done function tells us that's enough */
+int
+convreadfn(Conv *c, int (*done)(void*, int), char **ps)
+{
+ int n;
+ Rpc *r;
+ char *s;
+
+ for(;;){
+ r = convgetrpc(c, RpcWrite);
+ if(r == nil)
+ return -1;
+ n = (*done)(r->data, r->count);
+ if(n == r->count)
+ break;
+ rpcrespond(c, "toosmall %d", n);
+ }
+
+ s = emalloc(r->count+1);
+ memmove(s, r->data, r->count);
+ s[r->count] = 0;
+ *ps = s;
+ rpcrespond(c, "ok");
+ return r->count;
+}
+
+/*
+ * read until we get a non-zero write. assumes remote side
+ * knows something about the protocol (is not auth_proxy).
+ * the remote side typically won't bother with the zero-length
+ * write to find out the length -- the loop is there only so the
+ * test program can call auth_proxy on both sides of a pipe
+ * to play a conversation.
+ */
+int
+convreadm(Conv *c, char **ps)
+{
+ char *s;
+ Rpc *r;
+
+ for(;;){
+ r = convgetrpc(c, RpcWrite);
+ if(r == nil)
+ return -1;
+ if(r->count > 0)
+ break;
+ rpcrespond(c, "toosmall %d", AuthRpcMax);
+ }
+ s = emalloc(r->count+1);
+ memmove(s, r->data, r->count);
+ s[r->count] = 0;
+ *ps = s;
+ rpcrespond(c, "ok");
+ return r->count;
+}
+
+/* read exactly count bytes */
+int
+convread(Conv *c, void *data, int count)
+{
+ Rpc *r;
+
+ for(;;){
+ r = convgetrpc(c, RpcWrite);
+ if(r == nil)
+ return -1;
+ if(r->count == count)
+ break;
+ if(r->count < count)
+ rpcrespond(c, "toosmall %d", count);
+ else
+ rpcrespond(c, "error too much data; want %d got %d", count, r->count);
+ }
+ memmove(data, r->data, count);
+ rpcrespond(c, "ok");
+ return 0;
+}
+
+/* write exactly count bytes */
+int
+convwrite(Conv *c, void *data, int count)
+{
+ Rpc *r;
+
+ for(;;){
+ r = convgetrpc(c, RpcRead);
+ if(r == nil)
+ return -1;
+ break;
+ }
+ rpcrespondn(c, "ok", data, count);
+ return 0;
+}
+
+/* print to the conversation */
+int
+convprint(Conv *c, char *fmt, ...)
+{
+ char *s;
+ va_list arg;
+ int ret;
+
+ va_start(arg, fmt);
+ s = vsmprint(fmt, arg);
+ va_end(arg);
+ if(s == nil)
+ return -1;
+ ret = convwrite(c, s, strlen(s));
+ free(s);
+ return ret;
+}
+
+/* ask for a key */
+int
+convneedkey(Conv *c, Attr *a)
+{
+ /*
+ * Piggyback key requests in the usual RPC channel.
+ * Wait for the next RPC and then send a key request
+ * in response. The keys get added out-of-band (via the
+ * ctl file), so assume the key has been added when the
+ * next request comes in.
+ */
+ if(convgetrpc(c, -1) == nil)
+ return -1;
+ rpcrespond(c, "needkey %A", a);
+ if(convgetrpc(c, -1) == nil)
+ return -1;
+ return 0;
+}
+
+/* ask for a replacement for a bad key*/
+int
+convbadkey(Conv *c, Key *k, char *msg, Attr *a)
+{
+ if(convgetrpc(c, -1) == nil)
+ return -1;
+ rpcrespond(c, "badkey %A %N\n%s\n%A",
+ k->attr, k->privattr, msg, a);
+ if(convgetrpc(c, -1) == nil)
+ return -1;
+ return 0;
+}
+
diff --git a/src/cmd/factotum/cpu.c b/src/cmd/factotum/cpu.c
new file mode 100644
index 00000000..da8280ad
--- /dev/null
+++ b/src/cmd/factotum/cpu.c
@@ -0,0 +1,1117 @@
+/*
+ * cpu.c - Make a connection to a cpu server
+ *
+ * Invoked by listen as 'cpu -R | -N service net netdir'
+ * by users as 'cpu [-h system] [-c cmd args ...]'
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <auth.h>
+#include <fcall.h>
+#include <libsec.h>
+
+#define Maxfdata 8192
+
+void remoteside(int);
+void fatal(int, char*, ...);
+void lclnoteproc(int);
+void rmtnoteproc(void);
+void catcher(void*, char*);
+void usage(void);
+void writestr(int, char*, char*, int);
+int readstr(int, char*, int);
+char *rexcall(int*, char*, char*);
+int setamalg(char*);
+
+int notechan;
+char system[32];
+int cflag;
+int hflag;
+int dbg;
+char *user;
+
+char *srvname = "ncpu";
+char *exportfs = "/bin/exportfs";
+char *ealgs = "rc4_256 sha1";
+
+/* message size for exportfs; may be larger so we can do big graphics in CPU window */
+int msgsize = 8192+IOHDRSZ;
+
+/* authentication mechanisms */
+static int netkeyauth(int);
+static int netkeysrvauth(int, char*);
+static int p9auth(int);
+static int srvp9auth(int, char*);
+static int noauth(int);
+static int srvnoauth(int, char*);
+
+typedef struct AuthMethod AuthMethod;
+struct AuthMethod {
+ char *name; /* name of method */
+ int (*cf)(int); /* client side authentication */
+ int (*sf)(int, char*); /* server side authentication */
+} authmethod[] =
+{
+ { "p9", p9auth, srvp9auth,},
+ { "netkey", netkeyauth, netkeysrvauth,},
+// { "none", noauth, srvnoauth,},
+ { nil, nil}
+};
+AuthMethod *am = authmethod; /* default is p9 */
+
+char *p9authproto = "p9any";
+
+int setam(char*);
+
+void
+usage(void)
+{
+ fprint(2, "usage: cpu [-h system] [-a authmethod] [-e 'crypt hash'] [-c cmd args ...]\n");
+ exits("usage");
+}
+int fdd;
+
+void
+main(int argc, char **argv)
+{
+ char dat[128], buf[128], cmd[128], *p, *err;
+ int fd, ms, kms, data;
+
+ /* see if we should use a larger message size */
+ fd = open("/dev/draw", OREAD);
+ if(fd > 0){
+ ms = iounit(fd);
+ if(ms != 0 && ms < ms+IOHDRSZ)
+ msgsize = ms+IOHDRSZ;
+ close(fd);
+ }
+ kms = kiounit();
+ if(msgsize > kms-IOHDRSZ-100) /* 100 for network packets, etc. */
+ msgsize = kms-IOHDRSZ-100;
+
+ user = getuser();
+ if(user == nil)
+ fatal(1, "can't read user name");
+ ARGBEGIN{
+ case 'a':
+ p = EARGF(usage());
+ if(setam(p) < 0)
+ fatal(0, "unknown auth method %s", p);
+ break;
+ case 'e':
+ ealgs = EARGF(usage());
+ if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
+ ealgs = nil;
+ break;
+ case 'd':
+ dbg++;
+ break;
+ case 'f':
+ /* ignored but accepted for compatibility */
+ break;
+ case 'O':
+ p9authproto = "p9sk2";
+ remoteside(1); /* From listen */
+ break;
+ case 'R': /* From listen */
+ remoteside(0);
+ break;
+ case 'h':
+ hflag++;
+ p = EARGF(usage());
+ strcpy(system, p);
+ break;
+ case 'c':
+ cflag++;
+ cmd[0] = '!';
+ cmd[1] = '\0';
+ while(p = ARGF()) {
+ strcat(cmd, " ");
+ strcat(cmd, p);
+ }
+ break;
+ case 'o':
+ p9authproto = "p9sk2";
+ srvname = "cpu";
+ break;
+ case 'u':
+ user = EARGF(usage());
+ break;
+ default:
+ usage();
+ }ARGEND;
+
+
+ if(argc != 0)
+ usage();
+
+ if(hflag == 0) {
+ p = getenv("cpu");
+ if(p == 0)
+ fatal(0, "set $cpu");
+ strcpy(system, p);
+ }
+
+ if(err = rexcall(&data, system, srvname))
+ fatal(1, "%s: %s", err, system);
+
+ /* Tell the remote side the command to execute and where our working directory is */
+ if(cflag)
+ writestr(data, cmd, "command", 0);
+ if(getwd(dat, sizeof(dat)) == 0)
+ writestr(data, "NO", "dir", 0);
+ else
+ writestr(data, dat, "dir", 0);
+
+ /* start up a process to pass along notes */
+ lclnoteproc(data);
+
+ /*
+ * Wait for the other end to execute and start our file service
+ * of /mnt/term
+ */
+ if(readstr(data, buf, sizeof(buf)) < 0)
+ fatal(1, "waiting for FS");
+ if(strncmp("FS", buf, 2) != 0) {
+ print("remote cpu: %s", buf);
+ exits(buf);
+ }
+
+ /* Begin serving the gnot namespace */
+ close(0);
+ dup(data, 0);
+ close(data);
+ sprint(buf, "%d", msgsize);
+ if(dbg)
+ execl(exportfs, exportfs, "-dm", buf, 0);
+ else
+ execl(exportfs, exportfs, "-m", buf, 0);
+ fatal(1, "starting exportfs");
+}
+
+void
+fatal(int syserr, char *fmt, ...)
+{
+ char buf[ERRMAX];
+ va_list arg;
+
+ va_start(arg, fmt);
+ doprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ if(syserr)
+ fprint(2, "cpu: %s: %r\n", buf);
+ else
+ fprint(2, "cpu: %s\n", buf);
+ exits(buf);
+}
+
+char *negstr = "negotiating authentication method";
+
+char bug[256];
+
+int
+old9p(int fd)
+{
+ int p[2];
+
+ if(pipe(p) < 0)
+ fatal(1, "pipe");
+
+ switch(rfork(RFPROC|RFFDG|RFNAMEG)) {
+ case -1:
+ fatal(1, "rfork srvold9p");
+ case 0:
+ if(fd != 1){
+ dup(fd, 1);
+ close(fd);
+ }
+ if(p[0] != 0){
+ dup(p[0], 0);
+ close(p[0]);
+ }
+ close(p[1]);
+ if(0){
+ fd = open("/sys/log/cpu", OWRITE);
+ if(fd != 2){
+ dup(fd, 2);
+ close(fd);
+ }
+ execl("/bin/srvold9p", "srvold9p", "-ds", 0);
+ } else
+ execl("/bin/srvold9p", "srvold9p", "-s", 0);
+ fatal(1, "exec srvold9p");
+ default:
+ close(fd);
+ close(p[0]);
+ }
+ return p[1];
+}
+
+/* Invoked with stdin, stdout and stderr connected to the network connection */
+void
+remoteside(int old)
+{
+ char user[128], home[128], buf[128], xdir[128], cmd[128];
+ int i, n, fd, badchdir, gotcmd;
+
+ fd = 0;
+
+ /* negotiate authentication mechanism */
+ n = readstr(fd, cmd, sizeof(cmd));
+ if(n < 0)
+ fatal(1, "authenticating");
+ if(setamalg(cmd) < 0){
+ writestr(fd, "unsupported auth method", nil, 0);
+ fatal(1, "bad auth method %s", cmd);
+ } else
+ writestr(fd, "", "", 1);
+
+ fd = (*am->sf)(fd, user);
+ if(fd < 0)
+ fatal(1, "srvauth");
+
+ /* Set environment values for the user */
+ putenv("user", user);
+ sprint(home, "/usr/%s", user);
+ putenv("home", home);
+
+ /* Now collect invoking cpu's current directory or possibly a command */
+ gotcmd = 0;
+ if(readstr(fd, xdir, sizeof(xdir)) < 0)
+ fatal(1, "dir/cmd");
+ if(xdir[0] == '!') {
+ strcpy(cmd, &xdir[1]);
+ gotcmd = 1;
+ if(readstr(fd, xdir, sizeof(xdir)) < 0)
+ fatal(1, "dir");
+ }
+
+ /* Establish the new process at the current working directory of the
+ * gnot */
+ badchdir = 0;
+ if(strcmp(xdir, "NO") == 0)
+ chdir(home);
+ else if(chdir(xdir) < 0) {
+ badchdir = 1;
+ chdir(home);
+ }
+
+ /* Start the gnot serving its namespace */
+ writestr(fd, "FS", "FS", 0);
+ writestr(fd, "/", "exportfs dir", 0);
+
+ n = read(fd, buf, sizeof(buf));
+ if(n != 2 || buf[0] != 'O' || buf[1] != 'K')
+ exits("remote tree");
+
+ if(old)
+ fd = old9p(fd);
+
+ /* make sure buffers are big by doing fversion explicitly; pick a huge number; other side will trim */
+ strcpy(buf, VERSION9P);
+ if(fversion(fd, 64*1024, buf, sizeof buf) < 0)
+ exits("fversion failed");
+ if(mount(fd, -1, "/mnt/term", MCREATE|MREPL, "") < 0)
+ exits("mount failed");
+
+ close(fd);
+
+ /* the remote noteproc uses the mount so it must follow it */
+ rmtnoteproc();
+
+ for(i = 0; i < 3; i++)
+ close(i);
+
+ if(open("/mnt/term/dev/cons", OREAD) != 0)
+ exits("open stdin");
+ if(open("/mnt/term/dev/cons", OWRITE) != 1)
+ exits("open stdout");
+ dup(1, 2);
+
+ if(badchdir)
+ print("cpu: failed to chdir to '%s'\n", xdir);
+
+ if(gotcmd)
+ execl("/bin/rc", "rc", "-lc", cmd, 0);
+ else
+ execl("/bin/rc", "rc", "-li", 0);
+ fatal(1, "exec shell");
+}
+
+char*
+rexcall(int *fd, char *host, char *service)
+{
+ char *na;
+ char dir[128];
+ char err[ERRMAX];
+ char msg[128];
+ int n;
+
+ na = netmkaddr(host, 0, service);
+ if((*fd = dial(na, 0, dir, 0)) < 0)
+ return "can't dial";
+
+ /* negotiate authentication mechanism */
+ if(ealgs != nil)
+ snprint(msg, sizeof(msg), "%s %s", am->name, ealgs);
+ else
+ snprint(msg, sizeof(msg), "%s", am->name);
+ writestr(*fd, msg, negstr, 0);
+ n = readstr(*fd, err, sizeof err);
+ if(n < 0)
+ return negstr;
+ if(*err){
+ werrstr(err);
+ return negstr;
+ }
+
+ /* authenticate */
+ *fd = (*am->cf)(*fd);
+ if(*fd < 0)
+ return "can't authenticate";
+ return 0;
+}
+
+void
+writestr(int fd, char *str, char *thing, int ignore)
+{
+ int l, n;
+
+ l = strlen(str);
+ n = write(fd, str, l+1);
+ if(!ignore && n < 0)
+ fatal(1, "writing network: %s", thing);
+}
+
+int
+readstr(int fd, char *str, int len)
+{
+ int n;
+
+ while(len) {
+ n = read(fd, str, 1);
+ if(n < 0)
+ return -1;
+ if(*str == '\0')
+ return 0;
+ str++;
+ len--;
+ }
+ return -1;
+}
+
+static int
+readln(char *buf, int n)
+{
+ char *p = buf;
+
+ n--;
+ while(n > 0){
+ if(read(0, p, 1) != 1)
+ break;
+ if(*p == '\n' || *p == '\r'){
+ *p = 0;
+ return p-buf;
+ }
+ p++;
+ }
+ *p = 0;
+ return p-buf;
+}
+
+/*
+ * user level challenge/response
+ */
+static int
+netkeyauth(int fd)
+{
+ char chall[32];
+ char resp[32];
+
+ strcpy(chall, getuser());
+ print("user[%s]: ", chall);
+ if(readln(resp, sizeof(resp)) < 0)
+ return -1;
+ if(*resp != 0)
+ strcpy(chall, resp);
+ writestr(fd, chall, "challenge/response", 1);
+
+ for(;;){
+ if(readstr(fd, chall, sizeof chall) < 0)
+ break;
+ if(*chall == 0)
+ return fd;
+ print("challenge: %s\nresponse: ", chall);
+ if(readln(resp, sizeof(resp)) < 0)
+ break;
+ writestr(fd, resp, "challenge/response", 1);
+ }
+ return -1;
+}
+
+static int
+netkeysrvauth(int fd, char *user)
+{
+ char response[32];
+ Chalstate *ch;
+ int tries;
+ AuthInfo *ai;
+
+ if(readstr(fd, user, 32) < 0)
+ return -1;
+
+ ai = nil;
+ ch = nil;
+ for(tries = 0; tries < 10; tries++){
+ if((ch = auth_challenge("p9cr", user, nil)) == nil)
+ return -1;
+ writestr(fd, ch->chal, "challenge", 1);
+ if(readstr(fd, response, sizeof response) < 0)
+ return -1;
+ ch->resp = response;
+ ch->nresp = strlen(response);
+ if((ai = auth_response(ch)) != nil)
+ break;
+ }
+ auth_freechal(ch);
+ if(ai == nil)
+ return -1;
+ writestr(fd, "", "challenge", 1);
+ if(auth_chuid(ai, 0) < 0)
+ fatal(1, "newns");
+ auth_freeAI(ai);
+ return fd;
+}
+
+static void
+mksecret(char *t, uchar *f)
+{
+ sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
+ f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
+}
+
+/*
+ * plan9 authentication followed by rc4 encryption
+ */
+static int
+p9auth(int fd)
+{
+ uchar key[16];
+ uchar digest[SHA1dlen];
+ char fromclientsecret[21];
+ char fromserversecret[21];
+ int i;
+ AuthInfo *ai;
+
+ ai = auth_proxy(fd, auth_getkey, "proto=%q user=%q role=client", p9authproto, user);
+ if(ai == nil)
+ return -1;
+ memmove(key+4, ai->secret, ai->nsecret);
+ if(ealgs == nil)
+ return fd;
+
+ /* exchange random numbers */
+ srand(truerand());
+ for(i = 0; i < 4; i++)
+ key[i] = rand();
+ if(write(fd, key, 4) != 4)
+ return -1;
+ if(readn(fd, key+12, 4) != 4)
+ return -1;
+
+ /* scramble into two secrets */
+ sha1(key, sizeof(key), digest, nil);
+ mksecret(fromclientsecret, digest);
+ mksecret(fromserversecret, digest+10);
+
+ /* set up encryption */
+ i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil);
+ if(i < 0)
+ werrstr("can't establish ssl connection: %r");
+ return i;
+}
+
+static int
+noauth(int fd)
+{
+ ealgs = nil;
+ return fd;
+}
+
+static int
+srvnoauth(int fd, char *user)
+{
+ strcpy(user, getuser());
+ ealgs = nil;
+ return fd;
+}
+
+void
+loghex(uchar *p, int n)
+{
+ char buf[100];
+ int i;
+
+ for(i = 0; i < n; i++)
+ sprint(buf+2*i, "%2.2ux", p[i]);
+ syslog(0, "cpu", buf);
+}
+
+static int
+srvp9auth(int fd, char *user)
+{
+ uchar key[16];
+ uchar digest[SHA1dlen];
+ char fromclientsecret[21];
+ char fromserversecret[21];
+ int i;
+ AuthInfo *ai;
+
+ ai = auth_proxy(0, nil, "proto=%q role=server", p9authproto);
+ if(ai == nil)
+ return -1;
+ if(auth_chuid(ai, nil) < 0)
+ return -1;
+ strcpy(user, ai->cuid);
+ memmove(key+4, ai->secret, ai->nsecret);
+
+ if(ealgs == nil)
+ return fd;
+
+ /* exchange random numbers */
+ srand(truerand());
+ for(i = 0; i < 4; i++)
+ key[i+12] = rand();
+ if(readn(fd, key, 4) != 4)
+ return -1;
+ if(write(fd, key+12, 4) != 4)
+ return -1;
+
+ /* scramble into two secrets */
+ sha1(key, sizeof(key), digest, nil);
+ mksecret(fromclientsecret, digest);
+ mksecret(fromserversecret, digest+10);
+
+ /* set up encryption */
+ i = pushssl(fd, ealgs, fromserversecret, fromclientsecret, nil);
+ if(i < 0)
+ werrstr("can't establish ssl connection: %r");
+ return i;
+}
+
+/*
+ * set authentication mechanism
+ */
+int
+setam(char *name)
+{
+ for(am = authmethod; am->name != nil; am++)
+ if(strcmp(am->name, name) == 0)
+ return 0;
+ am = authmethod;
+ return -1;
+}
+
+/*
+ * set authentication mechanism and encryption/hash algs
+ */
+int
+setamalg(char *s)
+{
+ ealgs = strchr(s, ' ');
+ if(ealgs != nil)
+ *ealgs++ = 0;
+ return setam(s);
+}
+
+char *rmtnotefile = "/mnt/term/dev/cpunote";
+
+/*
+ * loop reading /mnt/term/dev/note looking for notes.
+ * The child returns to start the shell.
+ */
+void
+rmtnoteproc(void)
+{
+ int n, fd, pid, notepid;
+ char buf[256];
+
+ /* new proc returns to start shell */
+ pid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM);
+ switch(pid){
+ case -1:
+ syslog(0, "cpu", "cpu -R: can't start noteproc: %r");
+ return;
+ case 0:
+ return;
+ }
+
+ /* new proc reads notes from other side and posts them to shell */
+ switch(notepid = rfork(RFPROC|RFFDG|RFMEM)){
+ case -1:
+ syslog(0, "cpu", "cpu -R: can't start wait proc: %r");
+ _exits(0);
+ case 0:
+ fd = open(rmtnotefile, OREAD);
+ if(fd < 0){
+ syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile);
+ _exits(0);
+ }
+
+ for(;;){
+ n = read(fd, buf, sizeof(buf)-1);
+ if(n <= 0){
+ postnote(PNGROUP, pid, "hangup");
+ _exits(0);
+ }
+ buf[n] = 0;
+ postnote(PNGROUP, pid, buf);
+ }
+ break;
+ }
+
+ /* original proc waits for shell proc to die and kills note proc */
+ for(;;){
+ n = waitpid();
+ if(n < 0 || n == pid)
+ break;
+ }
+ postnote(PNPROC, notepid, "kill");
+ _exits(0);
+}
+
+enum
+{
+ Qdir,
+ Qcpunote,
+
+ Nfid = 32,
+};
+
+struct {
+ char *name;
+ Qid qid;
+ ulong perm;
+} fstab[] =
+{
+ [Qdir] { ".", {Qdir, 0, QTDIR}, DMDIR|0555 },
+ [Qcpunote] { "cpunote", {Qcpunote, 0}, 0444 },
+};
+
+typedef struct Note Note;
+struct Note
+{
+ Note *next;
+ char msg[ERRMAX];
+};
+
+typedef struct Request Request;
+struct Request
+{
+ Request *next;
+ Fcall f;
+};
+
+typedef struct Fid Fid;
+struct Fid
+{
+ int fid;
+ int file;
+};
+Fid fids[Nfid];
+
+struct {
+ Lock;
+ Note *nfirst, *nlast;
+ Request *rfirst, *rlast;
+} nfs;
+
+int
+fsreply(int fd, Fcall *f)
+{
+ uchar buf[IOHDRSZ+Maxfdata];
+ int n;
+
+ if(dbg)
+ fprint(2, "<-%F\n", f);
+ n = convS2M(f, buf, sizeof buf);
+ if(n > 0){
+ if(write(fd, buf, n) != n){
+ close(fd);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* match a note read request with a note, reply to the request */
+int
+kick(int fd)
+{
+ Request *rp;
+ Note *np;
+ int rv;
+
+ for(;;){
+ lock(&nfs);
+ rp = nfs.rfirst;
+ np = nfs.nfirst;
+ if(rp == nil || np == nil){
+ unlock(&nfs);
+ break;
+ }
+ nfs.rfirst = rp->next;
+ nfs.nfirst = np->next;
+ unlock(&nfs);
+
+ rp->f.type = Rread;
+ rp->f.count = strlen(np->msg);
+ rp->f.data = np->msg;
+ rv = fsreply(fd, &rp->f);
+ free(rp);
+ free(np);
+ if(rv < 0)
+ return -1;
+ }
+ return 0;
+}
+
+void
+flushreq(int tag)
+{
+ Request **l, *rp;
+
+ lock(&nfs);
+ for(l = &nfs.rfirst; *l != nil; l = &(*l)->next){
+ rp = *l;
+ if(rp->f.tag == tag){
+ *l = rp->next;
+ unlock(&nfs);
+ free(rp);
+ return;
+ }
+ }
+ unlock(&nfs);
+}
+
+Fid*
+getfid(int fid)
+{
+ int i, freefid;
+
+ freefid = -1;
+ for(i = 0; i < Nfid; i++){
+ if(freefid < 0 && fids[i].file < 0)
+ freefid = i;
+ if(fids[i].fid == fid)
+ return &fids[i];
+ }
+ if(freefid >= 0){
+ fids[freefid].fid = fid;
+ return &fids[freefid];
+ }
+ return nil;
+}
+
+int
+fsstat(int fd, Fid *fid, Fcall *f)
+{
+ Dir d;
+ uchar statbuf[256];
+
+ memset(&d, 0, sizeof(d));
+ d.name = fstab[fid->file].name;
+ d.uid = user;
+ d.gid = user;
+ d.muid = user;
+ d.qid = fstab[fid->file].qid;
+ d.mode = fstab[fid->file].perm;
+ d.atime = d.mtime = time(0);
+ f->stat = statbuf;
+ f->nstat = convD2M(&d, statbuf, sizeof statbuf);
+ return fsreply(fd, f);
+}
+
+int
+fsread(int fd, Fid *fid, Fcall *f)
+{
+ Dir d;
+ uchar buf[256];
+ Request *rp;
+
+ switch(fid->file){
+ default:
+ return -1;
+ case Qdir:
+ if(f->offset == 0 && f->count >0){
+ memset(&d, 0, sizeof(d));
+ d.name = fstab[Qcpunote].name;
+ d.uid = user;
+ d.gid = user;
+ d.muid = user;
+ d.qid = fstab[Qcpunote].qid;
+ d.mode = fstab[Qcpunote].perm;
+ d.atime = d.mtime = time(0);
+ f->count = convD2M(&d, buf, sizeof buf);
+ f->data = (char*)buf;
+ } else
+ f->count = 0;
+ return fsreply(fd, f);
+ case Qcpunote:
+ rp = mallocz(sizeof(*rp), 1);
+ if(rp == nil)
+ return -1;
+ rp->f = *f;
+ lock(&nfs);
+ if(nfs.rfirst == nil)
+ nfs.rfirst = rp;
+ else
+ nfs.rlast->next = rp;
+ nfs.rlast = rp;
+ unlock(&nfs);
+ return kick(fd);;
+ }
+}
+
+char Eperm[] = "permission denied";
+char Enofile[] = "out of files";
+char Enotdir[] = "not a directory";
+
+void
+notefs(int fd)
+{
+ uchar buf[IOHDRSZ+Maxfdata];
+ int i, j, n;
+ char err[ERRMAX];
+ Fcall f;
+ Fid *fid, *nfid;
+ int doreply;
+
+ rfork(RFNOTEG);
+ fmtinstall('F', fcallconv);
+
+ for(n = 0; n < Nfid; n++)
+ fids[n].file = -1;
+
+ for(;;){
+ n = read9pmsg(fd, buf, sizeof(buf));
+ if(n <= 0){
+ if(dbg)
+ fprint(2, "read9pmsg(%d) returns %d: %r\n", fd, n);
+ break;
+ }
+ if(convM2S(buf, n, &f) < 0)
+ break;
+ if(dbg)
+ fprint(2, "->%F\n", &f);
+ doreply = 1;
+ fid = getfid(f.fid);
+ if(fid == nil){
+nofids:
+ f.type = Rerror;
+ f.ename = Enofile;
+ fsreply(fd, &f);
+ continue;
+ }
+ switch(f.type++){
+ default:
+ f.type = Rerror;
+ f.ename = "unknown type";
+ break;
+ case Tflush:
+ flushreq(f.oldtag);
+ break;
+ case Tversion:
+ if(f.msize > IOHDRSZ+Maxfdata)
+ f.msize = IOHDRSZ+Maxfdata;
+ break;
+ case Tauth:
+ f.type = Rerror;
+ f.ename = "cpu: authentication not required";
+ break;
+ case Tattach:
+ f.qid = fstab[Qdir].qid;
+ fid->file = Qdir;
+ break;
+ case Twalk:
+ nfid = nil;
+ if(f.newfid != f.fid){
+ nfid = getfid(f.newfid);
+ if(nfid == nil)
+ goto nofids;
+ nfid->file = fid->file;
+ fid = nfid;
+ }
+
+ f.ename = nil;
+ for(i=0; i<f.nwname; i++){
+ if(i > MAXWELEM){
+ f.type = Rerror;
+ f.ename = "too many name elements";
+ break;
+ }
+ if(fid->file != Qdir){
+ f.type = Rerror;
+ f.ename = Enotdir;
+ break;
+ }
+ if(strcmp(f.wname[i], "cpunote") == 0){
+ fid->file = Qcpunote;
+ f.wqid[i] = fstab[Qcpunote].qid;
+ continue;
+ }
+ f.type = Rerror;
+ f.ename = err;
+ strcpy(err, "cpu: file \"");
+ for(j=0; j<=i; j++){
+ if(strlen(err)+1+strlen(f.wname[j])+32 > sizeof err)
+ break;
+ if(j != 0)
+ strcat(err, "/");
+ strcat(err, f.wname[j]);
+ }
+ strcat(err, "\" does not exist");
+ break;
+ }
+ if(nfid != nil && (f.ename != nil || i < f.nwname))
+ nfid ->file = -1;
+ if(f.type != Rerror)
+ f.nwqid = i;
+ break;
+ case Topen:
+ if(f.mode != OREAD){
+ f.type = Rerror;
+ f.ename = Eperm;
+ }
+ f.qid = fstab[fid->file].qid;
+ break;
+ case Tcreate:
+ f.type = Rerror;
+ f.ename = Eperm;
+ break;
+ case Tread:
+ if(fsread(fd, fid, &f) < 0)
+ goto err;
+ doreply = 0;
+ break;
+ case Twrite:
+ f.type = Rerror;
+ f.ename = Eperm;
+ break;
+ case Tclunk:
+ fid->file = -1;
+ break;
+ case Tremove:
+ f.type = Rerror;
+ f.ename = Eperm;
+ break;
+ case Tstat:
+ if(fsstat(fd, fid, &f) < 0)
+ goto err;
+ doreply = 0;
+ break;
+ case Twstat:
+ f.type = Rerror;
+ f.ename = Eperm;
+ break;
+ }
+ if(doreply)
+ if(fsreply(fd, &f) < 0)
+ break;
+ }
+err:
+ if(dbg)
+ fprint(2, "notefs exiting: %r\n");
+ close(fd);
+}
+
+char notebuf[ERRMAX];
+
+void
+catcher(void*, char *text)
+{
+ int n;
+
+ n = strlen(text);
+ if(n >= sizeof(notebuf))
+ n = sizeof(notebuf)-1;
+ memmove(notebuf, text, n);
+ notebuf[n] = '\0';
+ noted(NCONT);
+}
+
+/*
+ * mount in /dev a note file for the remote side to read.
+ */
+void
+lclnoteproc(int netfd)
+{
+ int exportfspid;
+ Waitmsg *w;
+ Note *np;
+ int pfd[2];
+
+ if(pipe(pfd) < 0){
+ fprint(2, "cpu: can't start note proc: pipe: %r\n");
+ return;
+ }
+
+ /* new proc mounts and returns to start exportfs */
+ switch(exportfspid = rfork(RFPROC|RFNAMEG|RFFDG|RFMEM)){
+ case -1:
+ fprint(2, "cpu: can't start note proc: rfork: %r\n");
+ return;
+ case 0:
+ close(pfd[0]);
+ if(mount(pfd[1], -1, "/dev", MBEFORE, "") < 0)
+ fprint(2, "cpu: can't mount note proc: %r\n");
+ close(pfd[1]);
+ return;
+ }
+
+ close(netfd);
+ close(pfd[1]);
+
+ /* new proc listens for note file system rpc's */
+ switch(rfork(RFPROC|RFNAMEG|RFMEM)){
+ case -1:
+ fprint(2, "cpu: can't start note proc: rfork1: %r\n");
+ _exits(0);
+ case 0:
+ notefs(pfd[0]);
+ _exits(0);
+ }
+
+ /* original proc waits for notes */
+ notify(catcher);
+ w = nil;
+ for(;;) {
+ *notebuf = 0;
+ free(w);
+ w = wait();
+ if(w == nil) {
+ if(*notebuf == 0)
+ break;
+ np = mallocz(sizeof(Note), 1);
+ if(np != nil){
+ strcpy(np->msg, notebuf);
+ lock(&nfs);
+ if(nfs.nfirst == nil)
+ nfs.nfirst = np;
+ else
+ nfs.nlast->next = np;
+ nfs.nlast = np;
+ unlock(&nfs);
+ kick(pfd[0]);
+ }
+ unlock(&nfs);
+ } else if(w->pid == exportfspid)
+ break;
+ }
+
+ if(w == nil)
+ exits(nil);
+ exits(w->msg);
+}
diff --git a/src/cmd/factotum/ctl.c b/src/cmd/factotum/ctl.c
new file mode 100644
index 00000000..df44b97d
--- /dev/null
+++ b/src/cmd/factotum/ctl.c
@@ -0,0 +1,158 @@
+#include "std.h"
+#include "dat.h"
+
+/*
+ * key attr=val... - add a key
+ * the attr=val pairs are protocol-specific.
+ * for example, both of these are valid:
+ * key p9sk1 gre cs.bell-labs.com mysecret
+ * key p9sk1 gre cs.bell-labs.com 11223344556677 fmt=des7hex
+ * delkey ... - delete a key
+ * if given, the attr=val pairs are used to narrow the search
+ * [maybe should require a password?]
+ *
+ * debug - toggle debugging
+ */
+
+static char *msg[] = {
+ "key",
+ "delkey",
+ "debug",
+};
+
+static int
+classify(char *s)
+{
+ int i;
+
+ for(i=0; i<nelem(msg); i++)
+ if(strcmp(msg[i], s) == 0)
+ return i;
+ return -1;
+}
+
+int
+ctlwrite(char *a)
+{
+ char *p;
+ int i, nmatch, ret;
+ Attr *attr, **l, **lpriv, **lprotos, *pa, *priv, *protos;
+ Key *k;
+ Proto *proto;
+
+ if(a[0] == '#' || a[0] == '\0')
+ return 0;
+
+ /*
+ * it would be nice to emit a warning of some sort here.
+ * we ignore all but the first line of the write. this helps
+ * both with things like "echo delkey >/mnt/factotum/ctl"
+ * and writes that (incorrectly) contain multiple key lines.
+ */
+ if(p = strchr(a, '\n')){
+ if(p[1] != '\0'){
+ werrstr("multiline write not allowed");
+ return -1;
+ }
+ *p = '\0';
+ }
+
+ if((p = strchr(a, ' ')) == nil)
+ p = "";
+ else
+ *p++ = '\0';
+ switch(classify(a)){
+ default:
+ werrstr("unknown verb");
+ return -1;
+ case 0: /* key */
+ attr = parseattr(p);
+ /* separate out proto= attributes */
+ lprotos = &protos;
+ for(l=&attr; (*l); ){
+ if(strcmp((*l)->name, "proto") == 0){
+ *lprotos = *l;
+ lprotos = &(*l)->next;
+ *l = (*l)->next;
+ }else
+ l = &(*l)->next;
+ }
+ *lprotos = nil;
+ if(protos == nil){
+ werrstr("key without protos");
+ freeattr(attr);
+ return -1;
+ }
+
+ /* separate out private attributes */
+ lpriv = &priv;
+ for(l=&attr; (*l); ){
+ if((*l)->name[0] == '!'){
+ *lpriv = *l;
+ lpriv = &(*l)->next;
+ *l = (*l)->next;
+ }else
+ l = &(*l)->next;
+ }
+ *lpriv = nil;
+
+ /* add keys */
+ ret = 0;
+ for(pa=protos; pa; pa=pa->next){
+ if((proto = protolookup(pa->val)) == nil){
+ werrstr("unknown proto %s", pa->val);
+ ret = -1;
+ continue;
+ }
+ if(proto->checkkey == nil){
+ werrstr("proto %s does not accept keys", proto->name);
+ ret = -1;
+ continue;
+ }
+ k = emalloc(sizeof(Key));
+ k->attr = mkattr(AttrNameval, "proto", proto->name, copyattr(attr));
+ k->privattr = copyattr(priv);
+ k->ref = 1;
+ k->proto = proto;
+ if((*proto->checkkey)(k) < 0){
+ ret = -1;
+ keyclose(k);
+ continue;
+ }
+ keyadd(k);
+ keyclose(k);
+ }
+ freeattr(attr);
+ freeattr(priv);
+ freeattr(protos);
+ return ret;
+ case 1: /* delkey */
+ nmatch = 0;
+ attr = parseattr(p);
+ for(pa=attr; pa; pa=pa->next){
+ if(pa->type != AttrQuery && pa->name[0]=='!'){
+ werrstr("only !private? patterns are allowed for private fields");
+ freeattr(attr);
+ return -1;
+ }
+ }
+ for(i=0; i<ring.nkey; ){
+ if(matchattr(attr, ring.key[i]->attr, ring.key[i]->privattr)){
+ nmatch++;
+ keyclose(ring.key[i]);
+ ring.nkey--;
+ memmove(&ring.key[i], &ring.key[i+1], (ring.nkey-i)*sizeof(ring.key[0]));
+ }else
+ i++;
+ }
+ freeattr(attr);
+ if(nmatch == 0){
+ werrstr("found no keys to delete");
+ return -1;
+ }
+ return 0;
+ case 2: /* debug */
+ debug ^= 1;
+ return 0;
+ }
+}
diff --git a/src/cmd/factotum/dat.h b/src/cmd/factotum/dat.h
new file mode 100644
index 00000000..e0c5b4fb
--- /dev/null
+++ b/src/cmd/factotum/dat.h
@@ -0,0 +1,224 @@
+enum
+{
+ MaxRpc = 2048, /* max size of any protocol message */
+
+ /* keep in sync with rpc.c:/rpcname */
+ RpcUnknown = 0, /* Rpc.op */
+ RpcAuthinfo,
+ RpcAttr,
+ RpcRead,
+ RpcStart,
+ RpcWrite,
+
+ /* thread stack size */
+ STACK = 8192,
+};
+
+typedef struct Conv Conv;
+typedef struct Key Key;
+typedef struct Logbuf Logbuf;
+typedef struct Proto Proto;
+typedef struct Ring Ring;
+typedef struct Role Role;
+typedef struct Rpc Rpc;
+
+struct Rpc
+{
+ int op;
+ void *data;
+ int count;
+};
+
+struct Conv
+{
+ int ref; /* ref count */
+ int hangup; /* flag: please hang up */
+ int active; /* flag: there is an active thread */
+ int done; /* flag: conversation finished successfully */
+ ulong tag; /* identifying tag */
+ Conv *next; /* in linked list */
+ char *sysuser; /* system name for user speaking to us */
+ char *state; /* for debugging */
+ char statebuf[128]; /* for formatted states */
+ char err[ERRMAX]; /* last error */
+
+ Attr *attr; /* current attributes */
+ Proto *proto; /* protocol */
+
+ Channel *rpcwait; /* wait here for an rpc */
+ Rpc rpc; /* current rpc. op==RpcUnknown means none */
+ char rpcbuf[MaxRpc]; /* buffer for rpc */
+ char reply[MaxRpc]; /* buffer for response */
+ int nreply; /* count of response */
+ void (*kickreply)(Conv*); /* call to send response */
+ Req *req; /* 9P call to read response */
+
+ Channel *keywait; /* wait here for key confirmation */
+
+};
+
+struct Key
+{
+ int ref; /* ref count */
+ ulong tag; /* identifying tag: sequence number */
+ Attr *attr; /* public attributes */
+ Attr *privattr; /* private attributes, like !password */
+ Proto *proto; /* protocol owner of key */
+ void *priv; /* protocol-specific storage */
+};
+
+struct Logbuf
+{
+ Req *wait;
+ Req **waitlast;
+ int rp;
+ int wp;
+ char *msg[128];
+};
+
+struct Ring
+{
+ Key **key;
+ int nkey;
+};
+
+struct Proto
+{
+ char *name; /* name of protocol */
+ Role *roles; /* list of roles and service functions */
+ char *keyprompt; /* required attributes for key proto=name */
+ int (*checkkey)(Key*); /* initialize k->priv or reject key */
+ void (*closekey)(Key*); /* free k->priv */
+};
+
+struct Role
+{
+ char *name; /* name of role */
+ int (*fn)(Conv*); /* service function */
+};
+
+extern char *authaddr; /* plan9.c */
+extern int *confirminuse; /* fs.c */
+extern Conv* conv; /* conv.c */
+extern int debug; /* main.c */
+extern char *factname; /* main.c */
+extern Srv fs; /* fs.c */
+extern int *needkeyinuse; /* fs.c */
+extern char *owner; /* main.c */
+extern Proto *prototab[]; /* main.c */
+extern Ring ring; /* key.c */
+extern char *rpcname[]; /* rpc.c */
+
+extern char Easproto[]; /* err.c */
+
+extern Proto apop; /* apop.c */
+extern Proto chap; /* chap.c */
+extern Proto cram; /* cram.c */
+extern Proto mschap; /* mschap.c */
+extern Proto p9any; /* p9any.c */
+extern Proto p9sk1; /* p9sk1.c */
+extern Proto p9sk2; /* p9sk2.c */
+
+/* provided by lib9p */
+#define emalloc emalloc9p
+#define erealloc erealloc9p
+#define estrdup estrdup9p
+
+/* hidden in libauth */
+#define attrfmt _attrfmt
+#define copyattr _copyattr
+#define delattr _delattr
+#define findattr _findattr
+#define freeattr _freeattr
+#define mkattr _mkattr
+#define parseattr _parseattr
+#define strfindattr _strfindattr
+
+extern Attr* addattr(Attr*, char*, ...);
+/* #pragma varargck argpos addattr 2 */
+extern Attr* addattrs(Attr*, Attr*);
+extern Attr* sortattr(Attr*);
+extern int attrnamefmt(Fmt*);
+/* #pragma varargck type "N" Attr* */
+extern int matchattr(Attr*, Attr*, Attr*);
+extern Attr* parseattrfmt(char*, ...);
+/* #pragma varargck argpos parseattrfmt 1 */
+extern Attr* parseattrfmtv(char*, va_list);
+
+extern void confirmflush(Req*);
+extern void confirmread(Req*);
+extern int confirmwrite(char*);
+extern int needkey(Conv*, Attr*);
+extern int badkey(Conv*, Key*, char*, Attr*);
+extern int confirmkey(Conv*, Key*);
+
+extern Conv* convalloc(char*);
+extern void convclose(Conv*);
+extern void convhangup(Conv*);
+extern int convneedkey(Conv*, Attr*);
+extern int convbadkey(Conv*, Key*, char*, Attr*);
+extern int convread(Conv*, void*, int);
+extern int convreadm(Conv*, char**);
+extern int convprint(Conv*, char*, ...);
+/* #pragma varargck argpos convprint 2 */
+extern int convreadfn(Conv*, int(*)(void*, int), char**);
+extern void convreset(Conv*);
+extern int convwrite(Conv*, void*, int);
+
+extern int ctlwrite(char*);
+
+extern char* estrappend(char*, char*, ...);
+/* #pragma varargck argpos estrappend 2 */
+extern int hexparse(char*, uchar*, int);
+
+extern void keyadd(Key*);
+extern Key* keylookup(char*, ...);
+/* #pragma varargck argpos keylookup 1 */
+extern Key* keyfetch(Conv*, char*, ...);
+/* #pragma varargck argpos keyfetch 2 */
+extern void keyclose(Key*);
+extern void keyevict(Conv*, Key*, char*, ...);
+/* #pragma varargck argpos keyevict 3 */
+extern Key* keyreplace(Conv*, Key*, char*, ...);
+/* #pragma varargck argpos keyreplace 3 */
+
+extern void lbkick(Logbuf*);
+extern void lbappend(Logbuf*, char*, ...);
+extern void lbvappend(Logbuf*, char*, va_list);
+/* #pragma varargck argpos lbappend 2 */
+extern void lbread(Logbuf*, Req*);
+extern void lbflush(Logbuf*, Req*);
+extern void flog(char*, ...);
+/* #pragma varargck argpos flog 1 */
+
+extern void logflush(Req*);
+extern void logread(Req*);
+extern void logwrite(Req*);
+
+extern void needkeyread(Req*);
+extern void needkeyflush(Req*);
+extern int needkeywrite(char*);
+extern int needkeyqueue(void);
+
+extern Attr* addcap(Attr*, char*, Ticket*);
+extern Key* plan9authkey(Attr*);
+extern int _authdial(char*, char*);
+
+extern int memrandom(void*, int);
+
+extern Proto* protolookup(char*);
+
+extern char* readcons(char *prompt, char *def, int raw);
+
+extern int rpcwrite(Conv*, void*, int);
+extern void rpcrespond(Conv*, char*, ...);
+/* #pragma varargck argpos rpcrespond 2 */
+extern void rpcrespondn(Conv*, char*, void*, int);
+extern void rpcexec(Conv*);
+
+extern int xioauthdial(char*, char*);
+extern void xioclose(int);
+extern int xiodial(char*, char*, char*, int*);
+extern int xiowrite(int, void*, int);
+extern int xioasrdresp(int, void*, int);
+extern int xioasgetticket(int, char*, char*);
diff --git a/src/cmd/factotum/fs.acid b/src/cmd/factotum/fs.acid
new file mode 100644
index 00000000..47d2a4e1
--- /dev/null
+++ b/src/cmd/factotum/fs.acid
@@ -0,0 +1,1686 @@
+sizeof_1_ = 8;
+aggr _1_
+{
+ 'D' 0 lo;
+ 'D' 4 hi;
+};
+
+defn
+_1_(addr) {
+ complex _1_ addr;
+ print(" lo ", addr.lo, "\n");
+ print(" hi ", addr.hi, "\n");
+};
+
+sizeofFPdbleword = 8;
+aggr FPdbleword
+{
+ 'F' 0 x;
+ {
+ 'D' 0 lo;
+ 'D' 4 hi;
+ };
+};
+
+defn
+FPdbleword(addr) {
+ complex FPdbleword addr;
+ print(" x ", addr.x, "\n");
+ print("_1_ {\n");
+ _1_(addr+0);
+ print("}\n");
+};
+
+UTFmax = 3;
+Runesync = 128;
+Runeself = 128;
+Runeerror = 128;
+sizeofFconv = 24;
+aggr Fconv
+{
+ 'X' 0 out;
+ 'X' 4 eout;
+ 'D' 8 f1;
+ 'D' 12 f2;
+ 'D' 16 f3;
+ 'D' 20 chr;
+};
+
+defn
+Fconv(addr) {
+ complex Fconv addr;
+ print(" out ", addr.out\X, "\n");
+ print(" eout ", addr.eout\X, "\n");
+ print(" f1 ", addr.f1, "\n");
+ print(" f2 ", addr.f2, "\n");
+ print(" f3 ", addr.f3, "\n");
+ print(" chr ", addr.chr, "\n");
+};
+
+sizeofTm = 40;
+aggr Tm
+{
+ 'D' 0 sec;
+ 'D' 4 min;
+ 'D' 8 hour;
+ 'D' 12 mday;
+ 'D' 16 mon;
+ 'D' 20 year;
+ 'D' 24 wday;
+ 'D' 28 yday;
+ 'a' 32 zone;
+ 'D' 36 tzoff;
+};
+
+defn
+Tm(addr) {
+ complex Tm addr;
+ print(" sec ", addr.sec, "\n");
+ print(" min ", addr.min, "\n");
+ print(" hour ", addr.hour, "\n");
+ print(" mday ", addr.mday, "\n");
+ print(" mon ", addr.mon, "\n");
+ print(" year ", addr.year, "\n");
+ print(" wday ", addr.wday, "\n");
+ print(" yday ", addr.yday, "\n");
+ print(" zone ", addr.zone, "\n");
+ print(" tzoff ", addr.tzoff, "\n");
+};
+
+PNPROC = 1;
+PNGROUP = 2;
+sizeofLock = 4;
+aggr Lock
+{
+ 'D' 0 val;
+};
+
+defn
+Lock(addr) {
+ complex Lock addr;
+ print(" val ", addr.val, "\n");
+};
+
+sizeofQLp = 12;
+aggr QLp
+{
+ 'D' 0 inuse;
+ 'A' QLp 4 next;
+ 'C' 8 state;
+};
+
+defn
+QLp(addr) {
+ complex QLp addr;
+ print(" inuse ", addr.inuse, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" state ", addr.state, "\n");
+};
+
+sizeofQLock = 16;
+aggr QLock
+{
+ Lock 0 lock;
+ 'D' 4 locked;
+ 'A' QLp 8 $head;
+ 'A' QLp 12 $tail;
+};
+
+defn
+QLock(addr) {
+ complex QLock addr;
+ print("Lock lock {\n");
+ Lock(addr.lock);
+ print("}\n");
+ print(" locked ", addr.locked, "\n");
+ print(" $head ", addr.$head\X, "\n");
+ print(" $tail ", addr.$tail\X, "\n");
+};
+
+sizeofRWLock = 20;
+aggr RWLock
+{
+ Lock 0 lock;
+ 'D' 4 readers;
+ 'D' 8 writer;
+ 'A' QLp 12 $head;
+ 'A' QLp 16 $tail;
+};
+
+defn
+RWLock(addr) {
+ complex RWLock addr;
+ print("Lock lock {\n");
+ Lock(addr.lock);
+ print("}\n");
+ print(" readers ", addr.readers, "\n");
+ print(" writer ", addr.writer, "\n");
+ print(" $head ", addr.$head\X, "\n");
+ print(" $tail ", addr.$tail\X, "\n");
+};
+
+RFNAMEG = 1;
+RFENVG = 2;
+RFFDG = 4;
+RFNOTEG = 8;
+RFPROC = 16;
+RFMEM = 32;
+RFNOWAIT = 64;
+RFCNAMEG = 1024;
+RFCENVG = 2048;
+RFCFDG = 4096;
+RFREND = 8192;
+RFNOMNT = 16384;
+sizeofQid = 16;
+aggr Qid
+{
+ 'W' 0 path;
+ 'U' 8 vers;
+ 'b' 12 type;
+};
+
+defn
+Qid(addr) {
+ complex Qid addr;
+ print(" path ", addr.path, "\n");
+ print(" vers ", addr.vers, "\n");
+ print(" type ", addr.type, "\n");
+};
+
+sizeofDir = 60;
+aggr Dir
+{
+ 'u' 0 type;
+ 'U' 4 dev;
+ Qid 8 qid;
+ 'U' 24 mode;
+ 'U' 28 atime;
+ 'U' 32 mtime;
+ 'V' 36 length;
+ 'X' 44 name;
+ 'X' 48 uid;
+ 'X' 52 gid;
+ 'X' 56 muid;
+};
+
+defn
+Dir(addr) {
+ complex Dir addr;
+ print(" type ", addr.type, "\n");
+ print(" dev ", addr.dev, "\n");
+ print("Qid qid {\n");
+ Qid(addr.qid);
+ print("}\n");
+ print(" mode ", addr.mode, "\n");
+ print(" atime ", addr.atime, "\n");
+ print(" mtime ", addr.mtime, "\n");
+ print(" length ", addr.length, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print(" gid ", addr.gid\X, "\n");
+ print(" muid ", addr.muid\X, "\n");
+};
+
+sizeofWaitmsg = 20;
+aggr Waitmsg
+{
+ 'D' 0 pid;
+ 'a' 4 time;
+ 'X' 16 msg;
+};
+
+defn
+Waitmsg(addr) {
+ complex Waitmsg addr;
+ print(" pid ", addr.pid, "\n");
+ print(" time ", addr.time, "\n");
+ print(" msg ", addr.msg\X, "\n");
+};
+
+sizeofIOchunk = 8;
+aggr IOchunk
+{
+ 'X' 0 addr;
+ 'U' 4 len;
+};
+
+defn
+IOchunk(addr) {
+ complex IOchunk addr;
+ print(" addr ", addr.addr\X, "\n");
+ print(" len ", addr.len, "\n");
+};
+
+MAXCHLEN = 256;
+MAXNAMELEN = 256;
+MD5LEN = 16;
+ARok = 0;
+ARdone = 1;
+ARerror = 2;
+ARneedkey = 3;
+ARbadkey = 4;
+ARwritenext = 5;
+ARtoosmall = 6;
+ARtoobig = 7;
+ARrpcfailure = 8;
+ARphase = 9;
+AuthRpcMax = 4096;
+sizeofAuthRpc = 8208;
+aggr AuthRpc
+{
+ 'D' 0 afd;
+ 'X' 4 verb;
+ 'a' 8 ibuf;
+ 'a' 4104 obuf;
+ 'X' 8200 arg;
+ 'U' 8204 narg;
+};
+
+defn
+AuthRpc(addr) {
+ complex AuthRpc addr;
+ print(" afd ", addr.afd, "\n");
+ print(" verb ", addr.verb\X, "\n");
+ print(" ibuf ", addr.ibuf, "\n");
+ print(" obuf ", addr.obuf, "\n");
+ print(" arg ", addr.arg\X, "\n");
+ print(" narg ", addr.narg, "\n");
+};
+
+sizeofAuthInfo = 20;
+aggr AuthInfo
+{
+ 'X' 0 cuid;
+ 'X' 4 suid;
+ 'X' 8 cap;
+ 'D' 12 nsecret;
+ 'X' 16 secret;
+};
+
+defn
+AuthInfo(addr) {
+ complex AuthInfo addr;
+ print(" cuid ", addr.cuid\X, "\n");
+ print(" suid ", addr.suid\X, "\n");
+ print(" cap ", addr.cap\X, "\n");
+ print(" nsecret ", addr.nsecret, "\n");
+ print(" secret ", addr.secret\X, "\n");
+};
+
+sizeofChalstate = 540;
+aggr Chalstate
+{
+ 'X' 0 user;
+ 'a' 4 chal;
+ 'D' 260 nchal;
+ 'X' 264 resp;
+ 'D' 268 nresp;
+ 'D' 272 afd;
+ 'A' AuthRpc 276 rpc;
+ 'a' 280 userbuf;
+ 'D' 536 userinchal;
+};
+
+defn
+Chalstate(addr) {
+ complex Chalstate addr;
+ print(" user ", addr.user\X, "\n");
+ print(" chal ", addr.chal, "\n");
+ print(" nchal ", addr.nchal, "\n");
+ print(" resp ", addr.resp\X, "\n");
+ print(" nresp ", addr.nresp, "\n");
+ print(" afd ", addr.afd, "\n");
+ print(" rpc ", addr.rpc\X, "\n");
+ print(" userbuf ", addr.userbuf, "\n");
+ print(" userinchal ", addr.userinchal, "\n");
+};
+
+sizeofChapreply = 20;
+aggr Chapreply
+{
+ 'b' 0 id;
+ 'a' 1 resp;
+};
+
+defn
+Chapreply(addr) {
+ complex Chapreply addr;
+ print(" id ", addr.id, "\n");
+ print(" resp ", addr.resp, "\n");
+};
+
+sizeofMSchapreply = 48;
+aggr MSchapreply
+{
+ 'a' 0 LMresp;
+ 'a' 24 NTresp;
+};
+
+defn
+MSchapreply(addr) {
+ complex MSchapreply addr;
+ print(" LMresp ", addr.LMresp, "\n");
+ print(" NTresp ", addr.NTresp, "\n");
+};
+
+sizeofUserPasswd = 8;
+aggr UserPasswd
+{
+ 'X' 0 user;
+ 'X' 4 passwd;
+};
+
+defn
+UserPasswd(addr) {
+ complex UserPasswd addr;
+ print(" user ", addr.user\X, "\n");
+ print(" passwd ", addr.passwd\X, "\n");
+};
+
+ANAMELEN = 28;
+AERRLEN = 64;
+DOMLEN = 48;
+DESKEYLEN = 7;
+CHALLEN = 8;
+NETCHLEN = 16;
+CONFIGLEN = 14;
+SECRETLEN = 32;
+KEYDBOFF = 8;
+OKEYDBLEN = 41;
+KEYDBLEN = 73;
+OMD5LEN = 16;
+AuthTreq = 1;
+AuthChal = 2;
+AuthPass = 3;
+AuthOK = 4;
+AuthErr = 5;
+AuthMod = 6;
+AuthApop = 7;
+AuthOKvar = 9;
+AuthChap = 10;
+AuthMSchap = 11;
+AuthCram = 12;
+AuthHttp = 13;
+AuthVNC = 14;
+AuthTs = 64;
+AuthTc = 65;
+AuthAs = 66;
+AuthAc = 67;
+AuthTp = 68;
+AuthHr = 69;
+sizeofTicketreq = 144;
+aggr Ticketreq
+{
+ 'C' 0 type;
+ 'a' 1 authid;
+ 'a' 29 authdom;
+ 'a' 77 chal;
+ 'a' 85 hostid;
+ 'a' 113 uid;
+};
+
+defn
+Ticketreq(addr) {
+ complex Ticketreq addr;
+ print(" type ", addr.type, "\n");
+ print(" authid ", addr.authid, "\n");
+ print(" authdom ", addr.authdom, "\n");
+ print(" chal ", addr.chal, "\n");
+ print(" hostid ", addr.hostid, "\n");
+ print(" uid ", addr.uid, "\n");
+};
+
+sizeofTicket = 72;
+aggr Ticket
+{
+ 'C' 0 num;
+ 'a' 1 chal;
+ 'a' 9 cuid;
+ 'a' 37 suid;
+ 'a' 65 key;
+};
+
+defn
+Ticket(addr) {
+ complex Ticket addr;
+ print(" num ", addr.num, "\n");
+ print(" chal ", addr.chal, "\n");
+ print(" cuid ", addr.cuid, "\n");
+ print(" suid ", addr.suid, "\n");
+ print(" key ", addr.key, "\n");
+};
+
+sizeofAuthenticator = 16;
+aggr Authenticator
+{
+ 'C' 0 num;
+ 'a' 1 chal;
+ 'U' 12 id;
+};
+
+defn
+Authenticator(addr) {
+ complex Authenticator addr;
+ print(" num ", addr.num, "\n");
+ print(" chal ", addr.chal, "\n");
+ print(" id ", addr.id, "\n");
+};
+
+sizeofPasswordreq = 92;
+aggr Passwordreq
+{
+ 'C' 0 num;
+ 'a' 1 old;
+ 'a' 29 new;
+ 'C' 57 changesecret;
+ 'a' 58 secret;
+};
+
+defn
+Passwordreq(addr) {
+ complex Passwordreq addr;
+ print(" num ", addr.num, "\n");
+ print(" old ", addr.old, "\n");
+ print(" new ", addr.new, "\n");
+ print(" changesecret ", addr.changesecret, "\n");
+ print(" secret ", addr.secret, "\n");
+};
+
+sizeofOChapreply = 48;
+aggr OChapreply
+{
+ 'b' 0 id;
+ 'a' 1 uid;
+ 'a' 29 resp;
+};
+
+defn
+OChapreply(addr) {
+ complex OChapreply addr;
+ print(" id ", addr.id, "\n");
+ print(" uid ", addr.uid, "\n");
+ print(" resp ", addr.resp, "\n");
+};
+
+sizeofOMSchapreply = 76;
+aggr OMSchapreply
+{
+ 'a' 0 uid;
+ 'a' 28 LMresp;
+ 'a' 52 NTresp;
+};
+
+defn
+OMSchapreply(addr) {
+ complex OMSchapreply addr;
+ print(" uid ", addr.uid, "\n");
+ print(" LMresp ", addr.LMresp, "\n");
+ print(" NTresp ", addr.NTresp, "\n");
+};
+
+NVwrite = 1;
+NVwriteonerr = 2;
+sizeofNvrsafe = 112;
+aggr Nvrsafe
+{
+ 'a' 0 machkey;
+ 'b' 7 machsum;
+ 'a' 8 authkey;
+ 'b' 15 authsum;
+ 'a' 16 config;
+ 'b' 30 configsum;
+ 'a' 31 authid;
+ 'b' 59 authidsum;
+ 'a' 60 authdom;
+ 'b' 108 authdomsum;
+};
+
+defn
+Nvrsafe(addr) {
+ complex Nvrsafe addr;
+ print(" machkey ", addr.machkey, "\n");
+ print(" machsum ", addr.machsum, "\n");
+ print(" authkey ", addr.authkey, "\n");
+ print(" authsum ", addr.authsum, "\n");
+ print(" config ", addr.config, "\n");
+ print(" configsum ", addr.configsum, "\n");
+ print(" authid ", addr.authid, "\n");
+ print(" authidsum ", addr.authidsum, "\n");
+ print(" authdom ", addr.authdom, "\n");
+ print(" authdomsum ", addr.authdomsum, "\n");
+};
+
+AESbsize = 16;
+AESmaxkey = 32;
+AESmaxrounds = 14;
+sizeofAESstate = 540;
+aggr AESstate
+{
+ 'U' 0 setup;
+ 'D' 4 rounds;
+ 'D' 8 keybytes;
+ 'a' 12 key;
+ 'a' 44 ekey;
+ 'a' 284 dkey;
+ 'a' 524 ivec;
+};
+
+defn
+AESstate(addr) {
+ complex AESstate addr;
+ print(" setup ", addr.setup, "\n");
+ print(" rounds ", addr.rounds, "\n");
+ print(" keybytes ", addr.keybytes, "\n");
+ print(" key ", addr.key, "\n");
+ print(" ekey ", addr.ekey, "\n");
+ print(" dkey ", addr.dkey, "\n");
+ print(" ivec ", addr.ivec, "\n");
+};
+
+BFbsize = 8;
+BFrounds = 16;
+sizeofBFstate = 4236;
+aggr BFstate
+{
+ 'U' 0 setup;
+ 'a' 4 key;
+ 'a' 60 ivec;
+ 'a' 68 pbox;
+ 'a' 140 sbox;
+};
+
+defn
+BFstate(addr) {
+ complex BFstate addr;
+ print(" setup ", addr.setup, "\n");
+ print(" key ", addr.key, "\n");
+ print(" ivec ", addr.ivec, "\n");
+ print(" pbox ", addr.pbox, "\n");
+ print(" sbox ", addr.sbox, "\n");
+};
+
+DESbsize = 8;
+sizeofDESstate = 148;
+aggr DESstate
+{
+ 'U' 0 setup;
+ 'a' 4 key;
+ 'a' 12 expanded;
+ 'a' 140 ivec;
+};
+
+defn
+DESstate(addr) {
+ complex DESstate addr;
+ print(" setup ", addr.setup, "\n");
+ print(" key ", addr.key, "\n");
+ print(" expanded ", addr.expanded, "\n");
+ print(" ivec ", addr.ivec, "\n");
+};
+
+DES3E = 0;
+DES3D = 1;
+DES3EEE = 0;
+DES3EDE = 2;
+DES3DED = 5;
+DES3DDD = 7;
+sizeofDES3state = 420;
+aggr DES3state
+{
+ 'U' 0 setup;
+ 'a' 4 key;
+ 'a' 28 expanded;
+ 'a' 412 ivec;
+};
+
+defn
+DES3state(addr) {
+ complex DES3state addr;
+ print(" setup ", addr.setup, "\n");
+ print(" key ", addr.key, "\n");
+ print(" expanded ", addr.expanded, "\n");
+ print(" ivec ", addr.ivec, "\n");
+};
+
+SHA1dlen = 20;
+MD4dlen = 16;
+MD5dlen = 16;
+sizeofDigestState = 160;
+aggr DigestState
+{
+ 'U' 0 len;
+ 'a' 4 state;
+ 'a' 24 buf;
+ 'D' 152 blen;
+ 'C' 156 malloced;
+ 'C' 157 seeded;
+};
+
+defn
+DigestState(addr) {
+ complex DigestState addr;
+ print(" len ", addr.len, "\n");
+ print(" state ", addr.state, "\n");
+ print(" buf ", addr.buf, "\n");
+ print(" blen ", addr.blen, "\n");
+ print(" malloced ", addr.malloced, "\n");
+ print(" seeded ", addr.seeded, "\n");
+};
+
+sizeofRC4state = 260;
+aggr RC4state
+{
+ 'a' 0 state;
+ 'b' 256 x;
+ 'b' 257 y;
+};
+
+defn
+RC4state(addr) {
+ complex RC4state addr;
+ print(" state ", addr.state, "\n");
+ print(" x ", addr.x, "\n");
+ print(" y ", addr.y, "\n");
+};
+
+sizeofRSApub = 8;
+aggr RSApub
+{
+ 'X' 0 n;
+ 'X' 4 ek;
+};
+
+defn
+RSApub(addr) {
+ complex RSApub addr;
+ print(" n ", addr.n\X, "\n");
+ print(" ek ", addr.ek\X, "\n");
+};
+
+sizeofRSApriv = 32;
+aggr RSApriv
+{
+ RSApub 0 pub;
+ 'X' 8 dk;
+ 'X' 12 p;
+ 'X' 16 q;
+ 'X' 20 kp;
+ 'X' 24 kq;
+ 'X' 28 c2;
+};
+
+defn
+RSApriv(addr) {
+ complex RSApriv addr;
+ print("RSApub pub {\n");
+ RSApub(addr.pub);
+ print("}\n");
+ print(" dk ", addr.dk\X, "\n");
+ print(" p ", addr.p\X, "\n");
+ print(" q ", addr.q\X, "\n");
+ print(" kp ", addr.kp\X, "\n");
+ print(" kq ", addr.kq\X, "\n");
+ print(" c2 ", addr.c2\X, "\n");
+};
+
+sizeofEGpub = 12;
+aggr EGpub
+{
+ 'X' 0 p;
+ 'X' 4 alpha;
+ 'X' 8 key;
+};
+
+defn
+EGpub(addr) {
+ complex EGpub addr;
+ print(" p ", addr.p\X, "\n");
+ print(" alpha ", addr.alpha\X, "\n");
+ print(" key ", addr.key\X, "\n");
+};
+
+sizeofEGpriv = 16;
+aggr EGpriv
+{
+ EGpub 0 pub;
+ 'X' 12 secret;
+};
+
+defn
+EGpriv(addr) {
+ complex EGpriv addr;
+ print("EGpub pub {\n");
+ EGpub(addr.pub);
+ print("}\n");
+ print(" secret ", addr.secret\X, "\n");
+};
+
+sizeofEGsig = 8;
+aggr EGsig
+{
+ 'X' 0 r;
+ 'X' 4 s;
+};
+
+defn
+EGsig(addr) {
+ complex EGsig addr;
+ print(" r ", addr.r\X, "\n");
+ print(" s ", addr.s\X, "\n");
+};
+
+sizeofString = 20;
+aggr String
+{
+ {
+ 'D' 0 val;
+ };
+ 'X' 4 base;
+ 'X' 8 end;
+ 'X' 12 ptr;
+ 'd' 16 ref;
+ 'b' 18 fixed;
+};
+
+defn
+String(addr) {
+ complex String addr;
+ print("Lock {\n");
+ Lock(addr+0);
+ print("}\n");
+ print(" base ", addr.base\X, "\n");
+ print(" end ", addr.end\X, "\n");
+ print(" ptr ", addr.ptr\X, "\n");
+ print(" ref ", addr.ref, "\n");
+ print(" fixed ", addr.fixed, "\n");
+};
+
+sizeofChannel = 156;
+aggr Channel
+{
+ 'D' 0 s;
+ 'U' 4 f;
+ 'U' 8 n;
+ 'D' 12 e;
+ 'D' 16 freed;
+ 'U' 20 qused;
+ 'a' 24 qentry;
+ 'a' 152 v;
+};
+
+defn
+Channel(addr) {
+ complex Channel addr;
+ print(" s ", addr.s, "\n");
+ print(" f ", addr.f, "\n");
+ print(" n ", addr.n, "\n");
+ print(" e ", addr.e, "\n");
+ print(" freed ", addr.freed, "\n");
+ print(" qused ", addr.qused, "\n");
+ print(" qentry ", addr.qentry, "\n");
+ print(" v ", addr.v, "\n");
+};
+
+sizeofAlt = 20;
+aggr Alt
+{
+ 'A' Channel 0 c;
+ 'X' 4 v;
+ 'D' 8 op;
+ 'A' Channel 12 tag;
+ 'U' 16 q;
+};
+
+defn
+Alt(addr) {
+ complex Alt addr;
+ print(" c ", addr.c\X, "\n");
+ print(" v ", addr.v\X, "\n");
+ print(" op ", addr.op, "\n");
+ print(" tag ", addr.tag\X, "\n");
+ print(" q ", addr.q, "\n");
+};
+
+sizeofRef = 4;
+aggr Ref
+{
+ 'D' 0 ref;
+};
+
+defn
+Ref(addr) {
+ complex Ref addr;
+ print(" ref ", addr.ref, "\n");
+};
+
+sizeof_2_ = 8;
+aggr _2_
+{
+ 'U' 0 msize;
+ 'X' 4 version;
+};
+
+defn
+_2_(addr) {
+ complex _2_ addr;
+ print(" msize ", addr.msize, "\n");
+ print(" version ", addr.version\X, "\n");
+};
+
+sizeof_3_ = 4;
+aggr _3_
+{
+ 'u' 0 oldtag;
+};
+
+defn
+_3_(addr) {
+ complex _3_ addr;
+ print(" oldtag ", addr.oldtag, "\n");
+};
+
+sizeof_4_ = 4;
+aggr _4_
+{
+ 'X' 0 ename;
+};
+
+defn
+_4_(addr) {
+ complex _4_ addr;
+ print(" ename ", addr.ename\X, "\n");
+};
+
+sizeof_5_ = 20;
+aggr _5_
+{
+ Qid 0 qid;
+ 'U' 16 iounit;
+};
+
+defn
+_5_(addr) {
+ complex _5_ addr;
+ print("Qid qid {\n");
+ Qid(addr.qid);
+ print("}\n");
+ print(" iounit ", addr.iounit, "\n");
+};
+
+sizeof_6_ = 16;
+aggr _6_
+{
+ Qid 0 aqid;
+};
+
+defn
+_6_(addr) {
+ complex _6_ addr;
+ print("Qid aqid {\n");
+ Qid(addr.aqid);
+ print("}\n");
+};
+
+sizeof_7_ = 12;
+aggr _7_
+{
+ 'U' 0 afid;
+ 'X' 4 uname;
+ 'X' 8 aname;
+};
+
+defn
+_7_(addr) {
+ complex _7_ addr;
+ print(" afid ", addr.afid, "\n");
+ print(" uname ", addr.uname\X, "\n");
+ print(" aname ", addr.aname\X, "\n");
+};
+
+sizeof_8_ = 12;
+aggr _8_
+{
+ 'U' 0 perm;
+ 'X' 4 name;
+ 'b' 8 mode;
+};
+
+defn
+_8_(addr) {
+ complex _8_ addr;
+ print(" perm ", addr.perm, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" mode ", addr.mode, "\n");
+};
+
+sizeof_9_ = 72;
+aggr _9_
+{
+ 'U' 0 newfid;
+ 'u' 4 nwname;
+ 'a' 8 wname;
+};
+
+defn
+_9_(addr) {
+ complex _9_ addr;
+ print(" newfid ", addr.newfid, "\n");
+ print(" nwname ", addr.nwname, "\n");
+ print(" wname ", addr.wname, "\n");
+};
+
+sizeof_10_ = 260;
+aggr _10_
+{
+ 'u' 0 nwqid;
+ 'a' 4 wqid;
+};
+
+defn
+_10_(addr) {
+ complex _10_ addr;
+ print(" nwqid ", addr.nwqid, "\n");
+ print(" wqid ", addr.wqid, "\n");
+};
+
+sizeof_11_ = 16;
+aggr _11_
+{
+ 'V' 0 offset;
+ 'U' 8 count;
+ 'X' 12 data;
+};
+
+defn
+_11_(addr) {
+ complex _11_ addr;
+ print(" offset ", addr.offset, "\n");
+ print(" count ", addr.count, "\n");
+ print(" data ", addr.data\X, "\n");
+};
+
+sizeof_12_ = 8;
+aggr _12_
+{
+ 'u' 0 nstat;
+ 'X' 4 stat;
+};
+
+defn
+_12_(addr) {
+ complex _12_ addr;
+ print(" nstat ", addr.nstat, "\n");
+ print(" stat ", addr.stat\X, "\n");
+};
+
+sizeof_13_ = 260;
+aggr _13_
+{
+ {
+ 'U' 0 msize;
+ 'X' 4 version;
+ };
+ {
+ 'u' 0 oldtag;
+ };
+ {
+ 'X' 0 ename;
+ };
+ {
+ Qid 0 qid;
+ 'U' 16 iounit;
+ };
+ {
+ Qid 0 aqid;
+ };
+ {
+ 'U' 0 afid;
+ 'X' 4 uname;
+ 'X' 8 aname;
+ };
+ {
+ 'U' 0 perm;
+ 'X' 4 name;
+ 'b' 8 mode;
+ };
+ {
+ 'U' 0 newfid;
+ 'u' 4 nwname;
+ 'a' 8 wname;
+ };
+ {
+ 'u' 0 nwqid;
+ 'a' 4 wqid;
+ };
+ {
+ 'V' 0 offset;
+ 'U' 8 count;
+ 'X' 12 data;
+ };
+ {
+ 'u' 0 nstat;
+ 'X' 4 stat;
+ };
+};
+
+defn
+_13_(addr) {
+ complex _13_ addr;
+ print("_2_ {\n");
+ _2_(addr+0);
+ print("}\n");
+ print("_3_ {\n");
+ _3_(addr+0);
+ print("}\n");
+ print("_4_ {\n");
+ _4_(addr+0);
+ print("}\n");
+ print("_5_ {\n");
+ _5_(addr+0);
+ print("}\n");
+ print("_6_ {\n");
+ _6_(addr+0);
+ print("}\n");
+ print("_7_ {\n");
+ _7_(addr+0);
+ print("}\n");
+ print("_8_ {\n");
+ _8_(addr+0);
+ print("}\n");
+ print("_9_ {\n");
+ _9_(addr+0);
+ print("}\n");
+ print("_10_ {\n");
+ _10_(addr+0);
+ print("}\n");
+ print("_11_ {\n");
+ _11_(addr+0);
+ print("}\n");
+ print("_12_ {\n");
+ _12_(addr+0);
+ print("}\n");
+};
+
+sizeofFcall = 272;
+aggr Fcall
+{
+ 'b' 0 type;
+ 'U' 4 fid;
+ 'u' 8 tag;
+ {
+ {
+ 'U' 12 msize;
+ 'X' 16 version;
+ };
+ {
+ 'u' 12 oldtag;
+ };
+ {
+ 'X' 12 ename;
+ };
+ {
+ Qid 12 qid;
+ 'U' 28 iounit;
+ };
+ {
+ Qid 12 aqid;
+ };
+ {
+ 'U' 12 afid;
+ 'X' 16 uname;
+ 'X' 20 aname;
+ };
+ {
+ 'U' 12 perm;
+ 'X' 16 name;
+ 'b' 20 mode;
+ };
+ {
+ 'U' 12 newfid;
+ 'u' 16 nwname;
+ 'a' 20 wname;
+ };
+ {
+ 'u' 12 nwqid;
+ 'a' 16 wqid;
+ };
+ {
+ 'V' 12 offset;
+ 'U' 20 count;
+ 'X' 24 data;
+ };
+ {
+ 'u' 12 nstat;
+ 'X' 16 stat;
+ };
+ };
+};
+
+defn
+Fcall(addr) {
+ complex Fcall addr;
+ print(" type ", addr.type, "\n");
+ print(" fid ", addr.fid, "\n");
+ print(" tag ", addr.tag, "\n");
+ print("_13_ {\n");
+ _13_(addr+12);
+ print("}\n");
+};
+
+Tversion = 100;
+Rversion = 101;
+Tauth = 102;
+Rauth = 103;
+Tattach = 104;
+Rattach = 105;
+Terror = 106;
+Rerror = 107;
+Tflush = 108;
+Rflush = 109;
+Twalk = 110;
+Rwalk = 111;
+Topen = 112;
+Ropen = 113;
+Tcreate = 114;
+Rcreate = 115;
+Tread = 116;
+Rread = 117;
+Twrite = 118;
+Rwrite = 119;
+Tclunk = 120;
+Rclunk = 121;
+Tremove = 122;
+Rremove = 123;
+Tstat = 124;
+Rstat = 125;
+Twstat = 126;
+Rwstat = 127;
+Tmax = 128;
+sizeofFid = 60;
+aggr Fid
+{
+ 'U' 0 fid;
+ 'C' 4 omode;
+ 'X' 8 file;
+ 'X' 12 uid;
+ Qid 16 qid;
+ 'X' 32 aux;
+ 'X' 36 rdir;
+ Ref 40 ref;
+ 'X' 44 pool;
+ 'V' 48 diroffset;
+ 'D' 56 dirindex;
+};
+
+defn
+Fid(addr) {
+ complex Fid addr;
+ print(" fid ", addr.fid, "\n");
+ print(" omode ", addr.omode, "\n");
+ print(" file ", addr.file\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print("Qid qid {\n");
+ Qid(addr.qid);
+ print("}\n");
+ print(" aux ", addr.aux\X, "\n");
+ print(" rdir ", addr.rdir\X, "\n");
+ print("Ref ref {\n");
+ Ref(addr.ref);
+ print("}\n");
+ print(" pool ", addr.pool\X, "\n");
+ print(" diroffset ", addr.diroffset, "\n");
+ print(" dirindex ", addr.dirindex, "\n");
+};
+
+sizeofReq = 656;
+aggr Req
+{
+ 'U' 0 tag;
+ 'X' 4 aux;
+ Fcall 8 ifcall;
+ Fcall 280 ofcall;
+ Dir 552 d;
+ 'A' Req 612 oldreq;
+ 'A' Fid 616 fid;
+ 'A' Fid 620 afid;
+ 'A' Fid 624 newfid;
+ 'X' 628 srv;
+ Ref 632 ref;
+ 'X' 636 pool;
+ 'X' 640 buf;
+ 'b' 644 type;
+ 'b' 645 responded;
+ 'X' 648 error;
+ 'X' 652 rbuf;
+};
+
+defn
+Req(addr) {
+ complex Req addr;
+ print(" tag ", addr.tag, "\n");
+ print(" aux ", addr.aux\X, "\n");
+ print("Fcall ifcall {\n");
+ Fcall(addr.ifcall);
+ print("}\n");
+ print("Fcall ofcall {\n");
+ Fcall(addr.ofcall);
+ print("}\n");
+ print("Dir d {\n");
+ Dir(addr.d);
+ print("}\n");
+ print(" oldreq ", addr.oldreq\X, "\n");
+ print(" fid ", addr.fid\X, "\n");
+ print(" afid ", addr.afid\X, "\n");
+ print(" newfid ", addr.newfid\X, "\n");
+ print(" srv ", addr.srv\X, "\n");
+ print("Ref ref {\n");
+ Ref(addr.ref);
+ print("}\n");
+ print(" pool ", addr.pool\X, "\n");
+ print(" buf ", addr.buf\X, "\n");
+ print(" type ", addr.type, "\n");
+ print(" responded ", addr.responded, "\n");
+ print(" error ", addr.error\X, "\n");
+ print(" rbuf ", addr.rbuf\X, "\n");
+};
+
+sizeofFidpool = 12;
+aggr Fidpool
+{
+ 'X' 0 map;
+ 'X' 4 destroy;
+ 'X' 8 srv;
+};
+
+defn
+Fidpool(addr) {
+ complex Fidpool addr;
+ print(" map ", addr.map\X, "\n");
+ print(" destroy ", addr.destroy\X, "\n");
+ print(" srv ", addr.srv\X, "\n");
+};
+
+sizeofReqpool = 12;
+aggr Reqpool
+{
+ 'X' 0 map;
+ 'X' 4 destroy;
+ 'X' 8 srv;
+};
+
+defn
+Reqpool(addr) {
+ complex Reqpool addr;
+ print(" map ", addr.map\X, "\n");
+ print(" destroy ", addr.destroy\X, "\n");
+ print(" srv ", addr.srv\X, "\n");
+};
+
+sizeofFile = 108;
+aggr File
+{
+ {
+ 'D' 0 ref;
+ };
+ {
+ 'u' 4 type;
+ 'U' 8 dev;
+ Qid 12 qid;
+ 'U' 28 mode;
+ 'U' 32 atime;
+ 'U' 36 mtime;
+ 'V' 40 length;
+ 'X' 48 name;
+ 'X' 52 uid;
+ 'X' 56 gid;
+ 'X' 60 muid;
+ };
+ 'A' File 64 parent;
+ 'X' 68 aux;
+ {
+ Lock 72 lock;
+ 'D' 76 readers;
+ 'D' 80 writer;
+ 'A' QLp 84 $head;
+ 'A' QLp 88 $tail;
+ };
+ 'X' 92 filelist;
+ 'X' 96 tree;
+ 'D' 100 nchild;
+ 'D' 104 allocd;
+};
+
+defn
+File(addr) {
+ complex File addr;
+ print("Ref {\n");
+ Ref(addr+0);
+ print("}\n");
+ print("Dir {\n");
+ Dir(addr+4);
+ print("}\n");
+ print(" parent ", addr.parent\X, "\n");
+ print(" aux ", addr.aux\X, "\n");
+ print("RWLock {\n");
+ RWLock(addr+72);
+ print("}\n");
+ print(" filelist ", addr.filelist\X, "\n");
+ print(" tree ", addr.tree\X, "\n");
+ print(" nchild ", addr.nchild, "\n");
+ print(" allocd ", addr.allocd, "\n");
+};
+
+sizeofTree = 20;
+aggr Tree
+{
+ 'A' File 0 root;
+ 'X' 4 destroy;
+ Lock 8 genlock;
+ 'U' 12 qidgen;
+ 'U' 16 dirqidgen;
+};
+
+defn
+Tree(addr) {
+ complex Tree addr;
+ print(" root ", addr.root\X, "\n");
+ print(" destroy ", addr.destroy\X, "\n");
+ print("Lock genlock {\n");
+ Lock(addr.genlock);
+ print("}\n");
+ print(" qidgen ", addr.qidgen, "\n");
+ print(" dirqidgen ", addr.dirqidgen, "\n");
+};
+
+sizeofSrv = 136;
+aggr Srv
+{
+ 'A' Tree 0 tree;
+ 'X' 4 destroyfid;
+ 'X' 8 destroyreq;
+ 'X' 12 end;
+ 'X' 16 aux;
+ 'X' 20 attach;
+ 'X' 24 auth;
+ 'X' 28 open;
+ 'X' 32 create;
+ 'X' 36 read;
+ 'X' 40 write;
+ 'X' 44 remove;
+ 'X' 48 flush;
+ 'X' 52 stat;
+ 'X' 56 wstat;
+ 'X' 60 walk;
+ 'X' 64 clone;
+ 'X' 68 walk1;
+ 'D' 72 infd;
+ 'D' 76 outfd;
+ 'D' 80 nopipe;
+ 'A' Fidpool 84 fpool;
+ 'A' Reqpool 88 rpool;
+ 'U' 92 msize;
+ 'X' 96 rbuf;
+ QLock 100 rlock;
+ 'X' 116 wbuf;
+ QLock 120 wlock;
+};
+
+defn
+Srv(addr) {
+ complex Srv addr;
+ print(" tree ", addr.tree\X, "\n");
+ print(" destroyfid ", addr.destroyfid\X, "\n");
+ print(" destroyreq ", addr.destroyreq\X, "\n");
+ print(" end ", addr.end\X, "\n");
+ print(" aux ", addr.aux\X, "\n");
+ print(" attach ", addr.attach\X, "\n");
+ print(" auth ", addr.auth\X, "\n");
+ print(" open ", addr.open\X, "\n");
+ print(" create ", addr.create\X, "\n");
+ print(" read ", addr.read\X, "\n");
+ print(" write ", addr.write\X, "\n");
+ print(" remove ", addr.remove\X, "\n");
+ print(" flush ", addr.flush\X, "\n");
+ print(" stat ", addr.stat\X, "\n");
+ print(" wstat ", addr.wstat\X, "\n");
+ print(" walk ", addr.walk\X, "\n");
+ print(" clone ", addr.clone\X, "\n");
+ print(" walk1 ", addr.walk1\X, "\n");
+ print(" infd ", addr.infd, "\n");
+ print(" outfd ", addr.outfd, "\n");
+ print(" nopipe ", addr.nopipe, "\n");
+ print(" fpool ", addr.fpool\X, "\n");
+ print(" rpool ", addr.rpool\X, "\n");
+ print(" msize ", addr.msize, "\n");
+ print(" rbuf ", addr.rbuf\X, "\n");
+ print("QLock rlock {\n");
+ QLock(addr.rlock);
+ print("}\n");
+ print(" wbuf ", addr.wbuf\X, "\n");
+ print("QLock wlock {\n");
+ QLock(addr.wlock);
+ print("}\n");
+};
+
+OMASK = 3;
+Maxname = 128;
+Maxrpc = 4096;
+Notstarted = -3;
+Broken = -2;
+Established = -1;
+RpcFailure = 0;
+RpcNeedkey = 1;
+RpcOk = 2;
+RpcErrstr = 3;
+RpcToosmall = 4;
+RpcPhase = 5;
+sizeofAttr = 12;
+aggr Attr
+{
+ 'A' Attr 0 next;
+ 'A' String 4 name;
+ 'A' String 8 val;
+};
+
+defn
+Attr(addr) {
+ complex Attr addr;
+ print(" next ", addr.next\X, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" val ", addr.val\X, "\n");
+};
+
+sizeof_14_ = 4120;
+aggr _14_
+{
+ 'X' 0 arg;
+ 'a' 4 buf;
+ 'X' 4100 verb;
+ 'D' 4104 iverb;
+ 'D' 4108 narg;
+ 'D' 4112 nbuf;
+ 'D' 4116 nwant;
+};
+
+defn
+_14_(addr) {
+ complex _14_ addr;
+ print(" arg ", addr.arg\X, "\n");
+ print(" buf ", addr.buf, "\n");
+ print(" verb ", addr.verb\X, "\n");
+ print(" iverb ", addr.iverb, "\n");
+ print(" narg ", addr.narg, "\n");
+ print(" nbuf ", addr.nbuf, "\n");
+ print(" nwant ", addr.nwant, "\n");
+};
+
+sizeofFsstate = 4700;
+aggr Fsstate
+{
+ 'X' 0 sysuser;
+ 'D' 4 listoff;
+ _14_ 8 rpc;
+ 'a' 4128 err;
+ 'a' 4256 keyinfo;
+ 'X' 4640 phasename;
+ 'D' 4644 isclient;
+ 'D' 4648 haveai;
+ 'D' 4652 maxphase;
+ 'D' 4656 phase;
+ 'D' 4660 started;
+ 'A' Attr 4664 attr;
+ AuthInfo 4668 ai;
+ 'X' 4688 proto;
+ 'X' 4692 ps;
+ 'X' 4696 ring;
+};
+
+defn
+Fsstate(addr) {
+ complex Fsstate addr;
+ print(" sysuser ", addr.sysuser\X, "\n");
+ print(" listoff ", addr.listoff, "\n");
+ print("_14_ rpc {\n");
+ _14_(addr.rpc);
+ print("}\n");
+ print(" err ", addr.err, "\n");
+ print(" keyinfo ", addr.keyinfo, "\n");
+ print(" phasename ", addr.phasename\X, "\n");
+ print(" isclient ", addr.isclient, "\n");
+ print(" haveai ", addr.haveai, "\n");
+ print(" maxphase ", addr.maxphase, "\n");
+ print(" phase ", addr.phase, "\n");
+ print(" started ", addr.started, "\n");
+ print(" attr ", addr.attr\X, "\n");
+ print("AuthInfo ai {\n");
+ AuthInfo(addr.ai);
+ print("}\n");
+ print(" proto ", addr.proto\X, "\n");
+ print(" ps ", addr.ps\X, "\n");
+ print(" ring ", addr.ring\X, "\n");
+};
+
+sizeofKey = 20;
+aggr Key
+{
+ 'D' 0 ref;
+ 'A' Attr 4 attr;
+ 'A' Attr 8 privattr;
+ 'X' 12 proto;
+ 'X' 16 priv;
+};
+
+defn
+Key(addr) {
+ complex Key addr;
+ print(" ref ", addr.ref, "\n");
+ print(" attr ", addr.attr\X, "\n");
+ print(" privattr ", addr.privattr\X, "\n");
+ print(" proto ", addr.proto\X, "\n");
+ print(" priv ", addr.priv\X, "\n");
+};
+
+sizeofKeyring = 8;
+aggr Keyring
+{
+ 'A' Key 0 key;
+ 'D' 4 nkey;
+};
+
+defn
+Keyring(addr) {
+ complex Keyring addr;
+ print(" key ", addr.key\X, "\n");
+ print(" nkey ", addr.nkey, "\n");
+};
+
+sizeofLogbuf = 520;
+aggr Logbuf
+{
+ 'D' 0 rp;
+ 'D' 4 wp;
+ 'a' 8 msg;
+};
+
+defn
+Logbuf(addr) {
+ complex Logbuf addr;
+ print(" rp ", addr.rp, "\n");
+ print(" wp ", addr.wp, "\n");
+ print(" msg ", addr.msg, "\n");
+};
+
+sizeofProto = 28;
+aggr Proto
+{
+ 'X' 0 name;
+ 'X' 4 init;
+ 'X' 8 addkey;
+ 'X' 12 closekey;
+ 'X' 16 write;
+ 'X' 20 read;
+ 'X' 24 close;
+};
+
+defn
+Proto(addr) {
+ complex Proto addr;
+ print(" name ", addr.name\X, "\n");
+ print(" init ", addr.init\X, "\n");
+ print(" addkey ", addr.addkey\X, "\n");
+ print(" closekey ", addr.closekey\X, "\n");
+ print(" write ", addr.write\X, "\n");
+ print(" read ", addr.read\X, "\n");
+ print(" close ", addr.close\X, "\n");
+};
+
+complex Keyring ring;
+complex Logbuf logbuf;
+complex Proto apop;
+complex Proto cram;
+complex Proto p9any;
+complex Proto p9sk1;
+complex Proto p9sk2;
+complex Keyring ring;
+complex Srv fs;
+complex Proto main:p;
+Qroot = 0;
+Qfactotum = 1;
+Qrpc = 2;
+Qkeylist = 3;
+Qprotolist = 4;
+Qconfirm = 5;
+Qlog = 6;
+Qctl = 7;
+complex Qid mkqid:q;
+complex Req fsattach:r;
+sizeof_15_ = 12;
+aggr _15_
+{
+ 'X' 0 name;
+ 'D' 4 qidpath;
+ 'U' 8 perm;
+};
+
+defn
+_15_(addr) {
+ complex _15_ addr;
+ print(" name ", addr.name\X, "\n");
+ print(" qidpath ", addr.qidpath, "\n");
+ print(" perm ", addr.perm, "\n");
+};
+
+complex Dir fillstat:dir;
+complex Dir fsdirgen:dir;
+complex Fid fswalk1:fid;
+complex Qid fswalk1:qid;
+complex Req fsstat:r;
+complex Req fsopen:r;
+complex Fsstate fsopen:fss;
+complex Fid fsdestroyfid:fid;
+complex Req readlist:r;
+complex Key keylist:k;
+complex Req fsread:r;
+complex Fsstate fsread:s;
+complex Req fswrite:r;
+complex Srv fs;
diff --git a/src/cmd/factotum/fs.c b/src/cmd/factotum/fs.c
new file mode 100644
index 00000000..0e4f2fd6
--- /dev/null
+++ b/src/cmd/factotum/fs.c
@@ -0,0 +1,524 @@
+#include "std.h"
+#include "dat.h"
+
+enum
+{
+ Qroot,
+ Qfactotum,
+ Qrpc,
+ Qkeylist,
+ Qprotolist,
+ Qconfirm,
+ Qlog,
+ Qctl,
+ Qneedkey,
+ Qconv,
+};
+
+Qid
+mkqid(int type, int path)
+{
+ Qid q;
+
+ q.type = type;
+ q.path = path;
+ q.vers = 0;
+ return q;
+}
+
+static struct
+{
+ char *name;
+ int qidpath;
+ ulong perm;
+} dirtab[] = {
+ /* positions of confirm and needkey known below */
+ "confirm", Qconfirm, 0600|DMEXCL,
+ "needkey", Qneedkey, 0600|DMEXCL,
+ "ctl", Qctl, 0600,
+ "rpc", Qrpc, 0666,
+ "proto", Qprotolist, 0444,
+ "log", Qlog, 0600|DMEXCL,
+ "conv", Qconv, 0400,
+};
+
+static void
+fillstat(Dir *dir, char *name, int type, int path, ulong perm)
+{
+ dir->name = estrdup(name);
+ dir->uid = estrdup(owner);
+ dir->gid = estrdup(owner);
+ dir->mode = perm;
+ dir->length = 0;
+ dir->qid = mkqid(type, path);
+ dir->atime = time(0);
+ dir->mtime = time(0);
+ dir->muid = estrdup("");
+}
+
+static int
+rootdirgen(int n, Dir *dir, void *v)
+{
+ USED(v);
+
+ if(n > 0)
+ return -1;
+
+ fillstat(dir, factname, QTDIR, Qfactotum, DMDIR|0555);
+ return 0;
+}
+
+static int
+fsdirgen(int n, Dir *dir, void *v)
+{
+ USED(v);
+
+ if(n >= nelem(dirtab))
+ return -1;
+ fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm);
+ return 0;
+}
+
+static char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+ int i;
+
+ switch((int)fid->qid.path){
+ default:
+ return "fswalk1: cannot happen";
+ case Qroot:
+ if(strcmp(name, factname) == 0){
+ *qid = mkqid(QTDIR, Qfactotum);
+ fid->qid = *qid;
+ return nil;
+ }
+ if(strcmp(name, "..") == 0){
+ *qid = fid->qid;
+ return nil;
+ }
+ return "not found";
+ case Qfactotum:
+ for(i=0; i<nelem(dirtab); i++)
+ if(strcmp(name, dirtab[i].name) == 0){
+ *qid = mkqid(0, dirtab[i].qidpath);
+ fid->qid = *qid;
+ return nil;
+ }
+ if(strcmp(name, "..") == 0){
+ *qid = mkqid(QTDIR, Qroot);
+ fid->qid = *qid;
+ return nil;
+ }
+ return "not found";
+ }
+}
+
+static void
+fsstat(Req *r)
+{
+ int i, path;
+
+ path = r->fid->qid.path;
+ switch(path){
+ case Qroot:
+ fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR);
+ break;
+ case Qfactotum:
+ fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR);
+ break;
+ default:
+ for(i=0; i<nelem(dirtab); i++)
+ if(dirtab[i].qidpath == path){
+ fillstat(&r->d, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm);
+ goto Break2;
+ }
+ respond(r, "file not found");
+ break;
+ }
+ Break2:
+ respond(r, nil);
+}
+
+static int
+readlist(int off, int (*gen)(int, char*, uint), Req *r)
+{
+ char *a, *ea;
+ int n;
+
+ a = r->ofcall.data;
+ ea = a+r->ifcall.count;
+ for(;;){
+ n = (*gen)(off, a, ea-a);
+ if(n == 0){
+ r->ofcall.count = a - (char*)r->ofcall.data;
+ return off;
+ }
+ a += n;
+ off++;
+ }
+ return -1; /* not reached */
+}
+
+static int
+keylist(int i, char *a, uint nn)
+{
+ int n;
+ char buf[512];
+ Key *k;
+
+ if(i >= ring.nkey)
+ return 0;
+
+ k = ring.key[i];
+ k->attr = sortattr(k->attr);
+ n = snprint(buf, sizeof buf, "key %A %N\n", k->attr, k->privattr);
+ if(n >= sizeof(buf)-5)
+ strcpy(buf+sizeof(buf)-5, "...\n");
+ n = strlen(buf);
+ if(n > nn)
+ return 0;
+ memmove(a, buf, n);
+ return n;
+}
+
+static int
+protolist(int i, char *a, uint n)
+{
+ if(prototab[i] == nil)
+ return 0;
+ if(strlen(prototab[i]->name)+1 > n)
+ return 0;
+ n = strlen(prototab[i]->name)+1;
+ memmove(a, prototab[i]->name, n-1);
+ a[n-1] = '\n';
+ return n;
+}
+
+/* BUG this is O(n^2) to fill in the list */
+static int
+convlist(int i, char *a, uint nn)
+{
+ Conv *c;
+ char buf[512];
+ int n;
+
+ for(c=conv; c && i-- > 0; c=c->next)
+ ;
+
+ if(c == nil)
+ return 0;
+
+ if(c->state)
+ n = snprint(buf, sizeof buf, "conv state=%q %A\n", c->state, c->attr);
+ else
+ n = snprint(buf, sizeof buf, "conv state=closed err=%q\n", c->err);
+
+ if(n >= sizeof(buf)-5)
+ strcpy(buf+sizeof(buf)-5, "...\n");
+ n = strlen(buf);
+ if(n > nn)
+ return 0;
+ memmove(a, buf, n);
+ return n;
+}
+
+static void
+fskickreply(Conv *c)
+{
+ Req *r;
+
+ if(c->hangup){
+ if(c->req){
+ respond(c->req, "hangup");
+ c->req = nil;
+ }
+ return;
+ }
+
+ if(!c->req || !c->nreply)
+ return;
+
+ r = c->req;
+ r->ofcall.count = c->nreply;
+ r->ofcall.data = c->reply;
+ if(r->ofcall.count > r->ifcall.count)
+ r->ofcall.count = r->ifcall.count;
+ respond(r, nil);
+ c->req = nil;
+ c->nreply = 0;
+}
+
+/*
+ * Some of the file system work happens in the fs proc, but
+ * fsopen, fsread, fswrite, fsdestroyfid, and fsflush happen in
+ * the main proc so that they can access the various shared
+ * data structures without worrying about locking.
+ */
+static int inuse[nelem(dirtab)];
+int *confirminuse = &inuse[0];
+int *needkeyinuse = &inuse[1];
+static void
+fsopen(Req *r)
+{
+ int i, *inusep, perm;
+ static int need[4] = { 4, 2, 6, 1 };
+ Conv *c;
+
+ inusep = nil;
+ perm = 5; /* directory */
+ for(i=0; i<nelem(dirtab); i++)
+ if(dirtab[i].qidpath == r->fid->qid.path){
+ if(dirtab[i].perm & DMEXCL)
+ inusep = &inuse[i];
+ if(strcmp(r->fid->uid, owner) == 0)
+ perm = dirtab[i].perm>>6;
+ else
+ perm = dirtab[i].perm;
+ break;
+ }
+
+ if((r->ifcall.mode&~(OMASK|OTRUNC))
+ || (need[r->ifcall.mode&3] & ~perm)){
+ respond(r, "permission denied");
+ return;
+ }
+
+ if(inusep){
+ if(*inusep){
+ respond(r, "file in use");
+ return;
+ }
+ *inusep = 1;
+ }
+
+ if(r->fid->qid.path == Qrpc){
+ if((c = convalloc(r->fid->uid)) == nil){
+ char e[ERRMAX];
+
+ rerrstr(e, sizeof e);
+ respond(r, e);
+ return;
+ }
+ c->kickreply = fskickreply;
+ r->fid->aux = c;
+ }
+
+ respond(r, nil);
+}
+
+static void
+fsread(Req *r)
+{
+ Conv *c;
+
+ switch((int)r->fid->qid.path){
+ default:
+ respond(r, "fsread: cannot happen");
+ break;
+ case Qroot:
+ dirread9p(r, rootdirgen, nil);
+ respond(r, nil);
+ break;
+ case Qfactotum:
+ dirread9p(r, fsdirgen, nil);
+ respond(r, nil);
+ break;
+ case Qrpc:
+ c = r->fid->aux;
+ if(c->rpc.op == RpcUnknown){
+ respond(r, "no rpc pending");
+ break;
+ }
+ if(c->req){
+ respond(r, "read already pending");
+ break;
+ }
+ c->req = r;
+ if(c->nreply)
+ (*c->kickreply)(c);
+ else
+ rpcexec(c);
+ break;
+ case Qconfirm:
+ confirmread(r);
+ break;
+ case Qlog:
+ logread(r);
+ break;
+ case Qctl:
+ r->fid->aux = (void*)readlist((int)r->fid->aux, keylist, r);
+ respond(r, nil);
+ break;
+ case Qneedkey:
+ needkeyread(r);
+ break;
+ case Qprotolist:
+ r->fid->aux = (void*)readlist((int)r->fid->aux, protolist, r);
+ respond(r, nil);
+ break;
+ case Qconv:
+ r->fid->aux = (void*)readlist((int)r->fid->aux, convlist, r);
+ respond(r, nil);
+ break;
+ }
+}
+
+static void
+fswrite(Req *r)
+{
+ int ret;
+ char err[ERRMAX], *s;
+ int (*strfn)(char*);
+
+ switch((int)r->fid->qid.path){
+ default:
+ respond(r, "fswrite: cannot happen");
+ break;
+ case Qrpc:
+ if(rpcwrite(r->fid->aux, r->ifcall.data, r->ifcall.count) < 0){
+ rerrstr(err, sizeof err);
+ respond(r, err);
+ }else{
+ r->ofcall.count = r->ifcall.count;
+ respond(r, nil);
+ }
+ break;
+ case Qneedkey:
+ strfn = needkeywrite;
+ goto string;
+ case Qctl:
+ strfn = ctlwrite;
+ goto string;
+ case Qconfirm:
+ strfn = confirmwrite;
+ string:
+ s = emalloc(r->ifcall.count+1);
+ memmove(s, r->ifcall.data, r->ifcall.count);
+ s[r->ifcall.count] = '\0';
+ ret = (*strfn)(s);
+ free(s);
+ if(ret < 0){
+ rerrstr(err, sizeof err);
+ respond(r, err);
+ }else{
+ r->ofcall.count = r->ifcall.count;
+ respond(r, nil);
+ }
+ break;
+ }
+}
+
+static void
+fsflush(Req *r)
+{
+ confirmflush(r);
+ logflush(r);
+}
+
+static void
+fsdestroyfid(Fid *fid)
+{
+ if(fid->qid.path == Qrpc){
+ convhangup(fid->aux);
+ convclose(fid->aux);
+ }
+}
+
+static Channel *creq;
+static Channel *cfid, *cfidr;
+
+static void
+fsreqthread(void *v)
+{
+ Req *r;
+
+ USED(v);
+
+ creq = chancreate(sizeof(Req*), 0);
+
+ while((r = recvp(creq)) != nil){
+ switch(r->ifcall.type){
+ default:
+ respond(r, "bug in fsreqthread");
+ break;
+ case Topen:
+ fsopen(r);
+ break;
+ case Tread:
+ fsread(r);
+ break;
+ case Twrite:
+ fswrite(r);
+ break;
+ case Tflush:
+ fsflush(r);
+ break;
+ }
+ }
+}
+
+static void
+fsclunkthread(void *v)
+{
+ Fid *f;
+
+ USED(v);
+ cfid = chancreate(sizeof(Fid*), 0);
+ cfidr = chancreate(sizeof(Fid*), 0);
+
+ while((f = recvp(cfid)) != nil){
+ fsdestroyfid(f);
+ sendp(cfidr, 0);
+ }
+}
+
+static void
+fsproc(void *v)
+{
+ USED(v);
+
+ threadcreate(fsreqthread, nil, STACK);
+ threadcreate(fsclunkthread, nil, STACK);
+ threadexits(nil);
+}
+
+static void
+fsattach(Req *r)
+{
+ static int first = 1;
+
+ if(first){
+ proccreate(fsproc, nil, STACK);
+ first = 0;
+ }
+
+ r->fid->qid = mkqid(QTDIR, Qroot);
+ r->ofcall.qid = r->fid->qid;
+ respond(r, nil);
+}
+
+static void
+fssend(Req *r)
+{
+ sendp(creq, r);
+}
+
+static void
+fssendclunk(Fid *f)
+{
+ sendp(cfid, f);
+ recvp(cfidr);
+}
+
+Srv fs = {
+.attach= fsattach,
+.walk1= fswalk1,
+.open= fssend,
+.read= fssend,
+.write= fssend,
+.stat= fsstat,
+.flush= fssend,
+.destroyfid= fssendclunk,
+};
+
diff --git a/src/cmd/factotum/guide b/src/cmd/factotum/guide
new file mode 100755
index 00000000..7293e54b
--- /dev/null
+++ b/src/cmd/factotum/guide
@@ -0,0 +1,3 @@
+kill 8.out|rc
+unmount /srv/factotum /mnt
+8.out
diff --git a/src/cmd/factotum/guide2 b/src/cmd/factotum/guide2
new file mode 100644
index 00000000..72f245d0
--- /dev/null
+++ b/src/cmd/factotum/guide2
@@ -0,0 +1,6 @@
+kill 8.out|rc
+unmount /mnt/factotum
+8.out -m /mnt/factotum
+cat /mnt/factotum/log &
+unmount /factotum
+bind 8.out /factotum
diff --git a/src/cmd/factotum/key.c b/src/cmd/factotum/key.c
new file mode 100644
index 00000000..9023fed5
--- /dev/null
+++ b/src/cmd/factotum/key.c
@@ -0,0 +1,190 @@
+#include "std.h"
+#include "dat.h"
+
+Ring ring;
+
+Key*
+keylookup(char *fmt, ...)
+{
+ int i;
+ Attr *a;
+ Key *k;
+ va_list arg;
+
+ va_start(arg, fmt);
+ a = parseattrfmtv(fmt, arg);
+ va_end(arg);
+
+ for(i=0; i<ring.nkey; i++){
+ k = ring.key[i];
+ if(matchattr(a, k->attr, k->privattr)){
+ k->ref++;
+ freeattr(a);
+ return k;
+ }
+ }
+ freeattr(a);
+ werrstr("no key found");
+ return nil;
+}
+
+Key*
+keyfetch(Conv *c, char *fmt, ...)
+{
+ int i, tag;
+ Attr *a;
+ Key *k;
+ va_list arg;
+
+ va_start(arg, fmt);
+ a = parseattrfmtv(fmt, arg);
+ va_end(arg);
+
+ tag = 0;
+
+ for(i=0; i<ring.nkey; i++){
+ k = ring.key[i];
+ if(tag < k->tag)
+ tag = k->tag;
+ if(matchattr(a, k->attr, k->privattr)){
+ k->ref++;
+ if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){
+ k->ref--;
+ continue;
+ }
+ freeattr(a);
+ return k;
+ }
+ }
+
+ if(needkey(c, a) < 0)
+ convneedkey(c, a);
+
+ for(i=0; i<ring.nkey; i++){
+ k = ring.key[i];
+ if(k->tag <= tag)
+ continue;
+ if(matchattr(a, k->attr, k->privattr)){
+ k->ref++;
+ if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){
+ k->ref--;
+ continue;
+ }
+ freeattr(a);
+ return k;
+ }
+ }
+ freeattr(a);
+ werrstr("no key found");
+ return nil;
+}
+
+static int taggen;
+
+void
+keyadd(Key *k)
+{
+ int i;
+
+ k->ref++;
+ k->tag = ++taggen;
+ for(i=0; i<ring.nkey; i++){
+ if(matchattr(k->attr, ring.key[i]->attr, nil)
+ && matchattr(ring.key[i]->attr, k->attr, nil)){
+ keyclose(ring.key[i]);
+ ring.key[i] = k;
+ return;
+ }
+ }
+
+ ring.key = erealloc(ring.key, (ring.nkey+1)*sizeof(ring.key[0]));
+ ring.key[ring.nkey++] = k;
+}
+
+void
+keyclose(Key *k)
+{
+ if(k == nil)
+ return;
+
+ if(--k->ref > 0)
+ return;
+
+ if(k->proto->closekey)
+ (*k->proto->closekey)(k);
+
+ freeattr(k->attr);
+ freeattr(k->privattr);
+ free(k);
+}
+
+Key*
+keyreplace(Conv *c, Key *k, char *fmt, ...)
+{
+ Key *kk;
+ char *msg;
+ Attr *a, *b, *bp;
+ va_list arg;
+
+ va_start(arg, fmt);
+ msg = vsmprint(fmt, arg);
+ if(msg == nil)
+ sysfatal("out of memory");
+ va_end(arg);
+
+ /* replace prompted values with prompts */
+ a = copyattr(k->attr);
+ bp = parseattr(k->proto->keyprompt);
+ for(b=bp; b; b=b->next){
+ a = delattr(a, b->name);
+ a = addattr(a, "%q?", b->name);
+ }
+ freeattr(bp);
+
+ if(badkey(c, k, msg, a) < 0)
+ convbadkey(c, k, msg, a);
+ kk = keylookup("%A", a);
+ freeattr(a);
+ keyclose(k);
+ if(kk == k){
+ keyclose(kk);
+ werrstr("%s", msg);
+ return nil;
+ }
+
+ if(strfindattr(kk->attr, "confirm")){
+ if(confirmkey(c, kk) != 1){
+ werrstr("key use not confirmed");
+ keyclose(kk);
+ return nil;
+ }
+ }
+ return kk;
+}
+
+void
+keyevict(Conv *c, Key *k, char *fmt, ...)
+{
+ char *msg;
+ Attr *a, *b, *bp;
+ va_list arg;
+
+ va_start(arg, fmt);
+ msg = vsmprint(fmt, arg);
+ if(msg == nil)
+ sysfatal("out of memory");
+ va_end(arg);
+
+ /* replace prompted values with prompts */
+ a = copyattr(k->attr);
+ bp = parseattr(k->proto->keyprompt);
+ for(b=bp; b; b=b->next){
+ a = delattr(a, b->name);
+ a = addattr(a, "%q?", b->name);
+ }
+ freeattr(bp);
+
+ if(badkey(c, k, msg, nil) < 0)
+ convbadkey(c, k, msg, nil);
+ keyclose(k);
+}
diff --git a/src/cmd/factotum/log.c b/src/cmd/factotum/log.c
new file mode 100644
index 00000000..6c2d69dd
--- /dev/null
+++ b/src/cmd/factotum/log.c
@@ -0,0 +1,121 @@
+#include "std.h"
+#include "dat.h"
+
+void
+lbkick(Logbuf *lb)
+{
+ char *s;
+ int n;
+ Req *r;
+
+ while(lb->wait && lb->rp != lb->wp){
+ r = lb->wait;
+ lb->wait = r->aux;
+ if(lb->wait == nil)
+ lb->waitlast = &lb->wait;
+ r->aux = nil;
+ if(r->ifcall.count < 5){
+ respond(r, "factotum: read request count too short");
+ continue;
+ }
+ s = lb->msg[lb->rp];
+ lb->msg[lb->rp] = nil;
+ if(++lb->rp == nelem(lb->msg))
+ lb->rp = 0;
+ n = r->ifcall.count;
+ if(n < strlen(s)+1+1){
+ memmove(r->ofcall.data, s, n-5);
+ n -= 5;
+ r->ofcall.data[n] = '\0';
+ /* look for first byte of UTF-8 sequence by skipping continuation bytes */
+ while(n>0 && (r->ofcall.data[--n]&0xC0)==0x80)
+ ;
+ strcpy(r->ofcall.data+n, "...\n");
+ }else{
+ strcpy(r->ofcall.data, s);
+ strcat(r->ofcall.data, "\n");
+ }
+ r->ofcall.count = strlen(r->ofcall.data);
+ free(s);
+ respond(r, nil);
+ }
+}
+
+void
+lbread(Logbuf *lb, Req *r)
+{
+ if(lb->waitlast == nil)
+ lb->waitlast = &lb->wait;
+ *(lb->waitlast) = r;
+ lb->waitlast = (Req**)&r->aux;
+ r->aux = nil;
+ lbkick(lb);
+}
+
+void
+lbflush(Logbuf *lb, Req *r)
+{
+ Req **l;
+
+ for(l=&lb->wait; *l; l=(Req**)&(*l)->aux){
+ if(*l == r){
+ *l = r->aux;
+ r->aux = nil;
+ if(*l == nil)
+ lb->waitlast = l;
+ closereq(r);
+ break;
+ }
+ }
+}
+
+void
+lbappend(Logbuf *lb, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ lbvappend(lb, fmt, arg);
+ va_end(arg);
+}
+
+void
+lbvappend(Logbuf *lb, char *fmt, va_list arg)
+{
+ char *s;
+
+ s = smprint(fmt, arg);
+ if(s == nil)
+ sysfatal("out of memory");
+ if(lb->msg[lb->wp])
+ free(lb->msg[lb->wp]);
+ lb->msg[lb->wp] = s;
+ if(++lb->wp == nelem(lb->msg))
+ lb->wp = 0;
+ lbkick(lb);
+}
+
+Logbuf logbuf;
+
+void
+logread(Req *r)
+{
+ lbread(&logbuf, r);
+}
+
+void
+logflush(Req *r)
+{
+ lbflush(&logbuf, r);
+}
+
+void
+flog(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ lbvappend(&logbuf, fmt, arg);
+ va_end(arg);
+}
+
diff --git a/src/cmd/factotum/main.c b/src/cmd/factotum/main.c
new file mode 100644
index 00000000..3e9f89f8
--- /dev/null
+++ b/src/cmd/factotum/main.c
@@ -0,0 +1,163 @@
+#include "std.h"
+#include "dat.h"
+
+int debug;
+char *factname = "factotum";
+char *service = nil;
+char *owner;
+char *authaddr;
+void gflag(char*);
+
+void
+usage(void)
+{
+ fprint(2, "usage: factotum [-Dd] [-a authaddr] [-m mtpt]\n");
+ fprint(2, " or factotum -g keypattern\n");
+ fprint(2, " or factotum -g 'badkeyattr\nmsg\nkeypattern'");
+ exits("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ char *mtpt;
+
+ mtpt = "/mnt";
+ owner = getuser();
+ quotefmtinstall();
+ fmtinstall('A', attrfmt);
+ fmtinstall('H', encodefmt);
+ fmtinstall('N', attrnamefmt);
+
+ if(argc == 3 && strcmp(argv[1], "-g") == 0){
+ gflag(argv[2]);
+ exits(nil);
+ }
+
+ ARGBEGIN{
+ default:
+ usage();
+ case 'D':
+ chatty9p++;
+ break;
+ case 'a':
+ authaddr = EARGF(usage());
+ break;
+ case 'g':
+ usage();
+ case 'm':
+ mtpt = EARGF(usage());
+ break;
+ case 's':
+ service = EARGF(usage());
+ break;
+ }ARGEND
+
+ if(argc != 0)
+ usage();
+
+ threadpostmountsrv(&fs, service, mtpt, MBEFORE);
+ threadexits(nil);
+}
+
+/*
+ * prompt user for a key. don't care about memory leaks, runs standalone
+ */
+static Attr*
+promptforkey(int fd, char *params)
+{
+ char *v;
+ Attr *a, *attr;
+ char *def;
+
+ attr = _parseattr(params);
+ fprint(fd, "!adding key:");
+ for(a=attr; a; a=a->next)
+ if(a->type != AttrQuery && a->name[0] != '!')
+ fprint(fd, " %q=%q", a->name, a->val);
+ fprint(fd, "\n");
+
+ for(a=attr; a; a=a->next){
+ v = a->name;
+ if(a->type != AttrQuery || v[0]=='!')
+ continue;
+ def = nil;
+ if(strcmp(v, "user") == 0)
+ def = getuser();
+ a->val = readcons(v, def, 0);
+ if(a->val == nil)
+ sysfatal("user terminated key input");
+ a->type = AttrNameval;
+ }
+ for(a=attr; a; a=a->next){
+ v = a->name;
+ if(a->type != AttrQuery || v[0]!='!')
+ continue;
+ def = nil;
+ if(strcmp(v+1, "user") == 0)
+ def = getuser();
+ a->val = readcons(v+1, def, 1);
+ if(a->val == nil)
+ sysfatal("user terminated key input");
+ a->type = AttrNameval;
+ }
+ fprint(fd, "!\n");
+ close(fd);
+ return attr;
+}
+
+/*
+ * send a key to the mounted factotum
+ */
+static int
+sendkey(Attr *attr)
+{
+ int fd, rv;
+ char buf[1024];
+
+ fd = open("/mnt/factotum/ctl", ORDWR);
+ if(fd < 0)
+ sysfatal("opening /mnt/factotum/ctl: %r");
+ rv = fprint(fd, "key %A\n", attr);
+ read(fd, buf, sizeof buf);
+ close(fd);
+ return rv;
+}
+
+static void
+askuser(int fd, char *params)
+{
+ Attr *attr;
+
+ attr = promptforkey(fd, params);
+ if(attr == nil)
+ sysfatal("no key supplied");
+ if(sendkey(attr) < 0)
+ sysfatal("sending key to factotum: %r");
+}
+
+void
+gflag(char *s)
+{
+ char *f[4];
+ int nf;
+ int fd;
+
+ if((fd = open("/dev/cons", ORDWR)) < 0)
+ sysfatal("open /dev/cons: %r");
+
+ nf = getfields(s, f, nelem(f), 0, "\n");
+ if(nf == 1){ /* needkey or old badkey */
+ fprint(fd, "\n");
+ askuser(fd, s);
+ exits(nil);
+ }
+ if(nf == 3){ /* new badkey */
+ fprint(fd, "\n");
+ fprint(fd, "!replace: %s\n", f[0]);
+ fprint(fd, "!because: %s\n", f[1]);
+ askuser(fd, f[2]);
+ exits(nil);
+ }
+ usage();
+}
diff --git a/src/cmd/factotum/mkfile b/src/cmd/factotum/mkfile
new file mode 100644
index 00000000..3aa9bb70
--- /dev/null
+++ b/src/cmd/factotum/mkfile
@@ -0,0 +1,34 @@
+PLAN9=../../..
+<$PLAN9/src/mkhdr
+
+TARG=factotum
+PROTO=\
+ apop.$O\
+ chap.$O\
+ p9any.$O\
+ p9sk1.$O\
+
+OFILES=\
+ $PROTO\
+ attr.$O\
+ confirm.$O\
+ conv.$O\
+ ctl.$O\
+ fs.$O\
+ key.$O\
+ log.$O\
+ main.$O\
+ plan9.$O\
+ proto.$O\
+ rpc.$O\
+ util.$O\
+ xio.$O\
+
+HFILES=dat.h
+
+SHORTLIB=auth authsrv sec mp 9p thread 9
+<$PLAN9/src/mkone
+
+$O.test: test.$O
+ $LD -o $target $prereq
+
diff --git a/src/cmd/factotum/p9any.c b/src/cmd/factotum/p9any.c
new file mode 100644
index 00000000..971c7e50
--- /dev/null
+++ b/src/cmd/factotum/p9any.c
@@ -0,0 +1,266 @@
+#include "std.h"
+#include "dat.h"
+
+/*
+ * p9any - protocol negotiator
+ *
+ * Protocol:
+ * S->C: v.2 proto@dom proto@dom proto@dom... NUL
+ * C->S: proto dom NUL
+ * [negotiated proto continues]
+ */
+
+static Proto* okproto[] =
+{
+ &p9sk1,
+ nil,
+};
+
+static int
+rolecall(Role *r, char *name, Conv *c)
+{
+ for(; r->name; r++)
+ if(strcmp(r->name, name) == 0)
+ return (*r->fn)(c);
+ werrstr("unknown role");
+ return -1;
+}
+
+static int
+hasnul(void *v, int n)
+{
+ char *c;
+
+ c = v;
+ if(n > 0 && c[n-1] == '\0')
+ return n;
+ else
+ return n+1;
+}
+
+static int
+p9anyserver(Conv *c)
+{
+ char *s, *dom;
+ int i, j, n, m, ret;
+ char *tok[3];
+ Attr *attr;
+ Key *k;
+
+ ret = -1;
+ s = estrdup("v.2");
+ n = 0;
+ attr = delattr(copyattr(c->attr), "proto");
+
+ for(i=0; i<ring.nkey; i++){
+ k = ring.key[i];
+ for(j=0; okproto[j]; j++)
+ if(k->proto == okproto[j]
+ && (dom = strfindattr(k->attr, "dom")) != nil
+ && matchattr(attr, k->attr, k->privattr)){
+ s = estrappend(s, " %s@%s", k->proto->name, dom);
+ n++;
+ }
+ }
+
+ if(n == 0){
+ werrstr("no valid keys");
+ goto out;
+ }
+
+ c->state = "write offer";
+ if(convwrite(c, s, strlen(s)+1) < 0)
+ goto out;
+ free(s);
+ s = nil;
+
+ c->state = "read choice";
+ if(convreadfn(c, hasnul, &s) < 0)
+ goto out;
+
+ m = tokenize(s, tok, nelem(tok));
+ if(m != 2){
+ werrstr("bad protocol message");
+ goto out;
+ }
+
+ for(i=0; okproto[i]; i++)
+ if(strcmp(okproto[i]->name, tok[0]) == 0)
+ break;
+ if(!okproto[i]){
+ werrstr("bad chosen protocol %q", tok[0]);
+ goto out;
+ }
+
+ c->state = "write ok";
+ if(convwrite(c, "OK\0", 3) < 0)
+ goto out;
+
+ c->state = "start choice";
+ attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]);
+ free(c->attr);
+ c->attr = attr;
+ attr = nil;
+ c->proto = okproto[i];
+
+ if(rolecall(c->proto->roles, "server", c) < 0){
+ werrstr("%s: %r", tok[0]);
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ free(s);
+ freeattr(attr);
+ return ret;
+}
+
+static int
+p9anyclient(Conv *c)
+{
+ char *s, **f, *tok[20], ok[3], *q, *user, *dom;
+ int i, n, ret, version;
+ Key *k;
+ Attr *attr;
+ Proto *p;
+
+ ret = -1;
+ s = nil;
+ k = nil;
+
+ user = strfindattr(c->attr, "user");
+ dom = strfindattr(c->attr, "dom");
+
+ /*
+ * 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 p9sk1.c
+ */
+ attr = delattr(copyattr(c->attr), "role");
+ attr = delattr(attr, "proto");
+ if(strcmp(c->sysuser, owner) == 0)
+ attr = addattr(attr, "role=client");
+ else if(user==nil || strcmp(c->sysuser, user)==0){
+ attr = delattr(attr, "user");
+ attr = addattr(attr, "role=speakfor");
+ }else{
+ werrstr("will not authenticate for %q as %q", c->sysuser, user);
+ goto out;
+ }
+
+ c->state = "read offer";
+ if(convreadfn(c, hasnul, &s) < 0)
+ goto out;
+
+ c->state = "look for keys";
+ n = tokenize(s, tok, nelem(tok));
+ f = tok;
+ version = 1;
+ if(n > 0 && memcmp(f[0], "v.", 2) == 0){
+ version = atoi(f[0]+2);
+ if(version != 2){
+ werrstr("unknown p9any version: %s", f[0]);
+ goto out;
+ }
+ f++;
+ n--;
+ }
+
+ /* look for keys that don't need confirmation */
+ for(i=0; i<n; i++){
+ if((q = strchr(f[i], '@')) == nil)
+ continue;
+ if(dom && strcmp(q+1, dom) != 0)
+ continue;
+ *q++ = '\0';
+ if((k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
+ && strfindattr(k->attr, "confirm") == nil)
+ goto found;
+ *--q = '@';
+ }
+
+ /* look for any keys at all */
+ for(i=0; i<n; i++){
+ if((q = strchr(f[i], '@')) == nil)
+ continue;
+ if(dom && strcmp(q+1, dom) != 0)
+ continue;
+ *q++ = '\0';
+ if(k = keyfetch(c, "%A proto=%q dom=%q", attr, f[i], q))
+ goto found;
+ *--q = '@';
+ }
+
+ /* ask for new keys */
+ c->state = "ask for keys";
+ for(i=0; i<n; i++){
+ if((q = strchr(f[i], '@')) == nil)
+ continue;
+ if(dom && strcmp(q+1, dom) != 0)
+ continue;
+ *q++ = '\0';
+ p = protolookup(f[i]);
+ if(p == nil || p->keyprompt == nil){
+ *--q = '@';
+ continue;
+ }
+ if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt))
+ goto found;
+ *--q = '@';
+ }
+
+ /* nothing worked */
+ werrstr("unable to find common key");
+ goto out;
+
+found:
+ /* f[i] is the chosen protocol, q the chosen domain */
+ attr = addattr(attr, "proto=%q dom=%q", f[i], q);
+ c->state = "write choice";
+ /* have a key: go for it */
+ if(convprint(c, "%q %q", f[i], q) < 0
+ || convwrite(c, "\0", 1) < 0)
+ goto out;
+
+ if(version == 2){
+ c->state = "read ok";
+ if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0)
+ goto out;
+ }
+
+ c->state = "start choice";
+ c->proto = protolookup(f[i]);
+ freeattr(c->attr);
+ c->attr = attr;
+ attr = nil;
+
+ if(rolecall(c->proto->roles, "client", c) < 0){
+ werrstr("%s: %r", c->proto->name);
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ keyclose(k);
+ freeattr(attr);
+ free(s);
+ return ret;
+}
+
+static Role
+p9anyroles[] =
+{
+ "client", p9anyclient,
+ "server", p9anyserver,
+ 0
+};
+
+Proto p9any = {
+.name= "p9any",
+.roles= p9anyroles,
+};
+
diff --git a/src/cmd/factotum/p9cr.c b/src/cmd/factotum/p9cr.c
new file mode 100644
index 00000000..7f53e447
--- /dev/null
+++ b/src/cmd/factotum/p9cr.c
@@ -0,0 +1,545 @@
+/*
+ * p9cr, vnc - one-sided challenge/response authentication
+ *
+ * Protocol:
+ *
+ * C -> S: user
+ * S -> C: challenge
+ * C -> S: response
+ * S -> C: ok or bad
+ *
+ * Note that this is the protocol between factotum and the local
+ * program, not between the two factotums. The information
+ * exchanged here is wrapped in other protocols by the local
+ * programs.
+ */
+
+#include "std.h"
+#include "dat.h"
+
+static int
+p9crcheck(Key *k)
+{
+ if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
+ werrstr("need user and !password attributes");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+p9crclient(Conv *c)
+{
+ char *chal, *pw, *res, *user;
+ int astype, nchal, npw, ntry, ret;
+ uchar resp[MD5dlen];
+ Attr *attr;
+ DigestState *ds;
+ Key *k;
+
+ chal = nil;
+ k = nil;
+ res = nil;
+ ret = -1;
+ attr = c->attr;
+
+ if(c->proto == &p9cr){
+ astype = AuthChal;
+ challen = NETCHLEN;
+ }else if(c->proto == &vnc){
+ astype = AuthVnc;
+ challen = MAXCHAL;
+ }else{
+ werrstr("bad proto");
+ goto out;
+ }
+
+ c->state = "find key";
+ k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
+ if(k == nil)
+ goto out;
+
+ for(ntry=1;; ntry++){
+ if(c->attr != attr)
+ freeattr(c->attr);
+ c->attr = addattrs(copyattr(attr), k->attr);
+ if((pw = strfindattr(k->privattr, "!password")) == nil){
+ werrstr("key has no !password (cannot happen)");
+ goto out;
+ }
+ npw = strlen(pw);
+
+ if((user = strfindattr(k->attr, "user")) == nil){
+ werrstr("key has no user (cannot happen)");
+ goto out;
+ }
+
+ if(convprint(c, "%s", user) < 0)
+ goto out;
+
+ if(convreadm(c, &chal) < 0)
+ goto out;
+
+ if((nresp = (*response)(chal, resp)) < 0)
+ goto out;
+
+ if(convwrite(c, resp, nresp) < 0)
+ goto out;
+
+ if(convreadm(c, &res) < 0)
+ goto out;
+
+ if(strcmp(res, "ok") == 0)
+ break;
+
+ if((k = keyreplace(c, k, "%s", res)) == nil){
+ c->state = "auth failed";
+ werrstr("%s", res);
+ goto out;
+ }
+ }
+
+ werrstr("succeeded");
+ ret = 0;
+
+out:
+ keyclose(k);
+ free(chal);
+ if(c->attr != attr)
+ freeattr(attr);
+ return ret;
+}
+
+static int
+p9crserver(Conv *c)
+{
+ char chal[APOPCHALLEN], *user, *resp;
+ ServerState s;
+ int astype, ret;
+ Attr *a;
+
+ ret = -1;
+ user = nil;
+ resp = nil;
+ memset(&s, 0, sizeof s);
+ s.asfd = -1;
+
+ if(c->proto == &apop)
+ astype = AuthApop;
+ else if(c->proto == &cram)
+ astype = AuthCram;
+ else{
+ werrstr("bad proto");
+ goto out;
+ }
+
+ c->state = "find key";
+ if((s.k = plan9authkey(c->attr)) == nil)
+ goto out;
+
+ a = copyattr(s.k->attr);
+ a = delattr(a, "proto");
+ c->attr = addattrs(c->attr, a);
+ freeattr(a);
+
+ c->state = "authdial";
+ s.hostid = strfindattr(s.k->attr, "user");
+ s.dom = strfindattr(s.k->attr, "dom");
+ if((s.asfd = xioauthdial(nil, s.dom)) < 0){
+ werrstr("authdial %s: %r", s.dom);
+ goto out;
+ }
+
+ c->state = "authchal";
+ if(p9crchal(&s, astype, chal) < 0)
+ goto out;
+
+ c->state = "write challenge";
+ if(convprint(c, "%s", chal) < 0)
+ goto out;
+
+ for(;;){
+ c->state = "read user";
+ if(convreadm(c, &user) < 0)
+ goto out;
+
+ c->state = "read response";
+ if(convreadm(c, &resp) < 0)
+ goto out;
+
+ c->state = "authwrite";
+ switch(apopresp(&s, user, resp)){
+ case -1:
+ goto out;
+ case 0:
+ c->state = "write status";
+ if(convprint(c, "bad authentication failed") < 0)
+ goto out;
+ break;
+ case 1:
+ c->state = "write status";
+ if(convprint(c, "ok") < 0)
+ goto out;
+ goto ok;
+ }
+ free(user);
+ free(resp);
+ user = nil;
+ resp = nil;
+ }
+
+ok:
+ ret = 0;
+ c->attr = addcap(c->attr, c->sysuser, &s.t);
+
+out:
+ keyclose(s.k);
+ free(user);
+ free(resp);
+// xioclose(s.asfd);
+ return ret;
+}
+
+enum
+{
+ MAXCHAL = 64,
+};
+
+typedef struct State State;
+struct State
+{
+ Key *key;
+ int astype;
+ int asfd;
+ Ticket t;
+ Ticketreq tr;
+ char chal[MAXCHAL];
+ int challen;
+ char resp[MAXCHAL];
+ int resplen;
+};
+
+enum
+{
+ CNeedChal,
+ CHaveResp,
+
+ SHaveChal,
+ SNeedResp,
+
+ Maxphase,
+};
+
+static char *phasenames[Maxphase] =
+{
+[CNeedChal] "CNeedChal",
+[CHaveResp] "CHaveResp",
+
+[SHaveChal] "SHaveChal",
+[SNeedResp] "SNeedResp",
+};
+
+static void
+p9crclose(Fsstate *fss)
+{
+ State *s;
+
+ s = fss->ps;
+ if(s->asfd >= 0){
+ close(s->asfd);
+ s->asfd = -1;
+ }
+ free(s);
+}
+
+static int getchal(State*, Fsstate*);
+
+static int
+p9crinit(Proto *p, Fsstate *fss)
+{
+ int iscli, ret;
+ char *user;
+ State *s;
+ Attr *attr;
+
+ if((iscli = isclient(_str_findattr(fss->attr, "role"))) < 0)
+ return failure(fss, nil);
+
+ s = emalloc(sizeof(*s));
+ s->asfd = -1;
+ if(p == &p9cr){
+ s->astype = AuthChal;
+ s->challen = NETCHLEN;
+ }else if(p == &vnc){
+ s->astype = AuthVNC;
+ s->challen = Maxchal;
+ }else
+ abort();
+
+ if(iscli){
+ fss->phase = CNeedChal;
+ if(p == &p9cr)
+ attr = setattr(_copyattr(fss->attr), "proto=p9sk1");
+ else
+ attr = nil;
+ ret = findkey(&s->key, fss, Kuser, 0, attr ? attr : fss->attr,
+ "role=client %s", p->keyprompt);
+ _freeattr(attr);
+ if(ret != RpcOk){
+ free(s);
+ return ret;
+ }
+ fss->ps = s;
+ }else{
+ if((ret = findp9authkey(&s->key, fss)) != RpcOk){
+ free(s);
+ return ret;
+ }
+ if((user = _str_findattr(fss->attr, "user")) == nil){
+ free(s);
+ return failure(fss, "no user name specified in start msg");
+ }
+ if(strlen(user) >= sizeof s->tr.uid){
+ free(s);
+ return failure(fss, "user name too long");
+ }
+ fss->ps = s;
+ strcpy(s->tr.uid, user);
+ ret = getchal(s, fss);
+ if(ret != RpcOk){
+ p9crclose(fss); /* frees s */
+ fss->ps = nil;
+ }
+ }
+ fss->phasename = phasenames;
+ fss->maxphase = Maxphase;
+ return ret;
+}
+
+static int
+p9crread(Fsstate *fss, void *va, uint *n)
+{
+ int m;
+ State *s;
+
+ s = fss->ps;
+ switch(fss->phase){
+ default:
+ return phaseerror(fss, "read");
+
+ case CHaveResp:
+ if(s->resplen < *n)
+ *n = s->resplen;
+ memmove(va, s->resp, *n);
+ fss->phase = Established;
+ return RpcOk;
+
+ case SHaveChal:
+ if(s->astype == AuthChal)
+ m = strlen(s->chal); /* ascii string */
+ else
+ m = s->challen; /* fixed length binary */
+ if(m > *n)
+ return toosmall(fss, m);
+ *n = m;
+ memmove(va, s->chal, m);
+ fss->phase = SNeedResp;
+ return RpcOk;
+ }
+}
+
+static int
+p9response(Fsstate *fss, State *s)
+{
+ char key[DESKEYLEN];
+ uchar buf[8];
+ ulong chal;
+ char *pw;
+
+ pw = _str_findattr(s->key->privattr, "!password");
+ if(pw == nil)
+ return failure(fss, "vncresponse cannot happen");
+ passtokey(key, pw);
+ memset(buf, 0, 8);
+ sprint((char*)buf, "%d", atoi(s->chal));
+ if(encrypt(key, buf, 8) < 0)
+ return failure(fss, "can't encrypt response");
+ chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
+ s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal);
+ return RpcOk;
+}
+
+static uchar tab[256];
+
+/* VNC reverses the bits of each byte before using as a des key */
+static void
+mktab(void)
+{
+ int i, j, k;
+ static int once;
+
+ if(once)
+ return;
+ once = 1;
+
+ for(i=0; i<256; i++) {
+ j=i;
+ tab[i] = 0;
+ for(k=0; k<8; k++) {
+ tab[i] = (tab[i]<<1) | (j&1);
+ j >>= 1;
+ }
+ }
+}
+
+static int
+vncaddkey(Key *k)
+{
+ uchar *p;
+ char *s;
+
+ k->priv = emalloc(8+1);
+ if(s = _str_findattr(k->privattr, "!password")){
+ mktab();
+ memset(k->priv, 0, 8+1);
+ strncpy((char*)k->priv, s, 8);
+ for(p=k->priv; *p; p++)
+ *p = tab[*p];
+ }else{
+ werrstr("no key data");
+ return -1;
+ }
+ return replacekey(k);
+}
+
+static void
+vncclosekey(Key *k)
+{
+ free(k->priv);
+}
+
+static int
+vncresponse(Fsstate*, State *s)
+{
+ DESstate des;
+
+ memmove(s->resp, s->chal, sizeof s->chal);
+ setupDESstate(&des, s->key->priv, nil);
+ desECBencrypt((uchar*)s->resp, s->challen, &des);
+ s->resplen = s->challen;
+ return RpcOk;
+}
+
+static int
+p9crwrite(Fsstate *fss, void *va, uint n)
+{
+ char tbuf[TICKETLEN+AUTHENTLEN];
+ State *s;
+ char *data = va;
+ Authenticator a;
+ char resp[Maxchal];
+ int ret;
+
+ s = fss->ps;
+ switch(fss->phase){
+ default:
+ return phaseerror(fss, "write");
+
+ case CNeedChal:
+ if(n >= sizeof(s->chal))
+ return failure(fss, Ebadarg);
+ memset(s->chal, 0, sizeof s->chal);
+ memmove(s->chal, data, n);
+ s->challen = n;
+
+ if(s->astype == AuthChal)
+ ret = p9response(fss, s);
+ else
+ ret = vncresponse(fss, s);
+ if(ret != RpcOk)
+ return ret;
+ fss->phase = CHaveResp;
+ return RpcOk;
+
+ case SNeedResp:
+ /* send response to auth server and get ticket */
+ if(n > sizeof(resp))
+ return failure(fss, Ebadarg);
+ memset(resp, 0, sizeof resp);
+ memmove(resp, data, n);
+ if(write(s->asfd, resp, s->challen) != s->challen)
+ return failure(fss, Easproto);
+
+ /* get ticket plus authenticator from auth server */
+ if(_asrdresp(s->asfd, tbuf, TICKETLEN+AUTHENTLEN) < 0)
+ return failure(fss, nil);
+
+ /* check ticket */
+ convM2T(tbuf, &s->t, s->key->priv);
+ if(s->t.num != AuthTs
+ || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0)
+ return failure(fss, Easproto);
+ convM2A(tbuf+TICKETLEN, &a, s->t.key);
+ if(a.num != AuthAc
+ || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
+ || a.id != 0)
+ return failure(fss, Easproto);
+
+ fss->haveai = 1;
+ fss->ai.cuid = s->t.cuid;
+ fss->ai.suid = s->t.suid;
+ fss->ai.nsecret = 0;
+ fss->ai.secret = nil;
+ fss->phase = Established;
+ return RpcOk;
+ }
+}
+
+static int
+getchal(State *s, Fsstate *fss)
+{
+ char trbuf[TICKREQLEN];
+ int n;
+
+ safecpy(s->tr.hostid, _str_findattr(s->key->attr, "user"), sizeof(s->tr.hostid));
+ safecpy(s->tr.authdom, _str_findattr(s->key->attr, "dom"), sizeof(s->tr.authdom));
+ s->tr.type = s->astype;
+ convTR2M(&s->tr, trbuf);
+
+ /* get challenge from auth server */
+ s->asfd = _authdial(nil, _str_findattr(s->key->attr, "dom"));
+ if(s->asfd < 0)
+ return failure(fss, Easproto);
+ if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
+ return failure(fss, Easproto);
+ n = _asrdresp(s->asfd, s->chal, s->challen);
+ if(n <= 0){
+ if(n == 0)
+ werrstr("_asrdresp short read");
+ return failure(fss, nil);
+ }
+ s->challen = n;
+ fss->phase = SHaveChal;
+ return RpcOk;
+}
+
+Proto p9cr =
+{
+.name= "p9cr",
+.init= p9crinit,
+.write= p9crwrite,
+.read= p9crread,
+.close= p9crclose,
+.keyprompt= "user? !password?",
+};
+
+Proto vnc =
+{
+.name= "vnc",
+.init= p9crinit,
+.write= p9crwrite,
+.read= p9crread,
+.close= p9crclose,
+.keyprompt= "!password?",
+.addkey= vncaddkey,
+};
diff --git a/src/cmd/factotum/p9sk1.c b/src/cmd/factotum/p9sk1.c
new file mode 100644
index 00000000..2828e708
--- /dev/null
+++ b/src/cmd/factotum/p9sk1.c
@@ -0,0 +1,352 @@
+/*
+ * 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"
+
+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,
+};
+
diff --git a/src/cmd/factotum/pass.c b/src/cmd/factotum/pass.c
new file mode 100644
index 00000000..b3d4cb6a
--- /dev/null
+++ b/src/cmd/factotum/pass.c
@@ -0,0 +1,100 @@
+/*
+ * This is just a repository for a password.
+ * We don't want to encourage this, there's
+ * no server side.
+ */
+
+#include "dat.h"
+
+typedef struct State State;
+struct State
+{
+ Key *key;
+};
+
+enum
+{
+ HavePass,
+ Maxphase,
+};
+
+static char *phasenames[Maxphase] =
+{
+[HavePass] "HavePass",
+};
+
+static int
+passinit(Proto *p, Fsstate *fss)
+{
+ int ask;
+ Key *k;
+ State *s;
+
+ k = findkey(fss, Kuser, &ask, 0, fss->attr, "%s", p->keyprompt);
+ if(k == nil){
+ if(ask)
+ return RpcNeedkey;
+ return failure(fss, nil);
+ }
+ setattrs(fss->attr, k->attr);
+ s = emalloc(sizeof(*s));
+ s->key = k;
+ fss->ps = s;
+ return RpcOk;
+}
+
+static void
+passclose(Fsstate *fss)
+{
+ State *s;
+
+ s = fss->ps;
+ if(s->key)
+ closekey(s->key);
+ free(s);
+}
+
+static int
+passread(Fsstate *fss, void *va, uint *n)
+{
+ int m;
+ char buf[500];
+ char *pass, *user;
+ State *s;
+
+ s = fss->ps;
+ switch(fss->phase){
+ default:
+ return phaseerror(fss, "read");
+
+ case HavePass:
+ user = strfindattr(s->key->attr, "user");
+ pass = strfindattr(s->key->privattr, "!password");
+ if(user==nil || pass==nil)
+ return failure(fss, "passread cannot happen");
+ snprint(buf, sizeof buf, "%q %q", user, pass);
+ m = strlen(buf);
+ if(m > *n)
+ return toosmall(fss, m);
+ *n = m;
+ memmove(va, buf, m);
+ return RpcOk;
+ }
+}
+
+static int
+passwrite(Fsstate *fss, void*, uint)
+{
+ return phaseerror(fss, "write");
+}
+
+Proto pass =
+{
+.name= "pass",
+.init= passinit,
+.write= passwrite,
+.read= passread,
+.close= passclose,
+.addkey= replacekey,
+.keyprompt= "user? !password?",
+};
diff --git a/src/cmd/factotum/plan9.c b/src/cmd/factotum/plan9.c
new file mode 100644
index 00000000..048c1906
--- /dev/null
+++ b/src/cmd/factotum/plan9.c
@@ -0,0 +1,189 @@
+#include "std.h"
+#include "dat.h"
+#include <bio.h>
+
+int
+memrandom(void *p, int n)
+{
+ uchar *cp;
+
+ for(cp = (uchar*)p; n > 0; n--)
+ *cp++ = fastrand();
+ return 0;
+}
+
+/*
+ * create a change uid capability
+ */
+static int caphashfd;
+
+static char*
+mkcap(char *from, char *to)
+{
+ uchar rand[20];
+ char *cap;
+ char *key;
+ int nfrom, nto;
+ uchar hash[SHA1dlen];
+
+ if(caphashfd < 0)
+ return nil;
+
+ /* create the capability */
+ nto = strlen(to);
+ nfrom = strlen(from);
+ cap = emalloc(nfrom+1+nto+1+sizeof(rand)*3+1);
+ sprint(cap, "%s@%s", from, to);
+ memrandom(rand, sizeof(rand));
+ key = cap+nfrom+1+nto+1;
+ enc64(key, sizeof(rand)*3, rand, sizeof(rand));
+
+ /* hash the capability */
+ hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
+
+ /* give the kernel the hash */
+ key[-1] = '@';
+ if(write(caphashfd, hash, SHA1dlen) < 0){
+ free(cap);
+ return nil;
+ }
+
+ return cap;
+}
+
+Attr*
+addcap(Attr *a, char *from, Ticket *t)
+{
+ char *cap;
+
+ cap = mkcap(from, t->suid);
+ return addattr(a, "cuid=%q suid=%q cap=%q", t->cuid, t->suid, cap);
+}
+
+/* bind in the default network and cs */
+static int
+bindnetcs(void)
+{
+ int srvfd;
+
+ if(access("/net/tcp", AEXIST) < 0)
+ bind("#I", "/net", MBEFORE);
+
+ if(access("/net/cs", AEXIST) < 0){
+ if((srvfd = open("#s/cs", ORDWR)) >= 0){
+ /* mount closes srvfd on success */
+ if(mount(srvfd, -1, "/net", MBEFORE, "") >= 0)
+ return 0;
+ close(srvfd);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+int
+_authdial(char *net, char *authdom)
+{
+ int vanilla;
+
+ vanilla = net==nil || strcmp(net, "/net")==0;
+
+ if(!vanilla || bindnetcs()>=0)
+ return authdial(net, authdom);
+
+ /* use the auth sever passed to us as an arg */
+ if(authaddr == nil)
+ return -1;
+ return dial(netmkaddr(authaddr, "tcp", "567"), 0, 0, 0);
+}
+
+Key*
+plan9authkey(Attr *a)
+{
+ char *dom;
+ Key *k;
+
+ /*
+ * The only important part of a is dom.
+ * We don't care, for example, about user name.
+ */
+ dom = strfindattr(a, "dom");
+ if(dom)
+ k = keylookup("proto=p9sk1 role=server user? dom=%q", dom);
+ else
+ k = keylookup("proto=p9sk1 role=server user? dom?");
+ if(k == nil)
+ werrstr("could not find plan 9 auth key dom %q", dom);
+ return k;
+}
+
+/*
+ * prompt for a string with a possible default response
+ */
+char*
+readcons(char *prompt, char *def, int raw)
+{
+ int fdin, fdout, ctl, n;
+ char line[10];
+ char *s;
+
+ fdin = open("/dev/cons", OREAD);
+ if(fdin < 0)
+ fdin = 0;
+ fdout = open("/dev/cons", OWRITE);
+ if(fdout < 0)
+ fdout = 1;
+ if(def != nil)
+ fprint(fdout, "%s[%s]: ", prompt, def);
+ else
+ fprint(fdout, "%s: ", prompt);
+ if(raw){
+ ctl = open("/dev/consctl", OWRITE);
+ if(ctl >= 0)
+ write(ctl, "rawon", 5);
+ } else
+ ctl = -1;
+ s = estrdup("");
+ for(;;){
+ n = read(fdin, line, 1);
+ if(n == 0){
+ Error:
+ close(fdin);
+ close(fdout);
+ if(ctl >= 0)
+ close(ctl);
+ free(s);
+ return nil;
+ }
+ if(n < 0)
+ goto Error;
+ if(line[0] == 0x7f)
+ goto Error;
+ if(n == 0 || line[0] == '\n' || line[0] == '\r'){
+ if(raw){
+ write(ctl, "rawoff", 6);
+ write(fdout, "\n", 1);
+ }
+ close(ctl);
+ close(fdin);
+ close(fdout);
+ if(*s == 0 && def != nil)
+ s = estrappend(s, "%s", def);
+ return s;
+ }
+ if(line[0] == '\b'){
+ if(strlen(s) > 0)
+ s[strlen(s)-1] = 0;
+ } else if(line[0] == 0x15) { /* ^U: line kill */
+ if(def != nil)
+ fprint(fdout, "\n%s[%s]: ", prompt, def);
+ else
+ fprint(fdout, "\n%s: ", prompt);
+
+ s[0] = 0;
+ } else {
+ s = estrappend(s, "%c", line[0]);
+ }
+ }
+ return nil; /* not reached */
+}
diff --git a/src/cmd/factotum/privattr b/src/cmd/factotum/privattr
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/cmd/factotum/privattr
diff --git a/src/cmd/factotum/proto.c b/src/cmd/factotum/proto.c
new file mode 100644
index 00000000..64bb2e34
--- /dev/null
+++ b/src/cmd/factotum/proto.c
@@ -0,0 +1,22 @@
+#include "std.h"
+#include "dat.h"
+
+Proto *prototab[] = {
+ &apop,
+ &cram,
+ &p9any,
+ &p9sk1,
+ &p9sk2,
+ nil,
+};
+
+Proto*
+protolookup(char *name)
+{
+ int i;
+
+ for(i=0; prototab[i]; i++)
+ if(strcmp(prototab[i]->name, name) == 0)
+ return prototab[i];
+ return nil;
+}
diff --git a/src/cmd/factotum/rpc.c b/src/cmd/factotum/rpc.c
new file mode 100644
index 00000000..e9c163aa
--- /dev/null
+++ b/src/cmd/factotum/rpc.c
@@ -0,0 +1,315 @@
+#include "std.h"
+#include "dat.h"
+
+/*
+ * Factotum RPC
+ *
+ * Must be paired write/read cycles on /mnt/factotum/rpc.
+ * The format of a request is verb, single space, data.
+ * Data format is verb-dependent; in particular, it can be binary.
+ * The format of a response is the same. The write only sets up
+ * the RPC. The read tries to execute it. If the /mnt/factotum/key
+ * file is open, we ask for new keys using that instead of returning
+ * an error in the RPC. This means the read blocks.
+ * Textual arguments are parsed with tokenize, so rc-style quoting
+ * rules apply.
+ *
+ * Only authentication protocol messages go here. Configuration
+ * is still via ctl (below).
+ *
+ * Request RPCs are:
+ * start attrs - initializes protocol for authentication, can fail.
+ * returns "ok read" or "ok write" on success.
+ * read - execute protocol read
+ * write - execute protocol write
+ * authinfo - if the protocol is finished, return the AI if any
+ * attr - return protocol information
+ * Return values are:
+ * error message - an error happened.
+ * ok [data] - success, possible data is request dependent.
+ * needkey attrs - request aborted, get me this key and try again
+ * badkey attrs - request aborted, this key might be bad
+ * done [haveai] - authentication is done [haveai: you can get an ai with authinfo]
+ */
+
+char *rpcname[] =
+{
+ "unknown",
+ "authinfo",
+ "attr",
+ "read",
+ "start",
+ "write",
+};
+
+static int
+classify(char *s)
+{
+ int i;
+
+ for(i=1; i<nelem(rpcname); i++)
+ if(strcmp(s, rpcname[i]) == 0)
+ return i;
+ return RpcUnknown;
+}
+
+int
+rpcwrite(Conv *c, void *data, int count)
+{
+ int op;
+ uchar *p;
+
+ if(count >= MaxRpc){
+ werrstr("rpc too large");
+ return -1;
+ }
+
+ /* cancel any current rpc */
+ c->rpc.op = RpcUnknown;
+ c->nreply = 0;
+
+ /* parse new rpc */
+ memmove(c->rpcbuf, data, count);
+ c->rpcbuf[count] = 0;
+ if(p = (uchar*)strchr((char*)c->rpcbuf, ' ')){
+ *p++ = '\0';
+ c->rpc.data = p;
+ c->rpc.count = count - (p - (uchar*)c->rpcbuf);
+ }else{
+ c->rpc.data = "";
+ c->rpc.count = 0;
+ }
+ op = classify(c->rpcbuf);
+ if(op == RpcUnknown){
+ werrstr("bad rpc verb: %s", c->rpcbuf);
+ return -1;
+ }
+
+ c->rpc.op = op;
+ return 0;
+}
+
+void
+convthread(void *v)
+{
+ Conv *c;
+ Attr *a;
+ char *role, *proto;
+ Proto *p;
+ Role *r;
+
+ c = v;
+ a = parseattr(c->rpc.data);
+ if(a == nil){
+ werrstr("empty attr");
+ goto out;
+ }
+ c->attr = a;
+ proto = strfindattr(a, "proto");
+ role = strfindattr(a, "role");
+
+ if(proto == nil){
+ werrstr("no proto in attrs");
+ goto out;
+ }
+ if(role == nil){
+ werrstr("no role in attrs");
+ goto out;
+ }
+
+ p = protolookup(proto);
+ if(p == nil){
+ werrstr("unknown proto %s", proto);
+ goto out;
+ }
+
+ c->proto = p;
+ for(r=p->roles; r->name; r++){
+ if(strcmp(r->name, role) != 0)
+ continue;
+ rpcrespond(c, "ok");
+ c->active = 1;
+ if((*r->fn)(c) == 0){
+ c->done = 1;
+ werrstr("protocol finished");
+ }else
+ werrstr("%s %s %s: %r", p->name, r->name, c->state);
+ goto out;
+ }
+ werrstr("unknown role");
+
+out:
+ c->active = 0;
+ c->state = 0;
+ rerrstr(c->err, sizeof c->err);
+ rpcrespond(c, "error %r");
+ convclose(c);
+}
+
+static uchar* convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex);
+
+void
+rpcexec(Conv *c)
+{
+ uchar *p;
+
+ switch(c->rpc.op){
+ case RpcRead:
+ if(c->rpc.count > 0){
+ rpcrespond(c, "error read takes no parameters");
+ break;
+ }
+ /* fall through */
+ default:
+ if(!c->active){
+ if(c->done)
+ rpcrespond(c, "done");
+ else
+ rpcrespond(c, "error %s", c->err);
+ break;
+ }
+ nbsendp(c->rpcwait, 0);
+ break;
+ case RpcUnknown:
+ break;
+ case RpcAuthinfo:
+ /* deprecated */
+ if(c->active)
+ rpcrespond(c, "error conversation still active");
+ else if(!c->done)
+ rpcrespond(c, "error conversation not successful");
+ else{
+ /* make up an auth info using the attr */
+ p = convAI2M((uchar*)c->reply+3, sizeof c->reply-3,
+ strfindattr(c->attr, "cuid"),
+ strfindattr(c->attr, "suid"),
+ strfindattr(c->attr, "cap"),
+ strfindattr(c->attr, "secret"));
+ if(p == nil)
+ rpcrespond(c, "error %r");
+ else
+ rpcrespondn(c, "ok", c->reply+3, p-(uchar*)(c->reply+3));
+ }
+ break;
+ case RpcAttr:
+ rpcrespond(c, "ok %A", c->attr);
+ break;
+ case RpcStart:
+ convreset(c);
+ c->ref++;
+ threadcreate(convthread, c, STACK);
+ break;
+ }
+}
+
+void
+rpcrespond(Conv *c, char *fmt, ...)
+{
+ va_list arg;
+
+ if(c->hangup)
+ return;
+
+ if(fmt == nil)
+ fmt = "";
+
+ va_start(arg, fmt);
+ c->nreply = vsnprint(c->reply, sizeof c->reply, fmt, arg);
+ va_end(arg);
+ (*c->kickreply)(c);
+ c->rpc.op = RpcUnknown;
+}
+
+void
+rpcrespondn(Conv *c, char *verb, void *data, int count)
+{
+ char *p;
+
+ if(c->hangup)
+ return;
+
+ if(strlen(verb)+1+count > sizeof c->reply){
+ print("RPC response too large; caller %#lux", getcallerpc(&c));
+ return;
+ }
+
+ strcpy(c->reply, verb);
+ p = c->reply + strlen(c->reply);
+ *p++ = ' ';
+ memmove(p, data, count);
+ c->nreply = count + (p - c->reply);
+ (*c->kickreply)(c);
+ c->rpc.op = RpcUnknown;
+}
+
+/* deprecated */
+static uchar*
+pstring(uchar *p, uchar *e, char *s)
+{
+ uint n;
+
+ if(p == nil)
+ return nil;
+ if(s == nil)
+ s = "";
+ n = strlen(s);
+ if(p+n+BIT16SZ >= e)
+ return nil;
+ PBIT16(p, n);
+ p += BIT16SZ;
+ memmove(p, s, n);
+ p += n;
+ return p;
+}
+
+static uchar*
+pcarray(uchar *p, uchar *e, uchar *s, uint n)
+{
+ if(p == nil)
+ return nil;
+ if(s == nil){
+ if(n > 0)
+ sysfatal("pcarray");
+ s = (uchar*)"";
+ }
+ if(p+n+BIT16SZ >= e)
+ return nil;
+ PBIT16(p, n);
+ p += BIT16SZ;
+ memmove(p, s, n);
+ p += n;
+ return p;
+}
+
+static uchar*
+convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex)
+{
+ uchar *e = p+n;
+ uchar *secret;
+ int nsecret;
+
+ if(cuid == nil)
+ cuid = "";
+ if(suid == nil)
+ suid = "";
+ if(cap == nil)
+ cap = "";
+ if(hex == nil)
+ hex = "";
+ nsecret = strlen(hex)/2;
+ secret = emalloc(nsecret);
+ if(hexparse(hex, secret, nsecret) < 0){
+ werrstr("hexparse %s failed", hex); /* can't happen */
+ free(secret);
+ return nil;
+ }
+ p = pstring(p, e, cuid);
+ p = pstring(p, e, suid);
+ p = pstring(p, e, cap);
+ p = pcarray(p, e, secret, nsecret);
+ free(secret);
+ if(p == nil)
+ werrstr("authinfo too big");
+ return p;
+}
+
diff --git a/src/cmd/factotum/ssh.c b/src/cmd/factotum/ssh.c
new file mode 100644
index 00000000..4c88bfe6
--- /dev/null
+++ b/src/cmd/factotum/ssh.c
@@ -0,0 +1,135 @@
+#include "dat.h"
+#include <mp.h>
+#include <libsec.h>
+
+typedef struct Sshrsastate Sshrsastate;
+
+enum {
+ CReadpub,
+ CWritechal,
+ CReadresp,
+};
+struct State
+{
+ RSApriv *priv;
+ Key *k;
+ mpint *resp;
+ int phase;
+};
+
+static RSApriv*
+readrsapriv(char *s)
+{
+ RSApriv *priv;
+
+ priv = rsaprivalloc();
+
+ strtoul(s, &s, 10);
+ if((priv->pub.ek=strtomp(s, &s, 16, nil)) == nil)
+ goto Error;
+ if((priv->dk=strtomp(s, &s, 16, nil)) == nil)
+ goto Error;
+ if((priv->pub.n=strtomp(s, &s, 16, nil)) == nil)
+ goto Error;
+ if((priv->p=strtomp(s, &s, 16, nil)) == nil)
+ goto Error;
+ if((priv->q=strtomp(s, &s, 16, nil)) == nil)
+ goto Error;
+ if((priv->kp=strtomp(s, &s, 16, nil)) == nil)
+ goto Error;
+ if((priv->kq=strtomp(s, &s, 16, nil)) == nil)
+ goto Error;
+ if((priv->c2=strtomp(s, &s, 16, nil)) == nil)
+ goto Error;
+
+ return priv;
+
+Error:
+ rsaprivfree(priv);
+ return nil;
+}
+
+int
+sshinit(Fsstate *fss,
+sshrsaopen(Key *k, char*, int client)
+{
+ Sshrsastate *s;
+
+ fmtinstall('B', mpconv);
+ assert(client);
+ s = emalloc(sizeof *s);
+ s->priv = readrsapriv(s_to_c(k->data));
+ s->k = k;
+ if(s->priv == nil){
+ agentlog("error parsing ssh key %s", k->file);
+ free(s);
+ return nil;
+ }
+ return s;
+}
+
+int
+sshrsaread(void *va, void *buf, int n)
+{
+ Sshrsastate *s;
+
+ s = va;
+ switch(s->phase){
+ case Readpub:
+ s->phase = Done;
+ return snprint(buf, n, "%B", s->priv->pub.n);
+ case Readresp:
+ s->phase = Done;
+ return snprint(buf, n, "%B", s->resp);
+ default:
+ return 0;
+ }
+}
+
+int
+sshrsawrite(void *va, void *vbuf, int n)
+{
+ mpint *m;
+ char *buf;
+ Sshrsastate *s;
+
+ s = va;
+ if((s->k->flags&Fconfirmuse) && confirm("ssh use") < 0)
+ return -1;
+
+ buf = emalloc(n+1);
+ memmove(buf, vbuf, n);
+ buf[n] = '\0';
+ m = strtomp(buf, nil, 16, nil);
+ free(buf);
+ if(m == nil){
+ werrstr("bad bignum");
+ return -1;
+ }
+
+ agentlog("ssh use");
+ m = rsadecrypt(s->priv, m, m);
+ s->resp = m;
+ s->phase = Readresp;
+ return n;
+}
+
+void
+sshrsaclose(void *v)
+{
+ Sshrsastate *s;
+
+ s = v;
+ rsaprivfree(s->priv);
+ mpfree(s->resp);
+ free(s);
+}
+
+Proto sshrsa = {
+.name= "ssh-rsa",
+.perm= 0666,
+.open= sshrsaopen,
+.read= sshrsaread,
+.write= sshrsawrite,
+.close= sshrsaclose,
+};
diff --git a/src/cmd/factotum/sshrsa.c b/src/cmd/factotum/sshrsa.c
new file mode 100644
index 00000000..7227c855
--- /dev/null
+++ b/src/cmd/factotum/sshrsa.c
@@ -0,0 +1,172 @@
+/*
+ * SSH RSA authentication.
+ *
+ * Client protocol:
+ * read public key
+ * if you don't like it, read another, repeat
+ * write challenge
+ * read response
+ * all numbers are hexadecimal biginits parsable with strtomp.
+ */
+
+#include "dat.h"
+
+enum {
+ CHavePub,
+ CHaveResp,
+
+ Maxphase,
+};
+
+static char *phasenames[] = {
+[CHavePub] "CHavePub",
+[CHaveResp] "CHaveResp",
+};
+
+struct State
+{
+ RSApriv *priv;
+ mpint *resp;
+ int off;
+ Key *key;
+};
+
+static RSApriv*
+readrsapriv(Key *k)
+{
+ char *a;
+ RSApriv *priv;
+
+ priv = rsaprivalloc();
+
+ if((a=strfindattr(k->attr, "ek"))==nil || (priv->pub.ek=strtomp(a, nil, 16, nil))==nil)
+ goto Error;
+ if((a=strfindattr(k->attr, "n"))==nil || (priv->pub.n=strtomp(a, nil, 16, nil))==nil)
+ goto Error;
+ if((a=strfindattr(k->privattr, "!p"))==nil || (priv->p=strtomp(a, nil, 16, nil))==nil)
+ goto Error;
+ if((a=strfindattr(k->privattr, "!q"))==nil || (priv->q=strtomp(a, nil, 16, nil))==nil)
+ goto Error;
+ if((a=strfindattr(k->privattr, "!kp"))==nil || (priv->kp=strtomp(a, nil, 16, nil))==nil)
+ goto Error;
+ if((a=strfindattr(k->privattr, "!kq"))==nil || (priv->kq=strtomp(a, nil, 16, nil))==nil)
+ goto Error;
+ if((a=strfindattr(k->privattr, "!c2"))==nil || (priv->c2=strtomp(a, nil, 16, nil))==nil)
+ goto Error;
+ if((a=strfindattr(k->privattr, "!dk"))==nil || (priv->dk=strtomp(a, nil, 16, nil))==nil)
+ goto Error;
+ return priv;
+
+Error:
+ rsaprivfree(priv);
+ return nil;
+}
+
+static int
+sshrsainit(Proto*, Fsstate *fss)
+{
+ int iscli;
+ State *s;
+
+ if((iscli = isclient(strfindattr(fss->attr, "role"))) < 0)
+ return failure(fss, nil);
+ if(iscli==0)
+ return failure(fss, "sshrsa server unimplemented");
+
+ s = emalloc(sizeof *s);
+ fss->phasename = phasenames;
+ fss->maxphase = Maxphase;
+ fss->phase = CHavePub;
+ fss->ps = s;
+ return RpcOk;
+}
+
+static int
+sshrsaread(Fsstate *fss, void *va, uint *n)
+{
+ RSApriv *priv;
+ State *s;
+
+ s = fss->ps;
+ switch(fss->phase){
+ default:
+ return phaseerror(fss, "read");
+ case CHavePub:
+ if(s->key){
+ closekey(s->key);
+ s->key = nil;
+ }
+ if((s->key = findkey(fss, Kuser, nil, s->off, fss->attr, nil)) == nil)
+ return failure(fss, nil);
+ s->off++;
+ priv = s->key->priv;
+ *n = snprint(va, *n, "%B", priv->pub.n);
+ return RpcOk;
+ case CHaveResp:
+ *n = snprint(va, *n, "%B", s->resp);
+ fss->phase = Established;
+ return RpcOk;
+ }
+}
+
+static int
+sshrsawrite(Fsstate *fss, void *va, uint)
+{
+ mpint *m;
+ State *s;
+
+ s = fss->ps;
+ switch(fss->phase){
+ default:
+ return phaseerror(fss, "write");
+ case CHavePub:
+ if(s->key == nil)
+ return failure(fss, "no current key");
+ m = strtomp(va, nil, 16, nil);
+ m = rsadecrypt(s->key->priv, m, m);
+ s->resp = m;
+ fss->phase = CHaveResp;
+ return RpcOk;
+ }
+}
+
+static void
+sshrsaclose(Fsstate *fss)
+{
+ State *s;
+
+ s = fss->ps;
+ if(s->key)
+ closekey(s->key);
+ if(s->resp)
+ mpfree(s->resp);
+ free(s);
+}
+
+static int
+sshrsaaddkey(Key *k)
+{
+ fmtinstall('B', mpconv);
+
+ if((k->priv = readrsapriv(k)) == nil){
+ werrstr("malformed key data");
+ return -1;
+ }
+ return replacekey(k);
+}
+
+static void
+sshrsaclosekey(Key *k)
+{
+ rsaprivfree(k->priv);
+}
+
+Proto sshrsa = {
+.name= "sshrsa",
+.init= sshrsainit,
+.write= sshrsawrite,
+.read= sshrsaread,
+.close= sshrsaclose,
+.addkey= sshrsaaddkey,
+.closekey= sshrsaclosekey,
+};
diff --git a/src/cmd/factotum/std.h b/src/cmd/factotum/std.h
new file mode 100644
index 00000000..814664e0
--- /dev/null
+++ b/src/cmd/factotum/std.h
@@ -0,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <authsrv.h>
+#include <mp.h>
+#include <libsec.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+
diff --git a/src/cmd/factotum/test.c b/src/cmd/factotum/test.c
new file mode 100644
index 00000000..b4104898
--- /dev/null
+++ b/src/cmd/factotum/test.c
@@ -0,0 +1,121 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+typedef struct Test Test;
+
+struct Test
+{
+ char *name;
+ int (*server)(Test*, AuthRpc*, int);
+ int (*client)(Test*, int);
+};
+
+int
+ai2status(AuthInfo *ai)
+{
+ if(ai == nil)
+ return -1;
+ auth_freeAI(ai);
+ return 0;
+}
+
+int
+proxyserver(Test *t, AuthRpc *rpc, int fd)
+{
+ char buf[1024];
+
+ sprint(buf, "proto=%q role=server", t->name);
+ return ai2status(fauth_proxy(fd, rpc, nil, buf));
+}
+
+int
+proxyclient(Test *t, int fd)
+{
+ return ai2status(auth_proxy(fd, auth_getkey, "proto=%q role=client", t->name));
+}
+
+Test test[] =
+{
+ "apop", proxyserver, proxyclient,
+ "cram", proxyserver, proxyclient,
+ "p9sk1", proxyserver, proxyclient,
+ "p9sk2", proxyserver, proxyclient,
+ "p9any", proxyserver, proxyclient,
+};
+
+void
+usage(void)
+{
+ fprint(2, "usage: test [name]...\n");
+ exits("usage");
+}
+
+void
+runtest(AuthRpc *srpc, Test *t)
+{
+ int p[2], bad;
+ Waitmsg *w;
+
+ if(pipe(p) < 0)
+ sysfatal("pipe: %r");
+
+ print("%s...", t->name);
+
+ switch(fork()){
+ case -1:
+ sysfatal("fork: %r");
+
+ case 0:
+ close(p[0]);
+ if((*t->server)(t, srpc, p[1]) < 0){
+ print("\n\tserver: %r");
+ _exits("oops");
+ }
+ close(p[1]);
+ _exits(nil);
+ default:
+ close(p[1]);
+ if((*t->client)(t, p[0]) < 0){
+ print("\n\tclient: %r");
+ bad = 1;
+ }
+ close(p[0]);
+ break;
+ }
+ w = wait();
+ if(w->msg[0])
+ bad = 1;
+ print("\n");
+}
+
+void
+main(int argc, char **argv)
+{
+ int i, j;
+ int afd;
+ AuthRpc *srpc;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ quotefmtinstall();
+ afd = open("/n/kremvax/factotum/rpc", ORDWR);
+ if(afd < 0)
+ sysfatal("open /n/kremvax/factotum/rpc: %r");
+ srpc = auth_allocrpc(afd);
+ if(srpc == nil)
+ sysfatal("auth_allocrpc: %r");
+
+ if(argc == 0)
+ for(i=0; i<nelem(test); i++)
+ runtest(srpc, &test[i]);
+ else
+ for(i=0; i<argc; i++)
+ for(j=0; j<nelem(test); j++)
+ if(strcmp(argv[i], test[j].name) == 0)
+ runtest(srpc, &test[j]);
+ exits(nil);
+}
diff --git a/src/cmd/factotum/testsetup b/src/cmd/factotum/testsetup
new file mode 100755
index 00000000..2f9f9b1d
--- /dev/null
+++ b/src/cmd/factotum/testsetup
@@ -0,0 +1,11 @@
+#!/bin/rc
+
+slay 8.out|rc
+8.out $* -s fact.s -m /n/kremvax
+8.out $* -s fact.c
+ramfs -m /n/sid >[2]/dev/null
+auth/aescbc -d < /usr/rsc/lib/factotum.aes >/n/sid/all
+read -m /n/sid/all >/n/kremvax/factotum/ctl
+read -m /n/sid/all >/mnt/factotum/ctl
+unmount /n/sid
+
diff --git a/src/cmd/factotum/util.c b/src/cmd/factotum/util.c
new file mode 100644
index 00000000..54b33519
--- /dev/null
+++ b/src/cmd/factotum/util.c
@@ -0,0 +1,52 @@
+#include "std.h"
+#include "dat.h"
+
+static int
+unhex(char c)
+{
+ if('0' <= c && c <= '9')
+ return c-'0';
+ if('a' <= c && c <= 'f')
+ return c-'a'+10;
+ if('A' <= c && c <= 'F')
+ return c-'A'+10;
+ abort();
+ return -1;
+}
+
+int
+hexparse(char *hex, uchar *dat, int ndat)
+{
+ int i, n;
+
+ n = strlen(hex);
+ if(n%2)
+ return -1;
+ n /= 2;
+ if(n > ndat)
+ return -1;
+ if(hex[strspn(hex, "0123456789abcdefABCDEF")] != '\0')
+ return -1;
+ for(i=0; i<n; i++)
+ dat[i] = (unhex(hex[2*i])<<4)|unhex(hex[2*i+1]);
+ return n;
+}
+
+char*
+estrappend(char *s, char *fmt, ...)
+{
+ char *t;
+ va_list arg;
+
+ va_start(arg, fmt);
+ t = vsmprint(fmt, arg);
+ if(t == nil)
+ sysfatal("out of memory");
+ va_end(arg);
+ s = erealloc(s, strlen(s)+strlen(t)+1);
+ strcat(s, t);
+ free(t);
+ return s;
+}
+
+
diff --git a/src/cmd/factotum/x.c b/src/cmd/factotum/x.c
new file mode 100644
index 00000000..3bedfdd4
--- /dev/null
+++ b/src/cmd/factotum/x.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+void
+f(void*)
+{
+}
+
+void
+main(void)
+{
+ f(auth_challenge);
+ f(auth_response);
+}
diff --git a/src/cmd/factotum/xio.c b/src/cmd/factotum/xio.c
new file mode 100644
index 00000000..50890847
--- /dev/null
+++ b/src/cmd/factotum/xio.c
@@ -0,0 +1,165 @@
+#include "std.h"
+#include "dat.h"
+
+static Ioproc *cache[5];
+static int ncache;
+
+static Ioproc*
+xioproc(void)
+{
+ Ioproc *c;
+ int i;
+
+ for(i=0; i<ncache; i++){
+ if(c = cache[i]){
+ cache[i] = nil;
+ return c;
+ }
+ }
+
+ return ioproc();
+}
+
+static void
+closexioproc(Ioproc *io)
+{
+ int i;
+
+ for(i=0; i<ncache; i++)
+ if(cache[i] == nil){
+ cache[i] = io;
+ return;
+ }
+
+ closeioproc(io);
+}
+
+int
+xiodial(char *ds, char *local, char *dir, int *cfdp)
+{
+ int fd;
+ Ioproc *io;
+
+ if((io = xioproc()) == nil)
+ return -1;
+ fd = iodial(io, ds, local, dir, cfdp);
+ closexioproc(io);
+ return fd;
+}
+
+void
+xioclose(int fd)
+{
+ Ioproc *io;
+
+ if((io = xioproc()) == nil){
+ close(fd);
+ return;
+ }
+
+ ioclose(io, fd);
+ closexioproc(io);
+}
+
+int
+xiowrite(int fd, void *v, int n)
+{
+ int m;
+ Ioproc *io;
+
+ if((io = xioproc()) == nil)
+ return -1;
+ m = iowrite(io, fd, v, n);
+ closexioproc(io);
+ if(m != n)
+ return -1;
+ return n;
+}
+
+static long
+_ioauthdial(va_list *arg)
+{
+ char *net;
+ char *dom;
+ int fd;
+
+ net = va_arg(*arg, char*);
+ dom = va_arg(*arg, char*);
+ fd = _authdial(net, dom);
+ if(fd < 0)
+ fprint(2, "authdial: %r");
+ return fd;
+}
+
+int
+xioauthdial(char *net, char *dom)
+{
+ int fd;
+ Ioproc *io;
+
+ if((io = xioproc()) == nil)
+ return -1;
+ fd = iocall(io, _ioauthdial, net, dom);
+ closexioproc(io);
+ return fd;
+}
+
+static long
+_ioasrdresp(va_list *arg)
+{
+ int fd;
+ void *a;
+ int n;
+
+ fd = va_arg(*arg, int);
+ a = va_arg(*arg, void*);
+ n = va_arg(*arg, int);
+
+ return _asrdresp(fd, a, n);
+}
+
+int
+xioasrdresp(int fd, void *a, int n)
+{
+ Ioproc *io;
+
+ if((io = xioproc()) == nil)
+ return -1;
+
+ n = iocall(io, _ioasrdresp, fd, a, n);
+ closexioproc(io);
+ return n;
+}
+
+static long
+_ioasgetticket(va_list *arg)
+{
+ int asfd;
+ char *trbuf;
+ char *tbuf;
+
+ asfd = va_arg(*arg, int);
+ trbuf = va_arg(*arg, char*);
+ tbuf = va_arg(*arg, char*);
+
+ return _asgetticket(asfd, trbuf, tbuf);
+}
+
+int
+xioasgetticket(int fd, char *trbuf, char *tbuf)
+{
+ int n;
+ Ioproc *io;
+
+ if((io = xioproc()) == nil)
+ return -1;
+
+ n = iocall(io, _ioasgetticket, fd, trbuf, tbuf);
+ closexioproc(io);
+ if(n != 2*TICKETLEN)
+ n = -1;
+ else
+ n = 0;
+ return n;
+}
+
diff --git a/src/cmd/mkfile b/src/cmd/mkfile
index bf7200de..3ad7ea7a 100644
--- a/src/cmd/mkfile
+++ b/src/cmd/mkfile
@@ -7,7 +7,7 @@ SHORTLIB=sec fs mux regexp9 thread bio 9
<$PLAN9/src/mkmany
-BUGGERED='CVS|faces|factotum|oplumb|plumb2|mk|vac|9term|upas|venti|htmlfmt'
+BUGGERED='CVS|9term|faces|factotum|htmlfmt|mk|rio|upas|vac|venti'
DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "^($BUGGERED)$"`
<$PLAN9/src/mkdirs
diff --git a/src/cmd/plumb/plumber.c b/src/cmd/plumb/plumber.c
index 54c60521..80a57af3 100644
--- a/src/cmd/plumb/plumber.c
+++ b/src/cmd/plumb/plumber.c
@@ -54,6 +54,8 @@ threadmain(int argc, char *argv[])
error("can't initialize $user or $home: %r");
if(plumbfile == nil){
sprint(buf, "%s/lib/plumbing", home);
+ if(access(buf, 0) < 0)
+ sprint(buf, "#9/plumb/initial.plumbing");
plumbfile = estrdup(buf);
}
diff --git a/src/cmd/rc/plan9ish.c b/src/cmd/rc/plan9ish.c
index 6e9d59f1..eb93a4f5 100644
--- a/src/cmd/rc/plan9ish.c
+++ b/src/cmd/rc/plan9ish.c
@@ -81,7 +81,7 @@ void Vinit(void){
for(s=*env;*s && *s!='(' && *s!='=';s++);
switch(*s){
case '\0':
- pfmt(err, "environment %q?\n", *env);
+ pfmt(err, "rc: odd environment %q?\n", *env);
break;
case '=':
*s='\0';
diff --git a/src/cmd/rc/rc.h b/src/cmd/rc/rc.h
index c81b6280..7df5fea0 100644
--- a/src/cmd/rc/rc.h
+++ b/src/cmd/rc/rc.h
@@ -26,6 +26,9 @@
#include "x.tab.h"
#endif
#endif
+
+#undef pipe /* so that /dev/fd works */
+
typedef struct tree tree;
typedef struct word word;
typedef struct io io;
diff --git a/src/cmd/samterm/plan9.c b/src/cmd/samterm/plan9.c
index 7c4ed39b..f8614d3f 100644
--- a/src/cmd/samterm/plan9.c
+++ b/src/cmd/samterm/plan9.c
@@ -10,6 +10,12 @@
#include <cursor.h>
#include <keyboard.h>
#include <frame.h>
+#define Tversion Tversion9p
+#define Twrite Twrite9p
+#include <fcall.h>
+#undef Tversion
+#undef Twrite
+#include <fs.h>
#include <plumb.h>
#include "flayer.h"
#include "samterm.h"
@@ -212,27 +218,22 @@ plumbformat(Plumbmsg *m, int i)
}
void
-plumbproc(void *argv)
+plumbproc(void *arg)
{
- Channel *c;
- int i, *fdp;
- void **arg;
+ Fid *fid;
+ int i;
Plumbmsg *m;
- arg = argv;
- c = arg[0];
- fdp = arg[1];
-
+ fid = arg;
i = 0;
- threadfdnoblock(*fdp);
for(;;){
- m = threadplumbrecv(*fdp);
+ m = plumbrecvfid(fid);
if(m == nil){
fprint(2, "samterm: plumb read error: %r\n");
threadexits("plumb"); /* not a fatal error */
}
if(plumbformat(m, i)){
- send(c, &i);
+ send(plumbc, &i);
i = 1-i; /* toggle */
}
}
@@ -241,21 +242,18 @@ plumbproc(void *argv)
int
plumbstart(void)
{
- static int fd;
- static void *arg[2];
+ Fid *fid;
plumbfd = plumbopen("send", OWRITE|OCEXEC); /* not open is ok */
- fd = plumbopen("edit", OREAD|OCEXEC);
- if(fd < 0)
+ fid = plumbopenfid("edit", OREAD|OCEXEC);
+ if(fid == nil)
return -1;
plumbc = chancreate(sizeof(int), 0);
if(plumbc == nil){
- close(fd);
+ fsclose(fid);
return -1;
}
- arg[0] = plumbc;
- arg[1] = &fd;
- threadcreate(plumbproc, arg, STACK);
+ threadcreate(plumbproc, fid, STACK);
return 1;
}
diff --git a/src/cmd/vac/vac.c b/src/cmd/vac/vac.c
index f6efb917..a10ce3d0 100644
--- a/src/cmd/vac/vac.c
+++ b/src/cmd/vac/vac.c
@@ -1,8 +1,11 @@
+#include <sys/stat.h>
#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
+int mainstacksize = 128*1024;
+
typedef struct Sink Sink;
typedef struct MetaSink MetaSink;
typedef struct DirSink DirSink;
@@ -170,6 +173,9 @@ threadmain(int argc, char *argv[])
break;
}ARGEND;
+ if(argc == 0)
+ usage();
+
if(bsize < 512)
bsize = 512;
if(bsize > VtMaxLumpSize)
@@ -215,8 +221,6 @@ vacwrite(VtConn *z, uchar score[VtScoreSize], int type, uchar *buf, int n)
sha1(buf, n, score, nil);
return 0;
}
-sha1(buf, n, score, nil);
-fprint(2, "write %V %d\n", score, type);
return vtwrite(z, score, type, buf, n);
}
@@ -377,6 +381,18 @@ isexcluded(char *name)
return 0;
}
+static int
+islink(char *name)
+{
+ struct stat st;
+
+ if(lstat(name, &st) < 0)
+ return 0;
+ if((st.st_mode&S_IFMT) == S_IFLNK)
+ return 1;
+ return 0;
+}
+
static void
vacfile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
{
@@ -393,6 +409,9 @@ vacfile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
if(merge && vacmerge(dsink, lname, sname) >= 0)
return;
+ if(islink(sname))
+ return;
+
fd = open(sname, OREAD);
if(fd < 0) {
warn("could not open file: %s: %r", lname);
@@ -820,10 +839,8 @@ sinkclose(Sink *k)
if(k->pbuf[n] > k->buf + kd->psize*n)
break;
-fprint(2, "type %d -> ", kd->type);
base = kd->type&~VtTypeDepthMask;
kd->type = base + sizetodepth(kd->size, kd->psize, kd->dsize);
-fprint(2, "%d ", kd->type);
/* skip full part of tree */
for(i=0; i<n && k->pbuf[i] == k->buf + kd->psize*i; i++)
@@ -831,7 +848,6 @@ fprint(2, "%d ", kd->type);
/* is the tree completely full */
if(i == n && k->pbuf[n] == k->buf + kd->psize*n + VtScoreSize) {
-fprint(2, "full\n");
memmove(kd->score, k->pbuf[n] - VtScoreSize, VtScoreSize);
return;
}
@@ -846,7 +862,6 @@ fprint(2, "full\n");
k->pbuf[i+1] += VtScoreSize;
}
memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize);
-fprint(2, "%V\n", kd->score);
}
void
@@ -881,7 +896,6 @@ dirsinkwrite(DirSink *k, VtEntry *dir)
sinkwrite(k->sink, k->buf, k->p - k->buf);
k->p = k->buf;
}
-fprint(2, "write entry %V %d\n", dir->score, dir->type);
vtentrypack(dir, k->p, 0);
k->nentry++;
k->p += VtEntrySize;