vfs: Remove vfs.h
[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 <slab.h>
11 #include <kmalloc.h>
12 #include <kref.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <assert.h>
16 #include <error.h>
17 #include <cpio.h>
18 #include <pmap.h>
19 #include <smp.h>
20 #include <net/ip.h>
21
22 #include <crypto/2crypto.h>
23 #include <crypto/2hmac.h>
24 #include <crypto/2id.h>
25 #include <crypto/2sha.h>
26
27 #include <ctype.h>
28
29 enum {
30         Hashlen = VB2_MAX_DIGEST_SIZE * 2,
31         Maxhash = 256,
32 };
33
34 /*
35  *  if a process knows cap->cap, it can change user
36  *  to capabilty->user.
37  */
38 struct Caphash {
39         struct Caphash *next;
40         char hash[Hashlen + 1];
41 };
42
43 struct {
44         qlock_t qlock;
45         struct Caphash *first;
46         int nhash;
47 } capalloc;
48
49 enum {
50         Qdir,
51         Qhash,
52         Quse,
53 };
54
55 /* caphash must be last */
56 struct dirtab capdir[] = {
57         {".",       {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
58         {"capuse",  {Quse},           0, 0222,},
59         {"caphash", {Qhash},          0, 0200,},
60 };
61 int ncapdir = ARRAY_SIZE(capdir);
62
63 static void capinit(void)
64 {
65         qlock_init(&capalloc.qlock);
66 }
67
68 static struct chan *capattach(char *spec)
69 {
70         return devattach("capability", spec);
71 }
72
73 static struct walkqid *capwalk(struct chan *c, struct chan *nc, char **name,
74                                unsigned int nname)
75 {
76         return devwalk(c, nc, name, nname, capdir, ncapdir, devgen);
77 }
78
79 static void capremove(struct chan *c)
80 {
81         if (iseve() && c->qid.path == Qhash)
82                 ncapdir = ARRAY_SIZE(capdir) - 1;
83         else
84                 error(EPERM, "Permission denied");
85 }
86
87 static size_t capstat(struct chan *c, uint8_t *db, size_t n)
88 {
89         return devstat(c, db, n, capdir, ncapdir, devgen);
90 }
91
92 /*
93  *  if the stream doesn't exist, create it
94  */
95 static struct chan *capopen(struct chan *c, int omode)
96 {
97         if (c->qid.type & QTDIR) {
98                 if (openmode(omode) != O_READ)
99                         error(EISDIR, "Can only read a directory");
100                 c->mode = openmode(omode);
101                 c->flag |= COPEN;
102                 c->offset = 0;
103                 return c;
104         }
105
106         switch ((uint32_t)c->qid.path) {
107         case Qhash:
108                 if (!iseve())
109                         error(EPERM, "Permission denied: only eve can open Qhash");
110                 break;
111         }
112
113         c->mode = openmode(omode);
114         c->flag |= COPEN;
115         c->offset = 0;
116         return c;
117 }
118
119 static size_t __hashstr(char *buf, uint8_t *hash, size_t bytes_to_split)
120 {
121         int i;
122
123         for (i = 0; i < bytes_to_split; i++)
124                 snprintf(buf + 2 * i, 3, "%02x", hash[i]);
125
126         return bytes_to_split;
127 }
128
129 static struct Caphash *remcap(uint8_t *hash)
130 {
131         struct Caphash *t, **l;
132
133         qlock(&capalloc.qlock);
134
135         /* find the matching capability */
136         for (l = &capalloc.first; *l != NULL;) {
137                 t = *l;
138                 if (memcmp(hash, t->hash, Hashlen) == 0)
139                         break;
140                 l = &t->next;
141         }
142         t = *l;
143         if (t != NULL) {
144                 capalloc.nhash--;
145                 *l = t->next;
146         }
147         qunlock(&capalloc.qlock);
148
149         return t;
150 }
151
152 /* add a capability, throwing out any old ones */
153 static void addcap(uint8_t *hash)
154 {
155         struct Caphash *p, *t, **l;
156
157         p = kzmalloc(sizeof(*p), 0);
158         memmove(p->hash, hash, Hashlen);
159         p->next = NULL;
160
161         qlock(&capalloc.qlock);
162
163         /* trim extras */
164         while (capalloc.nhash >= Maxhash) {
165                 t = capalloc.first;
166                 if (t == NULL)
167                         panic("addcap");
168                 capalloc.first = t->next;
169                 kfree(t);
170                 capalloc.nhash--;
171         }
172
173         /* add new one */
174         for (l = &capalloc.first; *l != NULL; l = &(*l)->next)
175                 ;
176         *l = p;
177         capalloc.nhash++;
178
179         qunlock(&capalloc.qlock);
180 }
181
182 static void capclose(struct chan *c)
183 {
184 }
185
186 static size_t capread(struct chan *c, void *va, size_t n, off64_t m)
187 {
188         switch ((uint32_t)c->qid.path) {
189         case Qdir:
190                 return devdirread(c, va, n, capdir, ncapdir, devgen);
191
192         default:
193                 error(EPERM, "Permission denied: can't read capability files");
194                 break;
195         }
196         return n;
197 }
198
199 static size_t capwrite(struct chan *c, void *va, size_t n, off64_t m)
200 {
201         struct Caphash *p;
202         char *cp;
203         uint8_t hash[Hashlen + 1] = {0};
204         char *hashstr = NULL;
205         char *key, *from, *to;
206         char err[256];
207         int ret;
208         ERRSTACK(1);
209
210         switch ((uint32_t)c->qid.path) {
211         case Qhash:
212                 if (!iseve())
213                         error(EPERM, "permission denied: you must be eve");
214                 if (n < VB2_SHA256_DIGEST_SIZE * 2)
215                         error(EIO, "Short read: on Qhash");
216                 memmove(hash, va, Hashlen);
217                 for (int i = 0; i < Hashlen; i++)
218                         hash[i] = tolower(hash[i]);
219                 addcap(hash);
220                 break;
221
222         case Quse:
223                 /* copy key to avoid a fault in hmac_xx */
224                 cp = NULL;
225                 if (waserror()) {
226                         kfree(cp);
227                         kfree(hashstr);
228                         nexterror();
229                 }
230                 cp = kzmalloc(n + 1, 0);
231                 memmove(cp, va, n);
232                 cp[n] = 0;
233
234                 from = cp;
235                 key = strrchr(cp, '@');
236                 if (key == NULL)
237                         error(EIO, "short read: Quse");
238                 *key++ = 0;
239
240                 ret = hmac(VB2_HASH_SHA256, key, strlen(key),
241                            from, strlen(from), hash, Hashlen);
242                 if (ret)
243                         error(EINVAL, "HMAC failed");
244
245                 // Convert to ASCII text
246                 hashstr = (char *)kzmalloc(sizeof(hash), MEM_WAIT);
247                 ret = __hashstr(hashstr, hash, VB2_SHA256_DIGEST_SIZE);
248                 if (ret != VB2_SHA256_DIGEST_SIZE)
249                         error(EINVAL, "hash is wrong length");
250
251                 p = remcap((uint8_t *)hashstr);
252                 if (p == NULL) {
253                         snprintf(err, sizeof(err), "invalid capability %s@%s", from, key);
254                         error(EINVAL, err);
255                 }
256
257                 kfree(hashstr);
258                 hashstr = NULL;
259
260                 /* if a from user is supplied, make sure it matches */
261                 to = strchr(from, '@');
262                 if (to == NULL) {
263                         to = from;
264                 } else {
265                         *to++ = 0;
266                         if (strcmp(from, current->user.name) != 0)
267                                 error(EINVAL, "capability must match user");
268                 }
269
270                 /* set user id */
271                 // In the original user names were NULL-terminated; ensure
272                 // that is still the case.
273                 if (strlen(to) > sizeof(current->user.name) - 1)
274                         error(EINVAL, "New user name is > %d bytes",
275                               sizeof(current->user.name) - 1);
276                 proc_set_username(current, to);
277                 //up->basepri = PriNormal;
278
279                 kfree(p);
280                 kfree(cp);
281                 poperror();
282                 break;
283
284         default:
285                 error(EPERM, "permission denied: capwrite");
286                 break;
287         }
288
289         return n;
290 }
291
292 struct dev capdevtab __devtab = {
293         .name = "capability",
294
295         .reset = devreset,
296         .init = capinit,
297         .shutdown = devshutdown,
298         .attach = capattach,
299         .walk = capwalk,
300         .stat = capstat,
301         .open = capopen,
302         .create = devcreate,
303         .close = capclose,
304         .read = capread,
305         .bread = devbread,
306         .write = capwrite,
307         .bwrite = devbwrite,
308         .remove = capremove,
309         .wstat = devwstat,
310 };