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