shithub: drawterm

Download patch

ref: ab73f2bbf59c2cdccc21952d9a13466793262b4b
parent: 06b60293ad32c72b3ee5809d47a4c8ed83776d4a
author: Russ Cox <rsc@swtch.com>
date: Fri Nov 4 10:36:40 EST 2005

Add real factotum.

--- a/Makefile
+++ b/Makefile
@@ -8,10 +8,12 @@
 	readcons.$O\
 	secstore.$O\
 	latin1.$O\
+	$(OS)-factotum.$O\
 
-LIBS=\
+LIBS1=\
 	kern/libkern.a\
 	exportfs/libexportfs.a\
+	libauth/libauth.a\
 	libauthsrv/libauthsrv.a\
 	libsec/libsec.a\
 	libmp/libmp.a\
@@ -18,18 +20,12 @@
 	libmemdraw/libmemdraw.a\
 	libmemlayer/libmemlayer.a\
 	libdraw/libdraw.a\
-	libc/libc.a\
-	kern/libkern.a\
 	gui-$(GUI)/libgui.a\
-	libmemdraw/libmemdraw.a\
-	libdraw/libdraw.a\
-	kern/libkern.a\
 	libc/libc.a\
-	libmemdraw/libmemdraw.a\
-	libmemlayer/libmemlayer.a\
-	libdraw/libdraw.a\
-	libmachdep.a
 
+# stupid gcc
+LIBS=$(LIBS1) $(LIBS1) $(LIBS1) libmachdep.a
+
 $(TARG): $(OFILES) $(LIBS)
 	$(CC) $(LDFLAGS) -o $(TARG) $(OFILES) $(LIBS) $(LDADD)
 
@@ -45,6 +41,9 @@
 exportfs/libexportfs.a:
 	(cd exportfs; make)
 
+libauth/libauth.a:
+	(cd libauth; make)
+	
 libauthsrv/libauthsrv.a:
 	(cd libauthsrv; make)
 
--- a/cpu.c
+++ b/cpu.c
@@ -17,7 +17,6 @@
 #define Maxfdata 8192
 #define MaxStr 128
 
-static char*	getuser(void);
 static void	fatal(int, char*, ...);
 static void	catcher(void*, char*);
 static void	usage(void);
@@ -98,7 +97,9 @@
 		close(fd);
 	}
 
-        user = readcons("user", getenv("USER"), 0);
+        user = getenv("USER");
+        if(user == nil)
+        	user = readcons("user", nil, 0);
 	secstoreserver = nil;
 	ARGBEGIN{
 	case 'a':
@@ -126,6 +127,9 @@
 		}
 		break;
 */
+	case 'k':
+		keyspec = EARGF(usage());
+		break;
 	case 'u':
 		user = EARGF(usage());
 		break;
@@ -136,6 +140,15 @@
 		usage();
 	}ARGEND;
 
+	if((fd = dialfactotum()) < 0)
+		fprint(2, "dial factotum: %r\n");
+	else if(sysmount(fd, -1, "/mnt/factotum", MREPL, "") < 0)
+		fprint(2, "mount factotum: %r\n");
+	else if((fd = open("/mnt/factotum/ctl", OREAD)) < 0)
+		fprint(2, "open /mnt/factotum/ctl: %r\n");
+	else
+		close(fd);
+
 	if(secstoreserver == nil)
 		secstoreserver = authserver;
 
@@ -436,7 +449,97 @@
 	return mkserverticket(tr, key, tbuf);
 }
 
+/*
+ *  prompt user for a key.  don't care about memory leaks, runs standalone
+ */
+static Attr*
+promptforkey(char *params)
+{
+	char *v;
+	int fd;
+	Attr *a, *attr;
+	char *def;
+
+	fd = open("/dev/cons", ORDWR);
+	if(fd < 0)
+		sysfatal("opening /dev/cons: %r");
+
+	attr = _parseattr(params);
+	fprint(fd, "\n!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;
+}
+
+int
+askuser(char *params)
+{
+	Attr *attr;
+	
+	fmtinstall('A', _attrfmt);
+	
+	attr = promptforkey(params);
+	if(attr == nil)
+		sysfatal("no key supplied");
+	if(sendkey(attr) < 0)
+		sysfatal("sending key to factotum: %r");
+	return 0;
+}
+
 AuthInfo*
+p9anyfactotum(int fd, int afd)
+{
+	return auth_proxy(fd, askuser, "proto=p9any role=client %s", keyspec);
+}
+
+AuthInfo*
 p9any(int fd)
 {
 	char buf[1024], buf2[1024], cchal[CHALLEN], *bbuf, *p, *dom, *u;
@@ -444,11 +547,14 @@
 	char tbuf[TICKETLEN+TICKETLEN+AUTHENTLEN], trbuf[TICKREQLEN];
 	char authkey[DESKEYLEN];
 	Authenticator auth;
-	int i, v2, n;
+	int afd, i, v2, n;
 	Ticketreq tr;
 	Ticket t;
 	AuthInfo *ai;
 
+	if((afd = open("/mnt/factotum/ctl", ORDWR)) >= 0)
+		return p9anyfactotum(fd, afd);
+
 	if((n = readstr(fd, buf, sizeof buf)) < 0)
 		fatal(1, "cannot read p9any negotiation");
 	bbuf = buf;
@@ -596,11 +702,5 @@
 	if(ealgs != nil)
 		*ealgs++ = 0;
 	return setam(s);
-}
-
-char*
-getuser(void)
-{
-	return getenv("USER");
 }
 
--- a/drawterm.h
+++ b/drawterm.h
@@ -8,3 +8,5 @@
 extern char *user;
 extern char *getkey(char*, char*);
 extern char *findkey(char**, char*);
+extern int dialfactotum(void);
+extern char *getuser(void);
--- a/exportfs/exportfs.c
+++ b/exportfs/exportfs.c
@@ -99,7 +99,7 @@
 			fatal("convM2S format error");
 		}
 
-//iprint("<- %F\n", &r->work);
+if(0) iprint("<- %F\n", &r->work);
 		DEBUG(DFD, "%F\n", &r->work);
 		(fcalls[r->work.type])(r);
 	}
@@ -110,6 +110,7 @@
 {
 	uchar *data;
 	int m, n;
+static QLock lk;
 
 	t->tag = r->tag;
 	t->fid = r->fid;
@@ -120,7 +121,7 @@
 	else 
 		t->type = r->type + 1;
 
-//iprint("-> %F\n", t);
+if(0) iprint("-> %F\n", t);
 	DEBUG(DFD, "\t%F\n", t);
 
 	data = malloc(messagesize);	/* not mallocz; no need to clear */
--- a/exportfs/exportsrv.c
+++ b/exportfs/exportsrv.c
@@ -431,6 +431,7 @@
 	Proc *p;
 	int pid;
 	static int nproc;
+	static QLock lk;
 
 	for(;;) {
 		for(p = Proclist; p; p = p->next) {
--- a/include/user.h
+++ b/include/user.h
@@ -50,6 +50,8 @@
 extern	int	dirfwstat(int, Dir*);
 extern	long	dirread(int, Dir*, long);
 
+extern	int	lfdfd(int);
+
 /*
  *  network dialing and authentication
  */
--- a/kern/Makefile
+++ b/kern/Makefile
@@ -13,6 +13,7 @@
 	devfs-$(OS).$O\
 	devip.$O\
 	devip-$(OS).$O\
+	devlfd.$O\
 	devmnt.$O\
 	devmouse.$O\
 	devpipe.$O\
--- a/kern/devcons.c
+++ b/kern/devcons.c
@@ -175,25 +175,6 @@
 	return n;
 }
 
-int
-iprint(char *fmt, ...)
-{
-	int n, s;
-	va_list arg;
-	char buf[PRINTSIZE];
-
-	s = splhi();
-	va_start(arg, fmt);
-	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
-	va_end(arg);
-	if(screenputs != nil && iprintscreenputs)
-		screenputs(buf, n);
-	uartputs(buf, n);
-	splx(s);
-
-	return n;
-}
-
 void
 panic(char *fmt, ...)
 {
@@ -1207,4 +1188,24 @@
 	return n;
 }
 
+
+int
+iprint(char *fmt, ...)
+{
+	int n, s;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	s = splhi();
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+	if(screenputs != nil && iprintscreenputs)
+		screenputs(buf, n);
+#undef write
+	write(2, buf, n);
+	splx(s);
+
+	return n;
+}
 
--- /dev/null
+++ b/kern/devlfd.c
@@ -1,0 +1,126 @@
+#include	"u.h"
+#include <errno.h>
+#include	"lib.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#undef pread
+#undef pwrite
+
+Chan*
+lfdchan(int fd)
+{
+	Chan *c;
+	
+	c = newchan();
+	c->type = devno('L', 0);
+	c->aux = (void*)fd;
+	c->name = newcname("fd");
+	c->mode = ORDWR;
+	c->qid.type = 0;
+	c->qid.path = 0;
+	c->qid.vers = 0;
+	c->dev = 0;
+	c->offset = 0;
+	return c;
+}
+
+int
+lfdfd(int fd)
+{
+	return newfd(lfdchan(fd));
+}
+
+static Chan*
+lfdattach(char *x)
+{
+	USED(x);
+	
+	error(Egreg);
+	return nil;
+}
+
+static Walkqid*
+lfdwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	USED(c);
+	USED(nc);
+	USED(name);
+	USED(nname);
+	
+	error(Egreg);
+	return nil;
+}
+
+static int
+lfdstat(Chan *c, uchar *dp, int n)
+{
+	USED(c);
+	USED(dp);
+	USED(n);
+	error(Egreg);
+	return -1;
+}
+
+static Chan*
+lfdopen(Chan *c, int omode)
+{
+	USED(c);
+	USED(omode);
+	
+	error(Egreg);
+	return nil;
+}
+
+static void
+lfdclose(Chan *c)
+{
+	close((int)c->aux);
+}
+
+static long
+lfdread(Chan *c, void *buf, long n, vlong off)
+{
+	USED(off);	/* can't pread on pipes */
+	n = read((int)c->aux, buf, n);
+	if(n < 0){
+		iprint("error %d\n", errno);
+		oserror();
+	}
+	return n;
+}
+
+static long
+lfdwrite(Chan *c, void *buf, long n, vlong off)
+{
+	USED(off);	/* can't pread on pipes */
+
+	n = write((int)c->aux, buf, n);
+	if(n < 0){
+		iprint("error %d\n", errno);
+		oserror();
+	}
+	return n;
+}
+
+Dev lfddevtab = {
+	'L',
+	"lfd",
+	
+	devreset,
+	devinit,
+	devshutdown,
+	lfdattach,
+	lfdwalk,
+	lfdstat,
+	lfdopen,
+	devcreate,
+	lfdclose,
+	lfdread,
+	devbread,
+	lfdwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- a/kern/devroot.c
+++ b/kern/devroot.c
@@ -8,9 +8,12 @@
 {
 	Qdir = 0,
 	Qboot = 0x1000,
+	Qmnt = 0x2000,
+	Qfactotum,
 
 	Nrootfiles = 32,
 	Nbootfiles = 32,
+	Nmntfiles = 2,
 };
 
 typedef struct Dirlist Dirlist;
@@ -26,6 +29,7 @@
 static Dirtab rootdir[Nrootfiles] = {
 	"#/",		{Qdir, 0, QTDIR},	0,		DMDIR|0555,
 	"boot",	{Qboot, 0, QTDIR},	0,		DMDIR|0555,
+	"mnt",	{Qmnt, 0, QTDIR},	0,		DMDIR|0555,
 };
 static uchar *rootdata[Nrootfiles];
 static Dirlist rootlist = 
@@ -33,7 +37,7 @@
 	0,
 	rootdir,
 	rootdata,
-	2,
+	3,
 	Nrootfiles
 };
 
@@ -50,6 +54,19 @@
 	Nbootfiles
 };
 
+static Dirtab mntdir[Nmntfiles] = {
+	"mnt",	{Qmnt, 0, QTDIR},	0,		DMDIR|0555,
+	"factotum",	{Qfactotum, 0, QTDIR},	0,	DMDIR|0555,
+};
+static Dirlist mntlist =
+{
+	Qmnt,
+	mntdir,
+	nil,
+	2,
+	Nmntfiles
+};
+
 /*
  *  add a file to the list
  */
@@ -97,7 +114,6 @@
 	addrootdir("dev");
 	addrootdir("env");
 	addrootdir("fd");
-	addrootdir("mnt");
 	addrootdir("net");
 	addrootdir("net.alt");
 	addrootdir("proc");
@@ -129,6 +145,13 @@
 			return 1;
 		}
 		return devgen(c, name, rootlist.dir, rootlist.ndir, s, dp);
+	case Qmnt:
+		if(s == DEVDOTDOT){
+			Qid tqiddir = {Qdir, 0, QTDIR};
+			devdir(c, tqiddir, "#/", 0, eve, 0555, dp);
+			return 1;
+		}
+		return devgen(c, name, mntlist.dir, mntlist.ndir, s, dp);
 	case Qboot:
 		if(s == DEVDOTDOT){
 			Qid tqiddir = {Qdir, 0, QTDIR};
@@ -139,22 +162,25 @@
 	default:
 		if(s == DEVDOTDOT){
 			Qid tqiddir = {Qdir, 0, QTDIR};
-			if((int)c->qid.path < Qboot)
-				devdir(c, tqiddir, "#/", 0, eve, 0555, dp);
-			else {
-				tqiddir.path = Qboot;
-				devdir(c, tqiddir, "#/", 0, eve, 0555, dp);
-			}
+			tqiddir.path = c->qid.path&0xF000;
+			devdir(c, tqiddir, "#/", 0, eve, 0555, dp);
 			return 1;
 		}
 		if(s != 0)
 			return -1;
-		if((int)c->qid.path < Qboot){
+		switch((int)c->qid.path & 0xF000){
+		case Qdir:
 			t = c->qid.path-1;
 			l = &rootlist;
-		}else{
+			break;
+		case Qboot:
 			t = c->qid.path - Qboot - 1;
 			l = &bootlist;
+			break;
+		case Qmnt:
+			t = c->qid.path - Qmnt - 1;
+			l = &mntlist;
+			break;
 		}
 		if(t >= l->ndir)
 			return -1;
@@ -209,17 +235,19 @@
 	switch(t){
 	case Qdir:
 	case Qboot:
+	case Qmnt:
 		return devdirread(c, buf, n, nil, 0, rootgen);
 	}
 
-	if(t<Qboot)
-		l = &rootlist;
-	else{
-		t -= Qboot;
+	if(t&Qboot)
 		l = &bootlist;
-	}
-
+	else if(t&Qmnt)
+		l = &mntlist;
+	else
+		l = &bootlist;
+	t &= 0xFFF;
 	t--;
+
 	if(t >= l->ndir)
 		error(Egreg);
 
--- a/kern/devssl.c
+++ b/kern/devssl.c
@@ -765,6 +765,7 @@
 	int offset;
 
 	if(waserror()){
+iprint("error: %s\n", up->errstr);
 		if(b != nil)
 			free(b);
 		nexterror();
@@ -1071,8 +1072,9 @@
 			nexterror();
 		}
 		qlock(&s->out.q);
-
 		p = a;
+if(0) iprint("write %d %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n",
+	n, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
 		e = p + n;
 		do {
 			m = e - p;
@@ -1092,7 +1094,9 @@
 
 			p += m;
 		} while(p < e);
-
+		p = a;
+if(0) iprint("wrote %d %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n",
+	n, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
 		poperror();
 		qunlock(&s->out.q);
 		return n;
--- a/kern/devtab.c
+++ b/kern/devtab.c
@@ -12,6 +12,8 @@
 extern Dev drawdevtab;
 extern Dev ipdevtab;
 extern Dev fsdevtab;
+extern Dev mntdevtab;
+extern Dev lfddevtab;
 
 Dev *devtab[] = {
 	&rootdevtab,
@@ -22,6 +24,8 @@
 	&drawdevtab,
 	&ipdevtab,
 	&fsdevtab,
+	&mntdevtab,
+	&lfddevtab,
 	0
 };
 
--- /dev/null
+++ b/libauth/Makefile
@@ -1,0 +1,21 @@
+ROOT=..
+include ../Make.config
+
+LIB=libauth.a
+OFILES=\
+	attr.$O\
+	auth_attr.$O\
+	auth_challenge.$O\
+	auth_getuserpasswd.$O\
+	auth_proxy.$O\
+	auth_respond.$O\
+	auth_rpc.$O\
+	auth_userpasswd.$O\
+
+$(LIB): $(OFILES)
+	$(AR) r $(LIB) $(OFILES)
+	$(RANLIB) $(LIB)
+
+%.$O: %.c
+	$(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/libauth/attr.c
@@ -1,0 +1,174 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+int
+_attrfmt(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;
+		switch(a->type){
+		case AttrQuery:
+			b = seprint(b, ebuf, " %q?", a->name);
+			break;
+		case AttrNameval:
+			b = seprint(b, ebuf, " %q=%q", a->name, a->val);
+			break;
+		case AttrDefault:
+			b = seprint(b, ebuf, " %q:=%q", a->name, a->val);
+			break;
+		}
+	}
+	return fmtstrcpy(fmt, buf+1);
+}
+
+Attr*
+_copyattr(Attr *a)
+{
+	Attr **la, *na;
+
+	na = nil;
+	la = &na;
+	for(; a; a=a->next){
+		*la = _mkattr(a->type, a->name, a->val, nil);
+		setmalloctag(*la, getcallerpc(&a));
+		la = &(*la)->next;
+	}
+	*la = nil;
+	return na;
+}
+
+Attr*
+_delattr(Attr *a, char *name)
+{
+	Attr *fa;
+	Attr **la;
+
+	for(la=&a; *la; ){
+		if(strcmp((*la)->name, name) == 0){
+			fa = *la;
+			*la = (*la)->next;
+			fa->next = nil;
+			_freeattr(fa);
+		}else
+			la=&(*la)->next;
+	}
+	return a;
+}
+
+Attr*
+_findattr(Attr *a, char *n)
+{
+	for(; a; a=a->next)
+		if(strcmp(a->name, n) == 0 && a->type != AttrQuery)
+			return a;
+	return nil;
+}
+
+void
+_freeattr(Attr *a)
+{
+	Attr *anext;
+
+	for(; a; a=anext){
+		anext = a->next;
+		free(a->name);
+		free(a->val);
+		a->name = (void*)~0;
+		a->val = (void*)~0;
+		a->next = (void*)~0;
+		free(a);
+	}
+}
+
+Attr*
+_mkattr(int type, char *name, char *val, Attr *next)
+{
+	Attr *a;
+
+	a = malloc(sizeof(*a));
+	if(a==nil)
+		sysfatal("_mkattr malloc: %r");
+	a->type = type;
+	a->name = strdup(name);
+	a->val = strdup(val);
+	if(a->name==nil || a->val==nil)
+		sysfatal("_mkattr malloc: %r");
+	a->next = next;
+	setmalloctag(a, getcallerpc(&type));
+	return a;
+}
+
+static Attr*
+cleanattr(Attr *a)
+{
+	Attr *fa;
+	Attr **la;
+
+	for(la=&a; *la; ){
+		if((*la)->type==AttrQuery && _findattr(a, (*la)->name)){
+			fa = *la;
+			*la = (*la)->next;
+			fa->next = nil;
+			_freeattr(fa);
+		}else
+			la=&(*la)->next;
+	}
+	return a;
+}
+
+Attr*
+_parseattr(char *s)
+{
+	char *p, *t, *tok[256];
+	int i, ntok, type;
+	Attr *a;
+
+	s = strdup(s);
+	if(s == nil)
+		sysfatal("_parseattr strdup: %r");
+
+	ntok = tokenize(s, tok, nelem(tok));
+	a = nil;
+	for(i=ntok-1; i>=0; i--){
+		t = tok[i];
+		if(p = strchr(t, '=')){
+			*p++ = '\0';
+		//	if(p-2 >= t && p[-2] == ':'){
+		//		p[-2] = '\0';
+		//		type = AttrDefault;
+		//	}else
+				type = AttrNameval;
+			a = _mkattr(type, t, p, a);
+			setmalloctag(a, getcallerpc(&s));
+		}
+		else if(t[strlen(t)-1] == '?'){
+			t[strlen(t)-1] = '\0';
+			a = _mkattr(AttrQuery, t, "", a);
+			setmalloctag(a, getcallerpc(&s));
+		}else{
+			/* really a syntax error, but better to provide some indication */
+			a = _mkattr(AttrNameval, t, "", a);
+			setmalloctag(a, getcallerpc(&s));
+		}
+	}
+	free(s);
+	return cleanattr(a);
+}
+
+char*
+_strfindattr(Attr *a, char *n)
+{
+	a = _findattr(a, n);
+	if(a == nil)
+		return nil;
+	return a->val;
+}
+
--- /dev/null
+++ b/libauth/auth_attr.c
@@ -1,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <authsrv.h>
+#include "authlocal.h"
+
+Attr*
+auth_attr(AuthRpc *rpc)
+{
+	if(auth_rpc(rpc, "attr", nil, 0) != ARok)
+		return nil;
+	return _parseattr(rpc->arg);
+}
--- /dev/null
+++ b/libauth/auth_challenge.c
@@ -1,0 +1,117 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <authsrv.h>
+#include "authlocal.h"
+
+Chalstate*
+auth_challenge(char *fmt, ...)
+{
+	char *p;
+	va_list arg;
+	Chalstate *c;
+
+	quotefmtinstall();	/* just in case */
+	va_start(arg, fmt);
+	p = vsmprint(fmt, arg);
+	va_end(arg);
+	if(p == nil)
+		return nil;
+
+	c = mallocz(sizeof(*c), 1);
+	if(c == nil){
+		free(p);
+		return nil;
+	}
+
+	if((c->afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
+	Error:
+		auth_freechal(c);
+		free(p);
+		return nil;
+	}
+
+	if((c->rpc=auth_allocrpc(c->afd)) == nil
+	|| auth_rpc(c->rpc, "start", p, strlen(p)) != ARok
+	|| auth_rpc(c->rpc, "read", nil, 0) != ARok)
+		goto Error;
+
+	if(c->rpc->narg > sizeof(c->chal)-1){
+		werrstr("buffer too small for challenge");
+		goto Error;
+	}
+	memmove(c->chal, c->rpc->arg, c->rpc->narg);
+	c->nchal = c->rpc->narg;
+	free(p);
+	return c;
+}
+
+AuthInfo*
+auth_response(Chalstate *c)
+{
+	int ret;
+	AuthInfo *ai;
+
+	ai = nil;
+	if(c->afd < 0){
+		werrstr("auth_response: connection not open");
+		return nil;
+	}
+	if(c->resp == nil){
+		werrstr("auth_response: nil response");
+		return nil;
+	}
+	if(c->nresp == 0){
+		werrstr("auth_response: unspecified response length");
+		return nil;
+	}
+
+	if(c->user){
+		if(auth_rpc(c->rpc, "write", c->user, strlen(c->user)) != ARok){
+			/*
+			 * if this fails we're out of phase with factotum.
+			 * give up.
+			 */
+			goto Out;
+		}
+	}
+
+	if(auth_rpc(c->rpc, "write", c->resp, c->nresp) != ARok){
+		/*
+		 * don't close the connection -- maybe we'll try again.
+		 */
+		return nil;
+	}
+
+	switch(ret = auth_rpc(c->rpc, "read", nil, 0)){
+	case ARok:
+	default:
+		werrstr("factotum protocol botch %d %s", ret, c->rpc->ibuf);
+		break;
+	case ARdone:
+		ai = auth_getinfo(c->rpc);
+		break;
+	}
+
+Out:
+	close(c->afd);
+	auth_freerpc(c->rpc);
+	c->afd = -1;
+	c->rpc = nil;
+	return ai;
+}
+
+void
+auth_freechal(Chalstate *c)
+{
+	if(c == nil)
+		return;
+
+	if(c->afd >= 0)
+		close(c->afd);
+	if(c->rpc != nil)
+		auth_freerpc(c->rpc);
+
+	memset(c, 0xBB, sizeof(*c));
+	free(c);
+}
--- /dev/null
+++ b/libauth/auth_getuserpasswd.c
@@ -1,0 +1,75 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include "authlocal.h"
+
+enum {
+	ARgiveup = 100,
+};
+
+static int
+dorpc(AuthRpc *rpc, char *verb, char *val, int len, AuthGetkey *getkey)
+{
+	int ret;
+
+	for(;;){
+		if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey)
+			return ret;
+		if(getkey == nil)
+			return ARgiveup;	/* don't know how */
+		if((*getkey)(rpc->arg) < 0)
+			return ARgiveup;	/* user punted */
+	}
+}
+
+UserPasswd*
+auth_getuserpasswd(AuthGetkey *getkey, char *fmt, ...)
+{
+	AuthRpc *rpc;
+	char *f[3], *p, *params;
+	int fd;
+	va_list arg;
+	UserPasswd *up;
+
+	up = nil;
+	rpc = nil;
+	params = nil;
+
+	fd = open("/mnt/factotum/rpc", ORDWR);
+	if(fd < 0)
+		goto out;
+	rpc = auth_allocrpc(fd);
+	if(rpc == nil)
+		goto out;
+	quotefmtinstall();	/* just in case */
+	va_start(arg, fmt);
+	params = vsmprint(fmt, arg);
+	va_end(arg);
+	if(params == nil)
+		goto out;
+
+	if(dorpc(rpc, "start", params, strlen(params), getkey) != ARok
+	|| dorpc(rpc, "read", nil, 0, getkey) != ARok)
+		goto out;
+
+	rpc->arg[rpc->narg] = '\0';
+	if(tokenize(rpc->arg, f, 2) != 2){
+		werrstr("bad answer from factotum");
+		goto out;
+	}
+	up = malloc(sizeof(*up)+rpc->narg+1);
+	if(up == nil)
+		goto out;
+	p = (char*)&up[1];
+	strcpy(p, f[0]);
+	up->user = p;
+	p += strlen(p)+1;
+	strcpy(p, f[1]);
+	up->passwd = p;
+
+out:
+	free(params);
+	auth_freerpc(rpc);
+	close(fd);
+	return up;
+}
--- /dev/null
+++ b/libauth/auth_proxy.c
@@ -1,0 +1,212 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <auth.h>
+#include "authlocal.h"
+
+enum { 
+	ARgiveup = 100,
+};
+
+static uchar*
+gstring(uchar *p, uchar *ep, char **s)
+{
+	uint n;
+
+	if(p == nil)
+		return nil;
+	if(p+BIT16SZ > ep)
+		return nil;
+	n = GBIT16(p);
+	p += BIT16SZ;
+	if(p+n > ep)
+		return nil;
+	*s = malloc(n+1);
+	memmove((*s), p, n);
+	(*s)[n] = '\0';
+	p += n;
+	return p;
+}
+
+static uchar*
+gcarray(uchar *p, uchar *ep, uchar **s, int *np)
+{
+	uint n;
+
+	if(p == nil)
+		return nil;
+	if(p+BIT16SZ > ep)
+		return nil;
+	n = GBIT16(p);
+	p += BIT16SZ;
+	if(p+n > ep)
+		return nil;
+	*s = malloc(n);
+	if(*s == nil)
+		return nil;
+	memmove((*s), p, n);
+	*np = n;
+	p += n;
+	return p;
+}
+
+void
+auth_freeAI(AuthInfo *ai)
+{
+	if(ai == nil)
+		return;
+	free(ai->cuid);
+	free(ai->suid);
+	free(ai->cap);
+	free(ai->secret);
+	free(ai);
+}
+
+static uchar*
+convM2AI(uchar *p, int n, AuthInfo **aip)
+{
+	uchar *e = p+n;
+	AuthInfo *ai;
+
+	ai = mallocz(sizeof(*ai), 1);
+	if(ai == nil)
+		return nil;
+
+	p = gstring(p, e, &ai->cuid);
+	p = gstring(p, e, &ai->suid);
+	p = gstring(p, e, &ai->cap);
+	p = gcarray(p, e, &ai->secret, &ai->nsecret);
+	if(p == nil)
+		auth_freeAI(ai);
+	else
+		*aip = ai;
+	return p;
+}
+
+AuthInfo*
+auth_getinfo(AuthRpc *rpc)
+{
+	AuthInfo *a;
+
+	if(auth_rpc(rpc, "authinfo", nil, 0) != ARok)
+		return nil;
+	if(convM2AI((uchar*)rpc->arg, rpc->narg, &a) == nil){
+		werrstr("bad auth info from factotum");
+		return nil;
+	}
+	return a;
+}
+
+static int
+dorpc(AuthRpc *rpc, char *verb, char *val, int len, AuthGetkey *getkey)
+{
+	int ret;
+
+	for(;;){
+		if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey)
+			return ret;
+		if(getkey == nil)
+			return ARgiveup;	/* don't know how */
+		if((*getkey)(rpc->arg) < 0)
+			return ARgiveup;	/* user punted */
+	}
+}
+
+/*
+ *  this just proxies what the factotum tells it to.
+ */
+AuthInfo*
+fauth_proxy(int fd, AuthRpc *rpc, AuthGetkey *getkey, char *params)
+{
+	char *buf;
+	int m, n, ret;
+	AuthInfo *a;
+	char oerr[ERRMAX];
+
+	rerrstr(oerr, sizeof oerr);
+	werrstr("UNKNOWN AUTH ERROR");
+
+	if(dorpc(rpc, "start", params, strlen(params), getkey) != ARok){
+		werrstr("fauth_proxy start: %r");
+		return nil;
+	}
+
+	buf = malloc(AuthRpcMax);
+	if(buf == nil)
+		return nil;
+	for(;;){
+		switch(dorpc(rpc, "read", nil, 0, getkey)){
+		case ARdone:
+			free(buf);
+			a = auth_getinfo(rpc);
+			errstr(oerr, sizeof oerr);	/* no error, restore whatever was there */
+			return a;
+		case ARok:
+			if(write(fd, rpc->arg, rpc->narg) != rpc->narg){
+				werrstr("auth_proxy write fd: %r");
+				goto Error;
+			}
+			break;
+		case ARphase:
+			n = 0;
+			memset(buf, 0, AuthRpcMax);
+			while((ret = dorpc(rpc, "write", buf, n, getkey)) == ARtoosmall){
+				if(atoi(rpc->arg) > AuthRpcMax)
+					break;
+				m = read(fd, buf+n, atoi(rpc->arg)-n);
+				if(m <= 0){
+					if(m == 0)
+						werrstr("auth_proxy short read: %s", buf);
+					goto Error;
+				}
+				n += m;
+			}
+			if(ret != ARok){
+				werrstr("auth_proxy rpc write: %s: %r", buf);
+				goto Error;
+			}
+			break;
+		default:
+			werrstr("auth_proxy rpc: %r");
+			goto Error;
+		}
+	}
+Error:
+	free(buf);
+	return nil;
+}
+
+AuthInfo*
+auth_proxy(int fd, AuthGetkey *getkey, char *fmt, ...)
+{
+	int afd;
+	char *p;
+	va_list arg;
+	AuthInfo *ai;
+	AuthRpc *rpc;
+
+	quotefmtinstall();	/* just in case */
+	va_start(arg, fmt);
+	p = vsmprint(fmt, arg);
+	va_end(arg);
+
+	afd = open("/mnt/factotum/rpc", ORDWR);
+	if(afd < 0){
+		werrstr("opening /mnt/factotum/rpc: %r");
+		free(p);
+		return nil;
+	}
+
+	rpc = auth_allocrpc(afd);
+	if(rpc == nil){
+		free(p);
+		return nil;
+	}
+
+	ai = fauth_proxy(fd, rpc, getkey, p);
+	free(p);
+	auth_freerpc(rpc);
+	close(afd);
+	return ai;
+}
+
--- /dev/null
+++ b/libauth/auth_respond.c
@@ -1,0 +1,73 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <authsrv.h>
+#include "authlocal.h"
+
+enum {
+	ARgiveup = 100,
+};
+
+static int
+dorpc(AuthRpc *rpc, char *verb, char *val, int len, AuthGetkey *getkey)
+{
+	int ret;
+
+	for(;;){
+		if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey)
+			return ret;
+		if(getkey == nil)
+			return ARgiveup;	/* don't know how */
+		if((*getkey)(rpc->arg) < 0)
+			return ARgiveup;	/* user punted */
+	}
+}
+
+int
+auth_respond(void *chal, uint nchal, char *user, uint nuser, void *resp, uint nresp, AuthGetkey *getkey, char *fmt, ...)
+{
+	char *p, *s;
+	va_list arg;
+	int afd;
+	AuthRpc *rpc;
+	Attr *a;
+
+	if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
+		return -1;
+	
+	if((rpc = auth_allocrpc(afd)) == nil){
+		close(afd);
+		return -1;
+	}
+
+	quotefmtinstall();	/* just in case */
+	va_start(arg, fmt);
+	p = vsmprint(fmt, arg);
+	va_end(arg);
+
+	if(p==nil
+	|| dorpc(rpc, "start", p, strlen(p), getkey) != ARok
+	|| dorpc(rpc, "write", chal, nchal, getkey) != ARok
+	|| dorpc(rpc, "read", nil, 0, getkey) != ARok){
+		free(p);
+		close(afd);
+		auth_freerpc(rpc);
+		return -1;
+	}
+	free(p);
+
+	if(rpc->narg < nresp)
+		nresp = rpc->narg;
+	memmove(resp, rpc->arg, nresp);
+
+	if((a = auth_attr(rpc)) != nil
+	&& (s = _strfindattr(a, "user")) != nil && strlen(s) < nuser)
+		strcpy(user, s);
+	else if(nuser > 0)
+		user[0] = '\0';
+
+	_freeattr(a);
+	close(afd);
+	auth_freerpc(rpc);
+	return nresp;	
+}
--- /dev/null
+++ b/libauth/auth_rpc.c
@@ -1,0 +1,116 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include "authlocal.h"
+
+static struct {
+	char *verb;
+	int val;
+} tab[] = {
+	"ok",			ARok,
+	"done",		ARdone,
+	"error",		ARerror,
+	"needkey",	ARneedkey,
+	"badkey",		ARbadkey,
+	"phase",		ARphase,
+	"toosmall",	ARtoosmall,
+	"error",		ARerror,
+};
+
+static int
+classify(char *buf, uint n, AuthRpc *rpc)
+{
+	int i, len;
+
+	for(i=0; i<nelem(tab); i++){
+		len = strlen(tab[i].verb);
+		if(n >= len && memcmp(buf, tab[i].verb, len) == 0 && (n==len || buf[len]==' ')){
+			if(n==len){
+				rpc->narg = 0;
+				rpc->arg = "";
+			}else{
+				rpc->narg = n - (len+1);
+				rpc->arg = (char*)buf+len+1;
+			}
+			return tab[i].val;
+		}
+	}
+	werrstr("malformed rpc response: %s", buf);
+	return ARrpcfailure;
+}
+
+AuthRpc*
+auth_allocrpc(int afd)
+{
+	AuthRpc *rpc;
+
+	rpc = mallocz(sizeof(*rpc), 1);
+	if(rpc == nil)
+		return nil;
+	rpc->afd = afd;
+	return rpc;
+}
+
+void
+auth_freerpc(AuthRpc *rpc)
+{
+	free(rpc);
+}
+
+uint
+auth_rpc(AuthRpc *rpc, char *verb, void *a, int na)
+{
+	int l, n, type;
+	char *f[4];
+
+	l = strlen(verb);
+	if(na+l+1 > AuthRpcMax){
+		werrstr("rpc too big");
+		return ARtoobig;
+	}
+
+	memmove(rpc->obuf, verb, l);
+	rpc->obuf[l] = ' ';
+	memmove(rpc->obuf+l+1, a, na);
+	if((n=write(rpc->afd, rpc->obuf, l+1+na)) != l+1+na){
+		if(n >= 0)
+			werrstr("auth_rpc short write");
+		return ARrpcfailure;
+	}
+
+	if((n=read(rpc->afd, rpc->ibuf, AuthRpcMax)) < 0)
+		return ARrpcfailure;
+	rpc->ibuf[n] = '\0';
+
+	/*
+	 * Set error string for good default behavior.
+	 */
+	switch(type = classify(rpc->ibuf, n, rpc)){
+	default:
+		werrstr("unknown rpc type %d (bug in auth_rpc.c)", type);
+		break;
+	case ARok:
+		break;
+	case ARrpcfailure:
+		break;
+	case ARerror:
+		if(rpc->narg == 0)
+			werrstr("unspecified rpc error");
+		else
+			werrstr("%s", rpc->arg);
+		break;
+	case ARneedkey:
+		werrstr("needkey %s", rpc->arg);
+		break;
+	case ARbadkey:
+		if(getfields(rpc->arg, f, nelem(f), 0, "\n") < 2)
+			werrstr("badkey %s", rpc->arg);
+		else
+			werrstr("badkey %s", f[1]);
+		break;
+	case ARphase:
+		werrstr("phase error %s", rpc->arg);
+		break;
+	}
+	return type;
+}
--- /dev/null
+++ b/libauth/auth_userpasswd.c
@@ -1,0 +1,50 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <authsrv.h>
+#include "authlocal.h"
+
+/*
+ * compute the proper response.  We encrypt the ascii of
+ * challenge number, with trailing binary zero fill.
+ * This process was derived empirically.
+ * this was copied from inet's guard.
+ */
+static void
+netresp(char *key, long chal, char *answer)
+{
+	uchar buf[8];
+
+	memset(buf, 0, 8);
+	sprint((char *)buf, "%lud", chal);
+	if(encrypt(key, buf, 8) < 0)
+		abort();
+	chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
+	sprint(answer, "%.8lux", chal);
+}
+
+AuthInfo*
+auth_userpasswd(char *user, char *passwd)
+{
+	char key[DESKEYLEN], resp[16];
+	AuthInfo *ai;
+	Chalstate *ch;
+
+	/*
+	 * Probably we should have a factotum protocol
+	 * to check a raw password.  For now, we use
+	 * p9cr, which is simplest to speak.
+	 */
+	if((ch = auth_challenge("user=%q proto=p9cr role=server", user)) == nil)
+		return nil;
+
+	passtokey(key, passwd);
+	netresp(key, atol(ch->chal), resp);
+	memset(key, 0, sizeof key);
+
+	ch->resp = resp;
+	ch->nresp = strlen(resp);
+	ai = auth_response(ch);
+	auth_freechal(ch);
+	return ai;
+}
--- /dev/null
+++ b/libauth/authlocal.h
@@ -1,0 +1,1 @@
+extern AuthInfo*	_fauth_proxy(int fd, AuthRpc *rpc, AuthGetkey *getkey, char *params);
--- /dev/null
+++ b/libauth/httpauth.c
@@ -1,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <authsrv.h>
+
+/* deprecated.
+	This is the mechanism that put entries in /sys/lib/httpd.rewrite
+	and passwords on the authserver in /sys/lib/httppasswords, which
+	was awkward to administer.  Instead, use local .httplogin files,
+	which are implemented in sys/src/cmd/ip/httpd/authorize.c */
+
+int
+httpauth(char *name, char *password)
+{
+	int afd;
+	Ticketreq tr;
+	Ticket	t;
+	char key[DESKEYLEN];
+	char buf[512];
+
+	afd = authdial(nil, nil);
+	if(afd < 0)
+		return -1;
+
+	/* send ticket request to AS */
+	memset(&tr, 0, sizeof(tr));
+	strcpy(tr.uid, name);
+	tr.type = AuthHttp;
+	convTR2M(&tr, buf);
+	if(write(afd, buf, TICKREQLEN) != TICKREQLEN){
+		close(afd);
+		return -1;
+	}
+	if(_asrdresp(afd, buf, TICKETLEN) < 0){
+		close(afd);
+		return -1;
+	}
+	close(afd);
+
+	/*
+	 *  use password and try to decrypt the
+	 *  ticket.  If it doesn't work we've got a bad password,
+	 *  give up.
+	 */
+	passtokey(key, password);
+	convM2T(buf, &t, key);
+	if(t.num != AuthHr || strcmp(t.cuid, tr.uid))
+		return -1;
+
+	return 0;
+}
--- a/main.c
+++ b/main.c
@@ -36,6 +36,8 @@
 	char buf[1024], *s;
 	int n;
 
+	eve = getuser();
+
 	sizebug();
 	fmtinstall('r', errfmt);
 
--- /dev/null
+++ b/posix-factotum.c
@@ -1,0 +1,107 @@
+#include <u.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <pwd.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <authsrv.h>
+#include <libsec.h>
+#include "drawterm.h"
+
+#undef socket
+#undef connect
+#undef getenv
+#undef access
+
+char*
+getuser(void)
+{
+	static char user[64];
+	struct passwd *pw;
+
+	pw = getpwuid(getuid());
+	if(pw == nil)
+		return "none";
+	strecpy(user, user+sizeof user, pw->pw_name);
+	return user;
+}
+/*
+ * Absent other hints, it works reasonably well to use
+ * the X11 display name as the name space identifier.
+ * This is how sam's B has worked since the early days.
+ * Since most programs using name spaces are also using X,
+ * this still seems reasonable.  Terminal-only sessions
+ * can set $NAMESPACE.
+ */
+static char*
+nsfromdisplay(void)
+{
+	int fd;
+	Dir *d;
+	char *disp, *p;
+
+	if((disp = getenv("DISPLAY")) == nil){
+		werrstr("$DISPLAY not set");
+		return nil;
+	}
+
+	/* canonicalize: xxx:0.0 => xxx:0 */
+	p = strrchr(disp, ':');
+	if(p){
+		p++;
+		while(isdigit((uchar)*p))
+			p++;
+		if(strcmp(p, ".0") == 0)
+			*p = 0;
+	}
+
+	return smprint("/tmp/ns.%s.%s", getuser(), disp);
+}
+
+char*
+getns(void)
+{
+	char *ns;
+
+	ns = getenv("NAMESPACE");
+	if(ns == nil)
+		ns = nsfromdisplay();
+	if(ns == nil){
+		werrstr("$NAMESPACE not set, %r");
+		return nil;
+	}
+	return ns;
+}
+
+int
+dialfactotum(void)
+{
+	int fd;
+	struct sockaddr_un su;
+	char *name;
+	
+	name = smprint("%s/factotum", getns());
+
+	if(name == nil || access(name, 0) < 0)
+		return -1;
+	memset(&su, 0, sizeof su);
+	su.sun_family = AF_UNIX;
+	if(strlen(name)+1 > sizeof su.sun_path){
+		werrstr("socket name too long");
+		return -1;
+	}
+	strcpy(su.sun_path, name);
+	if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
+		werrstr("socket: %r");
+		return -1;
+	}
+	if(connect(fd, (struct sockaddr*)&su, sizeof su) < 0){
+		werrstr("connect %s: %r", name);
+		close(fd);
+		return -1;
+	}
+
+	return lfdfd(fd);
+}
+
--- /dev/null
+++ b/win32-factotum.c
@@ -1,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <authsrv.h>
+#include <libsec.h>
+#include "drawterm.h"
+
+#undef getenv
+
+char*
+getuser(void)
+{
+	return getenv("USER");
+}
+
+int
+dialfactotum(void)
+{
+	return -1;
+}
+