| /* | 
 |  * This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 | #include <net/ip.h> | 
 |  | 
 | #include <crypto/2crypto.h> | 
 | #include <crypto/2hmac.h> | 
 | #include <crypto/2id.h> | 
 | #include <crypto/2sha.h> | 
 |  | 
 | #include <ctype.h> | 
 |  | 
 | enum { | 
 | 	Hashlen = VB2_MAX_DIGEST_SIZE * 2, | 
 | 	Maxhash = 256, | 
 | }; | 
 |  | 
 | /* | 
 |  *  if a process knows cap->cap, it can change user | 
 |  *  to capabilty->user. | 
 |  */ | 
 | struct Caphash { | 
 | 	struct Caphash *next; | 
 | 	char hash[Hashlen + 1]; | 
 | }; | 
 |  | 
 | struct { | 
 | 	qlock_t qlock; | 
 | 	struct Caphash *first; | 
 | 	int nhash; | 
 | } capalloc; | 
 |  | 
 | enum { | 
 | 	Qdir, | 
 | 	Qhash, | 
 | 	Quse, | 
 | }; | 
 |  | 
 | /* caphash must be last */ | 
 | struct dirtab capdir[] = { | 
 | 	{".",       {Qdir, 0, QTDIR}, 0, DMDIR | 0500}, | 
 | 	{"capuse",  {Quse},           0, 0222,}, | 
 | 	{"caphash", {Qhash},          0, 0200,}, | 
 | }; | 
 | int ncapdir = ARRAY_SIZE(capdir); | 
 |  | 
 | static void capinit(void) | 
 | { | 
 | 	qlock_init(&capalloc.qlock); | 
 | } | 
 |  | 
 | static struct chan *capattach(char *spec) | 
 | { | 
 | 	return devattach("capability", spec); | 
 | } | 
 |  | 
 | static struct walkqid *capwalk(struct chan *c, struct chan *nc, char **name, | 
 |                                unsigned int nname) | 
 | { | 
 | 	return devwalk(c, nc, name, nname, capdir, ncapdir, devgen); | 
 | } | 
 |  | 
 | static void capremove(struct chan *c) | 
 | { | 
 | 	if (iseve() && c->qid.path == Qhash) | 
 | 		ncapdir = ARRAY_SIZE(capdir) - 1; | 
 | 	else | 
 | 		error(EPERM, "Permission denied"); | 
 | } | 
 |  | 
 | static size_t capstat(struct chan *c, uint8_t *db, size_t n) | 
 | { | 
 | 	return devstat(c, db, n, capdir, ncapdir, devgen); | 
 | } | 
 |  | 
 | /* | 
 |  *  if the stream doesn't exist, create it | 
 |  */ | 
 | static struct chan *capopen(struct chan *c, int omode) | 
 | { | 
 | 	if (c->qid.type & QTDIR) { | 
 | 		if (openmode(omode) != O_READ) | 
 | 			error(EISDIR, "Can only read a directory"); | 
 | 		c->mode = openmode(omode); | 
 | 		c->flag |= COPEN; | 
 | 		c->offset = 0; | 
 | 		return c; | 
 | 	} | 
 |  | 
 | 	switch ((uint32_t)c->qid.path) { | 
 | 	case Qhash: | 
 | 		if (!iseve()) | 
 | 			error(EPERM, | 
 | 			      "Permission denied: only eve can open Qhash"); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	c->mode = openmode(omode); | 
 | 	c->flag |= COPEN; | 
 | 	c->offset = 0; | 
 | 	return c; | 
 | } | 
 |  | 
 | static size_t __hashstr(char *buf, uint8_t *hash, size_t bytes_to_split) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < bytes_to_split; i++) | 
 | 		snprintf(buf + 2 * i, 3, "%02x", hash[i]); | 
 |  | 
 | 	return bytes_to_split; | 
 | } | 
 |  | 
 | static struct Caphash *remcap(uint8_t *hash) | 
 | { | 
 | 	struct Caphash *t, **l; | 
 |  | 
 | 	qlock(&capalloc.qlock); | 
 |  | 
 | 	/* find the matching capability */ | 
 | 	for (l = &capalloc.first; *l != NULL;) { | 
 | 		t = *l; | 
 | 		if (memcmp(hash, t->hash, Hashlen) == 0) | 
 | 			break; | 
 | 		l = &t->next; | 
 | 	} | 
 | 	t = *l; | 
 | 	if (t != NULL) { | 
 | 		capalloc.nhash--; | 
 | 		*l = t->next; | 
 | 	} | 
 | 	qunlock(&capalloc.qlock); | 
 |  | 
 | 	return t; | 
 | } | 
 |  | 
 | /* add a capability, throwing out any old ones */ | 
 | static void addcap(uint8_t *hash) | 
 | { | 
 | 	struct Caphash *p, *t, **l; | 
 |  | 
 | 	p = kzmalloc(sizeof(*p), MEM_WAIT); | 
 | 	memmove(p->hash, hash, Hashlen); | 
 | 	p->next = NULL; | 
 |  | 
 | 	qlock(&capalloc.qlock); | 
 |  | 
 | 	/* trim extras */ | 
 | 	while (capalloc.nhash >= Maxhash) { | 
 | 		t = capalloc.first; | 
 | 		if (t == NULL) | 
 | 			panic("addcap"); | 
 | 		capalloc.first = t->next; | 
 | 		kfree(t); | 
 | 		capalloc.nhash--; | 
 | 	} | 
 |  | 
 | 	/* add new one */ | 
 | 	for (l = &capalloc.first; *l != NULL; l = &(*l)->next) | 
 | 		; | 
 | 	*l = p; | 
 | 	capalloc.nhash++; | 
 |  | 
 | 	qunlock(&capalloc.qlock); | 
 | } | 
 |  | 
 | static void capclose(struct chan *c) | 
 | { | 
 | } | 
 |  | 
 | static size_t capread(struct chan *c, void *va, size_t n, off64_t m) | 
 | { | 
 | 	switch ((uint32_t)c->qid.path) { | 
 | 	case Qdir: | 
 | 		return devdirread(c, va, n, capdir, ncapdir, devgen); | 
 |  | 
 | 	default: | 
 | 		error(EPERM, "Permission denied: can't read capability files"); | 
 | 		break; | 
 | 	} | 
 | 	return n; | 
 | } | 
 |  | 
 | static size_t capwrite(struct chan *c, void *va, size_t n, off64_t m) | 
 | { | 
 | 	struct Caphash *p; | 
 | 	char *cp, *hashstr; | 
 | 	uint8_t hash[Hashlen + 1] = {0}; | 
 | 	char *key, *from, *to; | 
 | 	int ret; | 
 | 	ERRSTACK(1); | 
 |  | 
 | 	switch ((uint32_t)c->qid.path) { | 
 | 	case Qhash: | 
 | 		if (!iseve()) | 
 | 			error(EPERM, "permission denied: you must be eve"); | 
 | 		if (n < VB2_SHA256_DIGEST_SIZE * 2) | 
 | 			error(EIO, "Short read: on Qhash"); | 
 | 		memmove(hash, va, Hashlen); | 
 | 		for (int i = 0; i < Hashlen; i++) | 
 | 			hash[i] = tolower(hash[i]); | 
 | 		addcap(hash); | 
 | 		break; | 
 |  | 
 | 	case Quse: | 
 | 		/* copy key to avoid a fault in hmac_xx and so we can enforce | 
 | 		 * null termination. */ | 
 | 		cp = kzmalloc(n + 1, MEM_WAIT); | 
 | 		hashstr = kzmalloc(sizeof(hash), MEM_WAIT); | 
 | 		if (waserror()) { | 
 | 			kfree(cp); | 
 | 			kfree(hashstr); | 
 | 			nexterror(); | 
 | 		} | 
 | 		memmove(cp, va, n); | 
 | 		cp[n] = 0; | 
 |  | 
 | 		from = cp; | 
 | 		key = strrchr(cp, '@'); | 
 | 		if (key == NULL) | 
 | 			error(EIO, "short read: Quse"); | 
 | 		*key++ = 0; | 
 |  | 
 | 		ret = hmac(VB2_HASH_SHA256, key, strlen(key), | 
 | 		           from, strlen(from), hash, Hashlen); | 
 | 		if (ret) | 
 | 			error(EINVAL, "HMAC failed"); | 
 |  | 
 | 		// Convert to ASCII text | 
 | 		ret = __hashstr(hashstr, hash, VB2_SHA256_DIGEST_SIZE); | 
 | 		if (ret != VB2_SHA256_DIGEST_SIZE) | 
 | 			error(EINVAL, "hash is wrong length"); | 
 |  | 
 | 		p = remcap((uint8_t *)hashstr); | 
 | 		if (p == NULL) | 
 | 			error(EINVAL, "invalid capability %s@%s", from, key); | 
 | 		kfree(p); | 
 |  | 
 | 		/* if a from user is supplied, make sure it matches */ | 
 | 		to = strchr(from, '@'); | 
 | 		if (to == NULL) { | 
 | 			to = from; | 
 | 		} else { | 
 | 			*to++ = 0; | 
 | 			if (strcmp(from, current->user.name) != 0) | 
 | 				error(EINVAL, "capability must match user"); | 
 | 		} | 
 |  | 
 | 		/* set user id */ | 
 | 		// In the original user names were NULL-terminated; ensure | 
 | 		// that is still the case. | 
 | 		if (strlen(to) > sizeof(current->user.name) - 1) | 
 | 			error(EINVAL, "New user name is > %d bytes", | 
 | 			      sizeof(current->user.name) - 1); | 
 | 		proc_set_username(current, to); | 
 | 		//up->basepri = PriNormal; | 
 |  | 
 | 		kfree(cp); | 
 | 		kfree(hashstr); | 
 | 		poperror(); | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 		error(EPERM, "permission denied: capwrite"); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | struct dev capdevtab __devtab = { | 
 | 	.name = "capability", | 
 |  | 
 | 	.reset = devreset, | 
 | 	.init = capinit, | 
 | 	.shutdown = devshutdown, | 
 | 	.attach = capattach, | 
 | 	.walk = capwalk, | 
 | 	.stat = capstat, | 
 | 	.open = capopen, | 
 | 	.create = devcreate, | 
 | 	.close = capclose, | 
 | 	.read = capread, | 
 | 	.bread = devbread, | 
 | 	.write = capwrite, | 
 | 	.bwrite = devbwrite, | 
 | 	.remove = capremove, | 
 | 	.wstat = devwstat, | 
 | }; |