capability: run scripts/PLAN9 on capability device
[akaros.git] / kern / drivers / dev / capability.c
1 /*
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 #include <vfs.h>
11 #include <kfs.h>
12 #include <slab.h>
13 #include <kmalloc.h>
14 #include <kref.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <error.h>
19 #include <cpio.h>
20 #include <pmap.h>
21 #include <smp.h>
22 #include <ip.h>
23
24 #include <vfs.h>
25 #include <kfs.h>
26 #include <slab.h>
27 #include <kmalloc.h>
28 #include <kref.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <assert.h>
32 #include <error.h>
33 #include <cpio.h>
34 #include <pmap.h>
35 #include <smp.h>
36 #include <ip.h>
37
38 enum {
39         Hashlen = SHA1dlen,
40         Maxhash = 256,
41 };
42
43 /*
44  *  if a process knows cap->cap, it can change user
45  *  to capabilty->user.
46  */
47 typedef struct Caphash Caphash;
48 struct Caphash {
49         Caphash *next;
50         char hash[Hashlen];
51 };
52
53 struct {
54         qlock_t qlock_t qlock;
55         Caphash *first;
56         int nhash;
57 } capalloc;
58
59 enum {
60         Qdir,
61         Qhash,
62         Quse,
63 };
64
65 /* caphash must be last */
66 struct dirtab capdir[] = {
67     ".",       {Qdir, 0, QTDIR}, 0, DMDIR | 0500, "capuse", {Quse}, 0, 0222,
68     "caphash", {Qhash},          0, 0200,
69 };
70 int ncapdir = ARRAY_SIZE(capdir);
71
72 static struct chan *capattach(char *spec)
73 {
74         return devattach(L'¤', spec);
75 }
76
77 static struct walkqid *capwalk(struct chan *c, struct chan *nc, char **name,
78                         int nname)
79 {
80         return devwalk(c, nc, name, nname, capdir, ncapdir, devgen);
81 }
82
83 static void capremove(struct chan *c)
84 {
85         if (iseve() && c->qid.path == Qhash)
86                 ncapdir = ARRAY_SIZE(capdir) - 1;
87         else
88                 error(Eperm);
89 }
90
91 static int32_t capstat(struct chan *c, uint8_t *db, int32_t n)
92 {
93         return devstat(c, db, n, capdir, ncapdir, devgen);
94 }
95
96 /*
97  *  if the stream doesn't exist, create it
98  */
99 static struct chan *capopen(struct chan *c, int omode)
100 {
101         if (c->qid.type & QTDIR) {
102                 if (omode != OREAD)
103                         error(Ebadarg);
104                 c->mode = omode;
105                 c->flag |= COPEN;
106                 c->offset = 0;
107                 return c;
108         }
109
110         switch ((uint32_t)c->qid.path) {
111         case Qhash:
112                 if (!iseve())
113                         error(Eperm);
114                 break;
115         }
116
117         c->mode = openmode(omode);
118         c->flag |= COPEN;
119         c->offset = 0;
120         return c;
121 }
122
123 /*
124 static char*
125 hashstr(uint8_t *hash)
126 {
127     static char buf[2*Hashlen+1];
128     int i;
129
130     for(i = 0; i < Hashlen; i++)
131         sprint(buf+2*i, "%2.2x", hash[i]);
132     buf[2*Hashlen] = 0;
133     return buf;
134 }
135  */
136
137 static Caphash *remcap(uint8_t *hash)
138 {
139         Caphash *t, **l;
140
141         qlock(&(&capalloc.QLock)->qlock);
142
143         /* find the matching capability */
144         for (l = &capalloc.first; *l != NULL;) {
145                 t = *l;
146                 if (memcmp(hash, t->hash, Hashlen) == 0)
147                         break;
148                 l = &t->next;
149         }
150         t = *l;
151         if (t != NULL) {
152                 capalloc.nhash--;
153                 *l = t->next;
154         }
155         qunlock(&(&capalloc.QLock)->qlock);
156
157         return t;
158 }
159
160 /* add a capability, throwing out any old ones */
161 static void addcap(uint8_t *hash)
162 {
163         Caphash *p, *t, **l;
164
165         p = kzmalloc(sizeof(*p), 0);
166         memmove(p->hash, hash, Hashlen);
167         p->next = NULL;
168
169         qlock(&(&capalloc.QLock)->qlock);
170
171         /* trim extras */
172         while (capalloc.nhash >= Maxhash) {
173                 t = capalloc.first;
174                 if (t == NULL)
175                         panic("addcap");
176                 capalloc.first = t->next;
177                 kfree(t);
178                 capalloc.nhash--;
179         }
180
181         /* add new one */
182         for (l = &capalloc.first; *l != NULL; l = &(*l)->next)
183                 ;
184         *l = p;
185         capalloc.nhash++;
186
187         qunlock(&(&capalloc.QLock)->qlock);
188 }
189
190 static void capclose(struct chan *c)
191 {
192 }
193
194 static int32_t capread(struct chan *c, void *va, int32_t n, int64_t m)
195 {
196         switch ((uint32_t)c->qid.path) {
197         case Qdir:
198                 return devdirread(c, va, n, capdir, ncapdir, devgen);
199
200         default:
201                 error(Eperm);
202                 break;
203         }
204         return n;
205 }
206
207 static int32_t capwrite(struct chan *c, void *va, int32_t n, int64_t m)
208 {
209         Caphash *p;
210         char *cp;
211         uint8_t hash[Hashlen];
212         char *key, *from, *to;
213         char err[256];
214         struct proc *up = externup();
215
216         switch ((uint32_t)c->qid.path) {
217         case Qhash:
218                 if (!iseve())
219                         error(Eperm);
220                 if (n < Hashlen)
221                         error(Eshort);
222                 memmove(hash, va, Hashlen);
223                 addcap(hash);
224                 break;
225
226         case Quse:
227                 /* copy key to avoid a fault in hmac_xx */
228                 cp = NULL;
229                 if (waserror()) {
230                         kfree(cp);
231                         nexterror();
232                 }
233                 cp = kzmalloc(n + 1, 0);
234                 memmove(cp, va, n);
235                 cp[n] = 0;
236
237                 from = cp;
238                 key = strrchr(cp, '@');
239                 if (key == NULL)
240                         error(Eshort);
241                 *key++ = 0;
242
243                 hmac_sha1((uint8_t *)from, strlen(from), (uint8_t *)key, strlen(key),
244                           hash, NULL);
245
246                 p = remcap(hash);
247                 if (p == NULL) {
248                         snprintf(err, sizeof(err), "invalid capability %s@%s", from, key);
249                         error(err);
250                 }
251
252                 /* if a from user is supplied, make sure it matches */
253                 to = strchr(from, '@');
254                 if (to == NULL) {
255                         to = from;
256                 } else {
257                         *to++ = 0;
258                         if (strcmp(from, up->user) != 0)
259                                 error("capability must match user");
260                 }
261
262                 /* set user id */
263                 kstrdup(&up->user, to);
264                 up->basepri = PriNormal;
265
266                 kfree(p);
267                 kfree(cp);
268                 poperror();
269                 break;
270
271         default:
272                 error(Eperm);
273                 break;
274         }
275
276         return n;
277 }
278
279 struct dev capdevtab = {.dc = L'¤',
280                  .name = "cap",
281
282                  .reset = devreset,
283                  .init = devinit,
284                  .shutdown = devshutdown,
285                  .attach = capattach,
286                  .walk = capwalk,
287                  .stat = capstat,
288                  .open = capopen,
289                  .create = devcreate,
290                  .close = capclose,
291                  .read = capread,
292                  .bread = devbread,
293                  .write = capwrite,
294                  .bwrite = devbwrite,
295                  .remove = capremove,
296                  .wstat = devwstat};