basically none of these build
diff --git a/src/libauth/amount.c b/src/libauth/amount.c
new file mode 100644
index 0000000..6565e37
--- /dev/null
+++ b/src/libauth/amount.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include "authlocal.h"
+
+int
+amount(int fd, char *mntpt, int flags, char *aname)
+{
+	int rv, afd;
+	AuthInfo *ai;
+
+	afd = fauth(fd, aname);
+	if(afd >= 0){
+		ai = auth_proxy(afd, amount_getkey, "proto=p9any role=client");
+		if(ai != nil)
+			auth_freeAI(ai);
+	}
+	rv = mount(fd, afd, mntpt, flags, aname);
+	if(afd >= 0)
+		close(afd);
+	return rv;
+}
diff --git a/src/libauth/amount_getkey.c b/src/libauth/amount_getkey.c
new file mode 100644
index 0000000..019fad2
--- /dev/null
+++ b/src/libauth/amount_getkey.c
@@ -0,0 +1,5 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+int (*amount_getkey)(char*) = auth_getkey;
diff --git a/src/libauth/attr.c b/src/libauth/attr.c
new file mode 100644
index 0000000..5f35750
--- /dev/null
+++ b/src/libauth/attr.c
@@ -0,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;
+}
+
diff --git a/src/libauth/auth_attr.c b/src/libauth/auth_attr.c
new file mode 100644
index 0000000..8842590
--- /dev/null
+++ b/src/libauth/auth_attr.c
@@ -0,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);
+}
diff --git a/src/libauth/auth_challenge.c b/src/libauth/auth_challenge.c
new file mode 100644
index 0000000..298f5f1
--- /dev/null
+++ b/src/libauth/auth_challenge.c
@@ -0,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);
+}
diff --git a/src/libauth/auth_chuid.c b/src/libauth/auth_chuid.c
new file mode 100644
index 0000000..1bfbfff
--- /dev/null
+++ b/src/libauth/auth_chuid.c
@@ -0,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+/*
+ *  become the authenticated user
+ */
+int
+auth_chuid(AuthInfo *ai, char *ns)
+{
+	int rv, fd;
+
+	if(ai == nil || ai->cap == nil){
+		werrstr("no capability");
+		return -1;
+	}
+
+	/* change uid */
+	fd = open("#¤/capuse", OWRITE);
+	if(fd < 0){
+		werrstr("opening #¤/capuse: %r");
+		return -1;
+	}
+	rv = write(fd, ai->cap, strlen(ai->cap));
+	close(fd);
+	if(rv < 0){
+		werrstr("writing %s to #¤/capuse: %r", ai->cap);
+		return -1;
+	}
+
+	/* get a link to factotum as new user */
+	fd = open("/srv/factotum", ORDWR);
+	if(fd >= 0)
+		mount(fd, -1, "/mnt", MREPL, "");
+
+	/* set up new namespace */
+	return newns(ai->cuid, ns);
+}
diff --git a/src/libauth/auth_getkey.c b/src/libauth/auth_getkey.c
new file mode 100644
index 0000000..0ae28b1
--- /dev/null
+++ b/src/libauth/auth_getkey.c
@@ -0,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+int
+auth_getkey(char *params)
+{
+	char *name;
+	Dir *d;
+	int pid;
+	Waitmsg *w;
+
+	/* start /factotum to query for a key */
+	name = "/factotum";
+	d = dirstat(name);
+	if(d == nil){
+		name = "/boot/factotum";
+		d = dirstat(name);
+	}
+	if(d == nil){
+		werrstr("auth_getkey: no /factotum or /boot/factotum: didn't get key %s", params);
+		return -1;
+	}
+if(0)	if(d->type != '/'){
+		werrstr("auth_getkey: /factotum may be bad: didn't get key %s", params);
+		return -1;
+	}
+	switch(pid = fork()){
+	case -1:
+		werrstr("can't fork for %s: %r", name);
+		return -1;
+	case 0:
+		execl(name, "getkey", "-g", params, nil);
+		exits(0);
+	default:
+		for(;;){
+			w = wait();
+			if(w == nil)
+				break;
+			if(w->pid == pid){
+				if(w->msg[0] != '\0'){
+					free(w);
+					return -1;
+				}
+				free(w);
+				return 0;
+			}
+		}
+	}
+	return 0;
+}
diff --git a/src/libauth/auth_getuserpasswd.c b/src/libauth/auth_getuserpasswd.c
new file mode 100644
index 0000000..4d66dce
--- /dev/null
+++ b/src/libauth/auth_getuserpasswd.c
@@ -0,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;
+}
diff --git a/src/libauth/auth_proxy.c b/src/libauth/auth_proxy.c
new file mode 100644
index 0000000..186031e
--- /dev/null
+++ b/src/libauth/auth_proxy.c
@@ -0,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;
+}
+
diff --git a/src/libauth/auth_respond.c b/src/libauth/auth_respond.c
new file mode 100644
index 0000000..910f06b
--- /dev/null
+++ b/src/libauth/auth_respond.c
@@ -0,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;	
+}
diff --git a/src/libauth/auth_rpc.c b/src/libauth/auth_rpc.c
new file mode 100644
index 0000000..4333a73
--- /dev/null
+++ b/src/libauth/auth_rpc.c
@@ -0,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;
+}
diff --git a/src/libauth/auth_userpasswd.c b/src/libauth/auth_userpasswd.c
new file mode 100644
index 0000000..34a38c2
--- /dev/null
+++ b/src/libauth/auth_userpasswd.c
@@ -0,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;
+}
diff --git a/src/libauth/auth_wep.c b/src/libauth/auth_wep.c
new file mode 100644
index 0000000..afde46b
--- /dev/null
+++ b/src/libauth/auth_wep.c
@@ -0,0 +1,50 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include "authlocal.h"
+
+/*
+ *  make factotum add wep keys to an 802.11 device
+ */
+int
+auth_wep(char *dev, char *fmt, ...)
+{
+	AuthRpc *rpc;
+	char *params, *p;
+	int fd;
+	va_list arg;
+	int rv;
+
+	rv = -1;
+
+	if(dev == nil){
+		werrstr("no device specified");
+		return rv;
+	}
+
+	fd = open("/mnt/factotum/rpc", ORDWR);
+	if(fd < 0)
+		return rv;
+
+	rpc = auth_allocrpc(fd);
+	if(rpc != nil){
+		quotefmtinstall();	/* just in case */
+		va_start(arg, fmt);
+		params = vsmprint(fmt, arg);
+		va_end(arg);
+		if(params != nil){
+			p = smprint("proto=wep %s", params);
+			if(p != nil){
+				if(auth_rpc(rpc, "start", p, strlen(p)) == ARok
+				&& auth_rpc(rpc, "write", dev, strlen(dev)) == ARok)
+					rv = 0;
+				free(p);
+			}
+			free(params);
+		}
+		auth_freerpc(rpc);
+	}
+	close(fd);
+		
+	return rv;
+}
diff --git a/src/libauth/authlocal.h b/src/libauth/authlocal.h
new file mode 100644
index 0000000..2c52bf8
--- /dev/null
+++ b/src/libauth/authlocal.h
@@ -0,0 +1 @@
+extern AuthInfo*	_fauth_proxy(int fd, AuthRpc *rpc, AuthGetkey *getkey, char *params);
diff --git a/src/libauth/httpauth.c b/src/libauth/httpauth.c
new file mode 100644
index 0000000..9d1b0d2
--- /dev/null
+++ b/src/libauth/httpauth.c
@@ -0,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;
+}
diff --git a/src/libauth/login.c b/src/libauth/login.c
new file mode 100644
index 0000000..8bbc7b1
--- /dev/null
+++ b/src/libauth/login.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+int
+login(char *user, char *password, char *namespace)
+{
+	int rv;
+	AuthInfo *ai;
+
+	if((ai = auth_userpasswd(user, password)) == nil)
+		return -1;
+
+	rv = auth_chuid(ai, namespace);
+	auth_freeAI(ai);
+	return rv;
+}
diff --git a/src/libauth/mkfile b/src/libauth/mkfile
new file mode 100644
index 0000000..647835b
--- /dev/null
+++ b/src/libauth/mkfile
@@ -0,0 +1,27 @@
+PLAN9=../..
+<$PLAN9/src/mkhdr
+
+LIB=libauth.a
+OFILES=\
+#	amount.$O\
+#	amount_getkey.$O\
+	attr.$O\
+#	auth_attr.$O\
+#	auth_challenge.$O\
+#	auth_chuid.$O\
+#	auth_getkey.$O\
+#	auth_getuserpasswd.$O\
+#	auth_proxy.$O\
+#	auth_respond.$O\
+#	auth_rpc.$O\
+#	auth_userpasswd.$O\
+#	auth_wep.$O\
+#	login.$O\
+#	newns.$O\
+#	noworld.$O\
+
+HFILES=\
+	$PLAN9/include/auth.h\
+	authlocal.h\
+
+<$PLAN9/src/mksyslib
diff --git a/src/libauth/newns.c b/src/libauth/newns.c
new file mode 100644
index 0000000..66f51d3
--- /dev/null
+++ b/src/libauth/newns.c
@@ -0,0 +1,322 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <auth.h>
+#include <authsrv.h>
+#include "authlocal.h"
+
+enum
+{
+	NARG	= 15,		/* max number of arguments */
+	MAXARG	= 10*ANAMELEN,	/* max length of an argument */
+};
+
+static int	setenv(char*, char*);
+static char	*expandarg(char*, char*);
+static int	splitargs(char*, char*[], char*, int);
+static int	nsfile(Biobuf *, AuthRpc *);
+static int	nsop(int, char*[], AuthRpc*);
+static int	callexport(char*, char*);
+static int	catch(void*, char*);
+
+static int
+buildns(int newns, char *user, char *file)
+{
+	Biobuf *b;
+	char home[4*ANAMELEN];
+	int afd;
+	AuthRpc *rpc;
+	int cdroot;
+	char *path;
+
+	rpc = nil;
+	/* try for factotum now because later is impossible */
+	afd = open("/mnt/factotum/rpc", ORDWR);
+	if(afd >= 0){
+		rpc = auth_allocrpc(afd);
+		if(rpc == nil){
+			close(afd);
+			afd = -1;
+		}
+	}
+	if(file == nil){
+		if(!newns){
+			werrstr("no namespace file specified");
+			return -1;
+		}
+		file = "/lib/namespace";
+	}
+	b = Bopen(file, OREAD);
+	if(b == 0){
+		werrstr("can't open %s: %r", file);
+		close(afd);
+		auth_freerpc(rpc);
+		return -1;
+	}
+	if(newns){
+		rfork(RFENVG|RFCNAMEG);
+		setenv("user", user);
+		snprint(home, 2*ANAMELEN, "/usr/%s", user);
+		setenv("home", home);
+	}
+	cdroot = nsfile(b, rpc);
+	Bterm(b);
+	if(rpc){
+		close(rpc->afd);
+		auth_freerpc(rpc);
+	}
+
+	/* make sure we managed to cd into the new name space */
+	if(newns && !cdroot){
+		path = malloc(1024);
+		if(path == nil || getwd(path, 1024) == 0 || chdir(path) < 0)
+			chdir("/");
+		if(path != nil)
+			free(path);
+	}
+
+	return 0;
+}
+
+static int
+nsfile(Biobuf *b, AuthRpc *rpc)
+{
+	int argc;
+	char *cmd, *argv[NARG+1], argbuf[MAXARG*NARG];
+	int cdroot = 0;
+
+	atnotify(catch, 1);
+	while(cmd = Brdline(b, '\n')){
+		cmd[Blinelen(b)-1] = '\0';
+		while(*cmd==' ' || *cmd=='\t')
+			cmd++;
+		if(*cmd == '#')
+			continue;
+		argc = splitargs(cmd, argv, argbuf, NARG);
+		if(argc)
+			cdroot |= nsop(argc, argv, rpc);
+	}
+	atnotify(catch, 0);
+	return cdroot;
+}
+
+int
+newns(char *user, char *file)
+{
+	return buildns(1, user, file);
+}
+
+int
+addns(char *user, char *file)
+{
+	return buildns(0, user, file);
+}
+
+static int
+famount(int fd, AuthRpc *rpc, char *mntpt, int flags, char *aname)
+{
+	int afd;
+	AuthInfo *ai;
+
+	afd = fauth(fd, aname);
+	if(afd >= 0){
+		ai = fauth_proxy(afd, rpc, amount_getkey, "proto=p9any role=client");
+		if(ai != nil)
+			auth_freeAI(ai);
+	}
+	return mount(fd, afd, mntpt, flags, aname);
+}
+
+static int
+nsop(int argc, char *argv[], AuthRpc *rpc)
+{
+	char *argv0;
+	ulong flags;
+	int fd;
+	Biobuf *b;
+	int cdroot = 0;
+
+	flags = 0;
+	argv0 = 0;
+	ARGBEGIN{
+	case 'a':
+		flags |= MAFTER;
+		break;
+	case 'b':
+		flags |= MBEFORE;
+		break;
+	case 'c':
+		flags |= MCREATE;
+		break;
+	case 'C':
+		flags |= MCACHE;
+		break;
+	}ARGEND
+
+	if(!(flags & (MAFTER|MBEFORE)))
+		flags |= MREPL;
+
+	if(strcmp(argv0, ".") == 0 && argc == 1){
+		b = Bopen(argv[0], OREAD);
+		if(b == nil)
+			return 0;
+		cdroot |= nsfile(b, rpc);
+		Bterm(b);
+	} else if(strcmp(argv0, "clear") == 0 && argc == 0)
+		rfork(RFCNAMEG);
+	else if(strcmp(argv0, "bind") == 0 && argc == 2)
+		bind(argv[0], argv[1], flags);
+	else if(strcmp(argv0, "unmount") == 0){
+		if(argc == 1)
+			unmount(nil, argv[0]);
+		else if(argc == 2)
+			unmount(argv[0], argv[1]);
+	} else if(strcmp(argv0, "mount") == 0){
+		fd = open(argv[0], ORDWR);
+		if(argc == 2)
+			famount(fd, rpc, argv[1], flags, "");
+		else if(argc == 3)
+			famount(fd, rpc, argv[1], flags, argv[2]);
+		close(fd);
+	} else if(strcmp(argv0, "import") == 0){
+		fd = callexport(argv[0], argv[1]);
+		if(argc == 2)
+			famount(fd, rpc, argv[1], flags, "");
+		else if(argc == 3)
+			famount(fd, rpc, argv[2], flags, "");
+		close(fd);
+	} else if(strcmp(argv0, "cd") == 0 && argc == 1)
+		if(chdir(argv[0]) == 0 && *argv[0] == '/')
+			cdroot = 1;
+	return cdroot;
+}
+
+static char *wocp = "sys: write on closed pipe";
+
+static int
+catch(void *x, char *m)
+{
+	USED(x);
+	return strncmp(m, wocp, strlen(wocp)) == 0;
+}
+
+static int
+callexport(char *sys, char *tree)
+{
+	char *na, buf[3];
+	int fd;
+	AuthInfo *ai;
+
+	na = netmkaddr(sys, 0, "exportfs");
+	if((fd = dial(na, 0, 0, 0)) < 0)
+		return -1;
+	if((ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client")) == nil
+	|| write(fd, tree, strlen(tree)) < 0
+	|| read(fd, buf, 3) != 2 || buf[0]!='O' || buf[1]!= 'K'){
+		close(fd);
+		auth_freeAI(ai);
+		return -1;
+	}
+	auth_freeAI(ai);
+	return fd;
+}
+
+static int
+splitargs(char *p, char *argv[], char *argbuf, int nargv)
+{
+	char *q;
+	int i, n;
+
+	n = gettokens(p, argv, nargv, " \t'\r");
+	if(n == nargv)
+		return 0;
+	for(i = 0; i < n; i++){
+		q = argv[i];
+		argv[i] = argbuf;
+		argbuf = expandarg(q, argbuf);
+		if(!argbuf)
+			return 0;
+	}
+	return n;
+}
+
+/*
+ * copy the arg into the buffer,
+ * expanding any environment variables.
+ * environment variables are assumed to be
+ * names (ie. < ANAMELEN long)
+ * the entire argument is expanded to be at
+ * most MAXARG long and null terminated
+ * the address of the byte after the terminating null is returned
+ * any problems cause a 0 return;
+ */
+static char *
+expandarg(char *arg, char *buf)
+{
+	char env[3+ANAMELEN], *p, *q, *x;
+	int fd, n, len;
+
+	n = 0;
+	while(p = utfrune(arg, L'$')){
+		len = p - arg;
+		if(n + len + ANAMELEN >= MAXARG-1)
+			return 0;
+		memmove(&buf[n], arg, len);
+		n += len;
+		p++;
+		arg = utfrune(p, L'\0');
+		q = utfrune(p, L'/');
+		if(q && q < arg)
+			arg = q;
+		q = utfrune(p, L'.');
+		if(q && q < arg)
+			arg = q;
+		q = utfrune(p, L'$');
+		if(q && q < arg)
+			arg = q;
+		len = arg - p;
+		if(len >= ANAMELEN)
+			continue;
+		strcpy(env, "#e/");
+		strncpy(env+3, p, len);
+		env[3+len] = '\0';
+		fd = open(env, OREAD);
+		if(fd >= 0){
+			len = read(fd, &buf[n], ANAMELEN - 1);
+			/* some singleton environment variables have trailing NULs */
+			/* lists separate entries with NULs; we arbitrarily take the first element */
+			if(len > 0){
+				x = memchr(&buf[n], 0, len);
+				if(x != nil)
+					len = x - &buf[n];
+				n += len;
+			}
+			close(fd);
+		}
+	}
+	len = strlen(arg);
+	if(n + len >= MAXARG - 1)
+		return 0;
+	strcpy(&buf[n], arg);
+	return &buf[n+len+1];
+}
+
+static int
+setenv(char *name, char *val)
+{
+	int f;
+	char ename[ANAMELEN+6];
+	long s;
+
+	sprint(ename, "#e/%s", name);
+	f = create(ename, OWRITE, 0664);
+	if(f < 0)
+		return -1;
+	s = strlen(val);
+	if(write(f, val, s) != s){
+		close(f);
+		return -1;
+	}
+	close(f);
+	return 0;
+}
diff --git a/src/libauth/noworld.c b/src/libauth/noworld.c
new file mode 100644
index 0000000..c61b146
--- /dev/null
+++ b/src/libauth/noworld.c
@@ -0,0 +1,45 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <auth.h>
+
+/*
+ *  see if user is in the group noworld, i.e., has all file
+ *  priviledges masked with 770, and all directories with
+ *  771, before checking access rights
+ */
+int
+noworld(char *user)
+{
+	Biobuf *b;
+	char *p;
+	int n;
+
+	b = Bopen("/adm/users", OREAD);
+	if(b == nil)
+		return 0;
+	while((p = Brdline(b, '\n')) != nil){
+		p[Blinelen(b)-1] = 0;
+		p = strchr(p, ':');
+		if(p == nil)
+			continue;
+		if(strncmp(p, ":noworld:", 9) == 0){
+			p += 9;
+			break;
+		}
+	}
+	n = strlen(user);
+	while(p != nil && *p != 0){
+		p = strstr(p, user);
+		if(p == nil)
+			break;
+		if(*(p-1) == ':' || *(p-1) == ',')
+		if(*(p+n) == ':' || *(p+n) == ',' || *(p+n) == 0){
+			Bterm(b);
+			return 1;
+		}
+		p++;
+	}
+	Bterm(b);
+	return 0;
+}