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