commit cef0d2f8a4aa3181901589321d0b289890d923a2
parent 31e59616e044a57c3829a225239160a5bd1dcba1
Author: Michael Teichgräber <mt4swm@googlemail.com>
Date:   Sun, 27 Dec 2009 09:48:22 -0800
To make it possible to log into 9vx using drawterm a
device #¤ (devcap) is needed, otherwise auth_chuid()
will fail, because it cannot open #¤/capuse.
This patch adds `9/port/devcap.c' from Plan 9 to
9vx/a, with only a few adaptions (see a/devcap.ed).
R=rsc_swtch, rsc
CC=codebot
http://codereview.appspot.com/151042
Committer: Russ Cox <rsc@swtch.com>
Diffstat:
4 files changed, 316 insertions(+), 0 deletions(-)
diff --git a/src/9vx/Makefrag b/src/9vx/Makefrag
@@ -66,6 +66,7 @@ PLAN9_A_OBJS = \
 		convS2M.o \
 		convM2S.o \
 		dev.o \
+		devcap.o \
 		devcons.o \
 		devdraw.o \
 		devdup.o \
diff --git a/src/9vx/a/devcap.c b/src/9vx/a/devcap.c
@@ -0,0 +1,286 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+
+#include "libsec.h"
+
+enum
+{
+	Hashlen=	SHA1dlen,
+	Maxhash=	256,
+};
+
+/*
+ *  if a process knows cap->cap, it can change user
+ *  to capabilty->user.
+ */
+typedef struct Caphash	Caphash;
+struct Caphash
+{
+	Caphash	*next;
+	char		hash[Hashlen];
+	ulong		ticks;
+};
+
+struct
+{
+	QLock l;
+	Caphash	*first;
+	int	nhash;
+} capalloc;
+
+enum
+{
+	Qdir,
+	Qhash,
+	Quse,
+};
+
+/* caphash must be last */
+Dirtab capdir[] =
+{
+	".",		{Qdir,0,QTDIR},	0,		DMDIR|0500,
+	"capuse",	{Quse},		0,		0222,
+	"caphash",	{Qhash},	0,		0200,
+};
+int ncapdir = nelem(capdir);
+
+static Chan*
+capattach(char *spec)
+{
+	return devattach(L'¤', spec);
+}
+
+static Walkqid*
+capwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, capdir, ncapdir, devgen);
+}
+
+static void
+capremove(Chan *c)
+{
+	if(iseve() && c->qid.path == Qhash)
+		ncapdir = nelem(capdir)-1;
+	else
+		error(Eperm);
+}
+
+
+static int
+capstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, capdir, ncapdir, devgen);
+}
+
+/*
+ *  if the stream doesn't exist, create it
+ */
+static Chan*
+capopen(Chan *c, int omode)
+{
+	if(c->qid.type & QTDIR){
+		if(omode != OREAD)
+			error(Ebadarg);
+		c->mode = omode;
+		c->flag |= COPEN;
+		c->offset = 0;
+		return c;
+	}
+
+	switch((ulong)c->qid.path){
+	case Qhash:
+		if(!iseve())
+			error(Eperm);
+		break;
+	}
+
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+/*
+static char*
+hashstr(uchar *hash)
+{
+	static char buf[2*Hashlen+1];
+	int i;
+
+	for(i = 0; i < Hashlen; i++)
+		sprint(buf+2*i, "%2.2ux", hash[i]);
+	buf[2*Hashlen] = 0;
+	return buf;
+}
+ */
+
+static Caphash*
+remcap(uchar *hash)
+{
+	Caphash *t, **l;
+
+	qlock(&capalloc.l);
+
+	/* find the matching capability */
+	for(l = &capalloc.first; *l != nil;){
+		t = *l;
+		if(memcmp(hash, t->hash, Hashlen) == 0)
+			break;
+		l = &t->next;
+	}
+	t = *l;
+	if(t != nil){
+		capalloc.nhash--;
+		*l = t->next;
+	}
+	qunlock(&capalloc.l);
+
+	return t;
+}
+
+/* add a capability, throwing out any old ones */
+static void
+addcap(uchar *hash)
+{
+	Caphash *p, *t, **l;
+
+	p = smalloc(sizeof *p);
+	memmove(p->hash, hash, Hashlen);
+	p->next = nil;
+	p->ticks = msec();
+
+	qlock(&capalloc.l);
+
+	/* trim extras */
+	while(capalloc.nhash >= Maxhash){
+		t = capalloc.first;
+		if(t == nil)
+			panic("addcap");
+		capalloc.first = t->next;
+		free(t);
+		capalloc.nhash--;
+	}
+
+	/* add new one */
+	for(l = &capalloc.first; *l != nil; l = &(*l)->next)
+		;
+	*l = p;
+	capalloc.nhash++;
+
+	qunlock(&capalloc.l);
+}
+
+static void
+capclose(Chan *c)
+{
+}
+
+static long
+capread(Chan *c, void *va, long n, vlong vl)
+{
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, va, n, capdir, ncapdir, devgen);
+
+	default:
+		error(Eperm);
+		break;
+	}
+	return n;
+}
+
+static long
+capwrite(Chan *c, void *va, long n, vlong vl)
+{
+	Caphash *p;
+	char *cp;
+	uchar hash[Hashlen];
+	char *key, *from, *to;
+	char err[256];
+
+	switch((ulong)c->qid.path){
+	case Qhash:
+		if(!iseve())
+			error(Eperm);
+		if(n < Hashlen)
+			error(Eshort);
+		memmove(hash, va, Hashlen);
+		addcap(hash);
+		break;
+
+	case Quse:
+		/* copy key to avoid a fault in hmac_xx */
+		cp = nil;
+		if(waserror()){
+			free(cp);
+			nexterror();
+		}
+		cp = smalloc(n+1);
+		memmove(cp, va, n);
+		cp[n] = 0;
+
+		from = cp;
+		key = strrchr(cp, '@');
+		if(key == nil)
+			error(Eshort);
+		*key++ = 0;
+
+		hmac_sha1((uchar*)from, strlen(from), (uchar*)key, strlen(key), hash, nil);
+
+		p = remcap(hash);
+		if(p == nil){
+			snprint(err, sizeof err, "invalid capability %s@%s", from, key);
+			error(err);
+		}
+
+		/* if a from user is supplied, make sure it matches */
+		to = strchr(from, '@');
+		if(to == nil){
+			to = from;
+		} else {
+			*to++ = 0;
+			if(strcmp(from, up->user) != 0)
+				error("capability must match user");
+		}
+
+		/* set user id */
+		kstrdup(&up->user, to);
+		up->basepri = PriNormal;
+
+		free(p);
+		free(cp);
+		poperror();
+		break;
+
+	default:
+		error(Eperm);
+		break;
+	}
+
+	return n;
+}
+
+Dev capdevtab = {
+	L'¤',
+	"cap",
+
+	devreset,
+	devinit,
+	devshutdown,
+	capattach,
+	capwalk,
+	capstat,
+	capopen,
+	devcreate,
+	capclose,
+	capread,
+	devbread,
+	capwrite,
+	devbwrite,
+	capremove,
+	devwstat
+};
diff --git a/src/9vx/a/devcap.ed b/src/9vx/a/devcap.ed
@@ -0,0 +1,27 @@
+197c
+capwrite(Chan *c, void *va, long n, vlong vl)
+.
+183c
+capread(Chan *c, void *va, long n, vlong vl)
+.
+178c
+capclose(Chan *c)
+.
+174c
+	qunlock(&capalloc.l);
+.
+156c
+	qlock(&capalloc.l);
+.
+154c
+	p->ticks = msec();
+.
+140c
+	qunlock(&capalloc.l);
+.
+126c
+	qlock(&capalloc.l);
+.
+30c
+	QLock l;
+.
diff --git a/src/9vx/devtab.c b/src/9vx/devtab.c
@@ -22,6 +22,7 @@ extern Dev procdevtab;
 extern Dev mntloopdevtab;
 extern Dev dupdevtab;
 extern Dev sddevtab;
+extern Dev capdevtab;
 
 Dev *devtab[] = {
 	&rootdevtab,	/* must be first */
@@ -41,6 +42,7 @@ Dev *devtab[] = {
 	&ssldevtab,
 	&tlsdevtab,
 	&sddevtab,
+	&capdevtab,
 	0
 };