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