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