xref: /inferno-os/emu/port/devcap.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
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