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