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