1 #include "dat.h"
2 #include "fns.h"
3 #include "error.h"
4 #include "mp.h"
5 #include "libsec.h"
6
7 /*
8 * Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
9 */
10
11 enum {
12 Captimeout = 15, /* seconds until expiry */
13 Capidletime = 60 /* idle seconds before capwatch exits */
14 };
15
16 typedef struct Caps Caps;
17 struct Caps
18 {
19 uchar hash[SHA1dlen];
20 ulong time;
21 Caps* next;
22 };
23
24 struct {
25 QLock l;
26 Caps* caps;
27 int kpstarted;
28 } allcaps;
29
30 enum {
31 Qdir,
32 Qhash,
33 Quse
34 };
35
36 static Dirtab capdir[] =
37 {
38 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
39 "capuse", {Quse, 0}, 0, 0222,
40 "caphash", {Qhash, 0}, 0, 0200,
41 };
42
43 static int ncapdir = nelem(capdir);
44
45 static void
capwatch(void * a)46 capwatch(void *a)
47 {
48 Caps *c, **l;
49 int idletime;
50
51 USED(a);
52 idletime = 0;
53 for(;;){
54 osmillisleep(30*1000);
55 qlock(&allcaps.l);
56 for(l = &allcaps.caps; (c = *l) != nil;)
57 if(++c->time > Captimeout){
58 *l = c->next;
59 free(c);
60 }else
61 l = &c->next;
62 if(allcaps.caps == nil){
63 if(++idletime > Capidletime){
64 allcaps.kpstarted = 0;
65 qunlock(&allcaps.l);
66 pexit("", 0);
67 }
68 }else
69 idletime = 0;
70 qunlock(&allcaps.l);
71 }
72 }
73
74 static Chan *
capattach(char * spec)75 capattach(char *spec)
76 {
77 return devattach(0x00A4, spec); /* L'¤' */
78 }
79
80 static Walkqid*
capwalk(Chan * c,Chan * nc,char ** name,int nname)81 capwalk(Chan *c, Chan *nc, char **name, int nname)
82 {
83 return devwalk(c, nc, name, nname, capdir, nelem(capdir), devgen);
84 }
85
86 static int
capstat(Chan * c,uchar * db,int n)87 capstat(Chan *c, uchar *db, int n)
88 {
89 return devstat(c, db, n, capdir, nelem(capdir), devgen);
90 }
91
92 static Chan*
capopen(Chan * c,int omode)93 capopen(Chan *c, int omode)
94 {
95 if(c->qid.type & QTDIR) {
96 if(omode != OREAD)
97 error(Eisdir);
98 c->mode = omode;
99 c->flag |= COPEN;
100 c->offset = 0;
101 return c;
102 }
103
104 if(c->qid.path == Qhash && !iseve())
105 error(Eperm);
106
107 c->mode = openmode(omode);
108 c->flag |= COPEN;
109 c->offset = 0;
110 return c;
111 }
112
113 static void
capclose(Chan * c)114 capclose(Chan *c)
115 {
116 USED(c);
117 }
118
119 static long
capread(Chan * c,void * va,long n,vlong vl)120 capread(Chan *c, void *va, long n, vlong vl)
121 {
122 USED(vl);
123 switch((ulong)c->qid.path){
124 case Qdir:
125 return devdirread(c, va, n, capdir, ncapdir, devgen);
126
127 default:
128 error(Eperm);
129 break;
130 }
131 return n;
132 }
133
134 static int
capwritehash(uchar * a,int l)135 capwritehash(uchar *a, int l)
136 {
137 Caps *c;
138
139 if(l != SHA1dlen)
140 return -1;
141 c = malloc(sizeof(*c));
142 if(c == nil)
143 return -1;
144 memmove(c->hash, a, l);
145 c->time = 0;
146 qlock(&allcaps.l);
147 c->next = allcaps.caps;
148 allcaps.caps = c;
149 if(!allcaps.kpstarted){
150 allcaps.kpstarted = 1;
151 kproc("capwatch", capwatch, 0, 0);
152 }
153 qunlock(&allcaps.l);
154 return 0;
155 }
156
157 static int
capwriteuse(uchar * a,int len)158 capwriteuse(uchar *a, int len)
159 {
160 int n;
161 uchar digest[SHA1dlen];
162 char buf[128], *p, *users[3];
163 Caps *c, **l;
164
165 if(len >= sizeof(buf)-1)
166 return -1;
167 memmove(buf, a, len);
168 buf[len] = 0;
169 p = strrchr(buf, '@');
170 if(p == nil)
171 return -1;
172 *p++ = 0;
173 len = strlen(p);
174 n = strlen(buf);
175 if(len == 0 || n == 0)
176 return -1;
177 hmac_sha1((uchar*)buf, n, (uchar*)p, len, digest, nil);
178 n = getfields(buf, users, nelem(users), 0, "@");
179 if(n == 1)
180 users[1] = users[0];
181 else if(n != 2)
182 return -1;
183 if(*users[0] == 0 || *users[1] == 0)
184 return -1;
185 qlock(&allcaps.l);
186 for(l = &allcaps.caps; (c = *l) != nil; l = &c->next)
187 if(memcmp(c->hash, digest, sizeof(c->hash)) == 0){
188 *l = c->next;
189 qunlock(&allcaps.l);
190 free(c);
191 if(n == 2 && strcmp(up->env->user, users[0]) != 0)
192 return -1;
193 setid(users[1], 0); /* could use users[2] to mean `host OS user' */
194 return 0;
195 }
196 qunlock(&allcaps.l);
197 return -1;
198 }
199
200 static long
capwrite(Chan * c,void * buf,long n,vlong offset)201 capwrite(Chan* c, void* buf, long n, vlong offset)
202 {
203 USED(offset);
204 switch((ulong)c->qid.path){
205 case Qhash:
206 if(capwritehash(buf, n) < 0)
207 error(Ebadarg);
208 return n;
209 case Quse:
210 if(capwriteuse(buf, n) < 0)
211 error("invalid capability");
212 return n;
213 }
214 error(Ebadarg);
215 return 0;
216 }
217
218 static void
capremove(Chan * c)219 capremove(Chan *c)
220 {
221 if(c->qid.path != Qhash || !iseve())
222 error(Eperm);
223 ncapdir = nelem(capdir)-1;
224 }
225
226 Dev capdevtab = {
227 0x00A4, /* L'¤' */
228 "cap",
229
230 devinit,
231 capattach,
232 capwalk,
233 capstat,
234 capopen,
235 devcreate,
236 capclose,
237 capread,
238 devbread,
239 capwrite,
240 devbwrite,
241 capremove,
242 devwstat
243 };
244