xref: /plan9/sys/src/9/port/devcap.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 #include	"netif.h"
9 #include	<libsec.h>
10 
11 enum
12 {
13 	Hashlen=	SHA1dlen,
14 	Maxhash=	256,
15 };
16 
17 /*
18  *  if a process knows cap->cap, it can change user
19  *  to capabilty->user.
20  */
21 typedef struct Caphash	Caphash;
22 struct Caphash
23 {
24 	Caphash	*next;
25 	char		hash[Hashlen];
26 	ulong		ticks;
27 };
28 
29 struct
30 {
31 	QLock;
32 	Caphash	*first;
33 	int	nhash;
34 } capalloc;
35 
36 enum
37 {
38 	Qdir,
39 	Qhash,
40 	Quse,
41 };
42 
43 /* caphash must be last */
44 Dirtab capdir[] =
45 {
46 	".",		{Qdir,0,QTDIR},	0,		DMDIR|0500,
47 	"capuse",	{Quse},		0,		0222,
48 	"caphash",	{Qhash},	0,		0200,
49 };
50 int ncapdir = nelem(capdir);
51 
52 static Chan*
53 capattach(char *spec)
54 {
55 	return devattach(L'¤', spec);
56 }
57 
58 static Walkqid*
59 capwalk(Chan *c, Chan *nc, char **name, int nname)
60 {
61 	return devwalk(c, nc, name, nname, capdir, ncapdir, devgen);
62 }
63 
64 static void
65 capremove(Chan *c)
66 {
67 	if(iseve() && c->qid.path == Qhash)
68 		ncapdir = nelem(capdir)-1;
69 	else
70 		error(Eperm);
71 }
72 
73 
74 static int
75 capstat(Chan *c, uchar *db, int n)
76 {
77 	return devstat(c, db, n, capdir, ncapdir, devgen);
78 }
79 
80 /*
81  *  if the stream doesn't exist, create it
82  */
83 static Chan*
84 capopen(Chan *c, int omode)
85 {
86 	if(c->qid.type & QTDIR){
87 		if(omode != OREAD)
88 			error(Ebadarg);
89 		c->mode = omode;
90 		c->flag |= COPEN;
91 		c->offset = 0;
92 		return c;
93 	}
94 
95 	switch((ulong)c->qid.path){
96 	case Qhash:
97 		if(!iseve())
98 			error(Eperm);
99 		break;
100 	}
101 
102 	c->mode = openmode(omode);
103 	c->flag |= COPEN;
104 	c->offset = 0;
105 	return c;
106 }
107 
108 static char*
109 hashstr(uchar *hash)
110 {
111 	static char buf[256];
112 	int i;
113 
114 	for(i = 0; i < Hashlen; i++)
115 		sprint(buf+2*i, "%2.2ux", hash[i]);
116 	buf[2*Hashlen] = 0;
117 	return buf;
118 }
119 
120 static Caphash*
121 remcap(uchar *hash)
122 {
123 	Caphash *t, **l;
124 
125 	qlock(&capalloc);
126 
127 	/* find the matching capability */
128 	for(l = &capalloc.first; *l != nil;){
129 		t = *l;
130 		if(memcmp(hash, t->hash, Hashlen) == 0)
131 			break;
132 		l = &t->next;
133 	}
134 	t = *l;
135 	if(t != nil){
136 		capalloc.nhash--;
137 		*l = t->next;
138 	}
139 	qunlock(&capalloc);
140 
141 	return t;
142 }
143 
144 /* add a capability, throwing out any old ones */
145 static void
146 addcap(uchar *hash)
147 {
148 	Caphash *p, *t, **l;
149 
150 	p = smalloc(sizeof *p);
151 	memmove(p->hash, hash, Hashlen);
152 	p->next = nil;
153 	p->ticks = m->ticks;
154 
155 	qlock(&capalloc);
156 
157 	/* trim extras */
158 	while(capalloc.nhash >= Maxhash){
159 		t = capalloc.first;
160 		if(t == nil)
161 			panic("addcap");
162 		capalloc.first = t->next;
163 		free(t);
164 		capalloc.nhash--;
165 	}
166 
167 	/* add new one */
168 	for(l = &capalloc.first; *l != nil; l = &(*l)->next)
169 		;
170 	*l = p;
171 	capalloc.nhash++;
172 
173 	qunlock(&capalloc);
174 }
175 
176 static void
177 capclose(Chan*)
178 {
179 }
180 
181 static long
182 capread(Chan *c, void *va, long n, vlong)
183 {
184 	switch((ulong)c->qid.path){
185 	case Qdir:
186 		return devdirread(c, va, n, capdir, ncapdir, devgen);
187 
188 	default:
189 		error(Eperm);
190 		break;
191 	}
192 	return n;
193 }
194 
195 static long
196 capwrite(Chan *c, void *va, long n, vlong)
197 {
198 	Caphash *p;
199 	char *cp;
200 	uchar hash[Hashlen];
201 	char *key, *from, *to;
202 	char err[256];
203 
204 	switch((ulong)c->qid.path){
205 	case Qhash:
206 		if(n < Hashlen)
207 			error(Eshort);
208 		memmove(hash, va, Hashlen);
209 		addcap(hash);
210 		break;
211 
212 	case Quse:
213 		/* copy key to avoid a fault in hmac_xx */
214 		cp = nil;
215 		if(waserror()){
216 			free(cp);
217 			nexterror();
218 		}
219 		cp = smalloc(n+1);
220 		memmove(cp, va, n);
221 		cp[n] = 0;
222 
223 		from = cp;
224 		key = strrchr(cp, '@');
225 		if(key == nil)
226 			error(Eshort);
227 		*key++ = 0;
228 
229 		hmac_sha1((uchar*)from, strlen(from), (uchar*)key, strlen(key), hash, nil);
230 
231 		p = remcap(hash);
232 		if(p == nil){
233 			snprint(err, sizeof err, "invalid capability %s@%s", from, key);
234 			error(err);
235 		}
236 
237 		/* if a from user is supplied, make sure it matches */
238 		to = strchr(from, '@');
239 		if(to == nil){
240 			to = from;
241 		} else {
242 			*to++ = 0;
243 			if(strcmp(from, up->user) != 0)
244 				error("capability must match user");
245 		}
246 
247 		/* set user id */
248 		kstrdup(&up->user, to);
249 		up->basepri = PriNormal;
250 
251 		free(p);
252 		free(cp);
253 		poperror();
254 		break;
255 
256 	default:
257 		error(Eperm);
258 		break;
259 	}
260 
261 	return n;
262 }
263 
264 Dev capdevtab = {
265 	L'¤',
266 	"cap",
267 
268 	devreset,
269 	devinit,
270 	devshutdown,
271 	capattach,
272 	capwalk,
273 	capstat,
274 	capopen,
275 	devcreate,
276 	capclose,
277 	capread,
278 	devbread,
279 	capwrite,
280 	devbwrite,
281 	capremove,
282 	devwstat
283 };
284