xref: /inferno-os/emu/port/devsign.c (revision a60fa48ce2f27a689f276bea9538b5db2b74ff86)
1 #include	"dat.h"
2 #include	"fns.h"
3 #include	"error.h"
4 #include	"interp.h"
5 #include	<isa.h>
6 #include	"runt.h"
7 #include "mp.h"
8 #include "libsec.h"
9 #include "../../libkeyring/keys.h"
10 
11 /*
12  * experimental version of signed modules
13  */
14 
15 enum
16 {
17 	Qdir,
18 	Qkey,
19 	Qctl,
20 
21 	Maxkey = 2048
22 };
23 
24 static Dirtab signdir[] =
25 {
26 	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,
27 	"signerkey",	{Qkey},	0,			0644,
28 	"signerctl",	{Qctl},	0,			0600,
29 };
30 
31 typedef struct Get Get;
32 struct Get {
33 	uchar*	p;
34 	uchar*	ep;
35 };
36 
37 #define	G32(b)	((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3])
38 
39 static	int	vc(Get*);
40 static	int	vs(void*, int, Get*, int);
41 static Signerkey* findsignerkey(Skeyset*, char*, int, char*);
42 extern vlong		osusectime(void);
43 
44 int
45 verifysigner(uchar *sign, int len, uchar *data, ulong ndata)
46 {
47 	Get sig;
48 	int alg;
49 	ulong issued, expires, now;
50 	int footprint, r, n;
51 	uchar buf[128], digest[SHA1dlen];
52 	DigestState *ds;
53 	volatile struct {mpint* b;} b;
54 	volatile struct {mpint* s;} s;
55 	SigAlgVec *sa;
56 	Signerkey *key;
57 	Skeyset *sigs;
58 
59 	/* alg[1] issued[4] expires[4] footprint[2] signer[n] sig[m] */
60 	sigs = up->env->sigs;
61 	if(sigs == nil)
62 		return 1;	/* not enforcing signed modules */
63 	sig.p = sign;
64 	sig.ep = sign+len;
65 	alg = vc(&sig);
66 	if(alg != 2)
67 		return 0;	/* we do only SHA1/RSA */
68 	sa = findsigalg("rsa");
69 	if(sa == nil)
70 		return 0;
71 	if(vs(buf, sizeof(buf), &sig, 4) < 0)
72 		return 0;
73 	now = osusectime()/1000000;
74 	issued = G32(buf);
75 	if(vs(buf, sizeof(buf), &sig, 4) < 0)
76 		return 0;
77 	if(issued != 0 && now < issued)
78 		return 0;
79 	expires = G32(buf);
80 	if(expires != 0 && now >= expires)
81 		return 0;
82 	footprint = vc(&sig) << 8;
83 	footprint |= vc(&sig);
84 	if(footprint < 0)
85 		return 0;
86 	r = 0;
87 	b.b = nil;
88 	s.s = nil;
89 	qlock(&sigs->l);
90 	if(waserror())
91 		goto out;
92 	if((n = vs(buf, sizeof(buf)-NUMSIZE-1, &sig, -1)) < 0)	/* owner */
93 		goto out;
94 	buf[n] = 0;
95 	key = findsignerkey(sigs, sa->name, footprint, (char*)buf);
96 	if(key == nil)
97 		goto out;
98 	n += snprint((char*)buf+n, NUMSIZE, " %lud", expires);
99 	ds = sha1(buf, n, nil, nil);
100 	sha1(data, ndata, digest, ds);
101 	b.b = betomp(digest, SHA1dlen, nil);
102 	if(b.b == nil)
103 		goto out;
104 	s.s = betomp(sig.p, sig.ep-sig.p, nil);
105 	if(s.s == nil)
106 		goto out;
107 	r = (*sa->verify)(b.b, s.s, key->pk);
108 out:
109 	qunlock(&sigs->l);
110 	if(b.b != nil)
111 		mpfree(b.b);
112 	if(s.s != nil)
113 		mpfree(s.s);
114 	return r;
115 }
116 
117 int
118 mustbesigned(char *path, uchar *code, ulong length, Dir *dir)
119 {
120 	USED(code); USED(length);
121 if(0)print("load %s: %d %C\n", path, up->env->sigs!=nil, dir==nil?'?':dir->type);
122 	/* allow only signed modules and those in #/; already loaded modules are reloaded from cache */
123 	return up->env->sigs != nil && (dir == nil || dir->type != '/');
124 }
125 
126 static int
127 vc(Get *g)
128 {
129 	return g->p < g->ep? *g->p++: -1;
130 }
131 
132 static int
133 vs(void *s, int lim, Get *g, int n)
134 {
135 	int nr;
136 
137 	if(n < 0){
138 		if(g->p >= g->ep)
139 			return -1;
140 		n = *g->p++;
141 		lim--;
142 	}
143 	if(n > lim)
144 		return -1;
145 	nr = g->ep - g->p;
146 	if(n > nr)
147 		return -1;
148 	if(s != nil)
149 		memmove(s, g->p, n);
150 	g->p += n;
151 	return n;
152 }
153 
154 static char*
155 cstring(char *str, char **strp)
156 {
157 	char *p, *s;
158 	int n;
159 
160 	p = strchr(str, '\n');
161 	if(p == 0)
162 		p = str + strlen(str);
163 	n = p - str;
164 	s = malloc(n+1);
165 	if(s == nil)
166 		return nil;
167 	memmove(s, str, n);
168 	s[n] = 0;
169 
170 	if(strp){
171 		if(*p)
172 			p++;
173 		*strp = p;
174 	}
175 
176 	return s;
177 }
178 
179 static SigAlgVec*
180 cstrtoalg(char *str, char **strp)
181 {
182 	int n;
183 	char *p, name[KNAMELEN];
184 
185 	p = strchr(str, '\n');
186 	if(p == 0){
187 		p = str + strlen(str);
188 		if(strp)
189 			*strp = p;
190 	} else {
191 		if(strp)
192 			*strp = p+1;
193 	}
194 
195 	n = p - str;
196 	if(n >= sizeof(name))
197 		return nil;
198 	strncpy(name, str, n);
199 	name[n] = 0;
200 	return findsigalg(name);
201 }
202 
203 static Signerkey*
204 strtopk(char *buf)
205 {
206 	SigAlgVec *sa;
207 	char *p;
208 	Signerkey *key;
209 
210 	key = malloc(sizeof(*key));
211 	if(key == nil)
212 		return nil;
213 	key->r.ref = 1;
214 	sa = cstrtoalg(buf, &p);
215 	if(sa == nil){
216 		free(key);
217 		return nil;
218 	}
219 	key->alg = sa;
220 	key->pkfree = sa->pkfree;
221 	key->owner = cstring(p, &p);
222 	if(key->owner == nil){
223 		free(key);
224 		return nil;
225 	}
226 	key->pk = (*sa->str2pk)(p, &p);
227 	if(key->pk == nil){
228 		free(key->owner);
229 		free(key);
230 		return nil;
231 	}
232 	return key;
233 }
234 
235 static Signerkey*
236 findsignerkey(Skeyset *sigs, char *alg, int footprint, char *owner)
237 {
238 	int i;
239 	Signerkey *key;
240 
241 	for(i=0; i<sigs->nkey; i++){
242 		key = sigs->keys[i];
243 		if(key->footprint == footprint &&
244 		   strcmp(alg, ((SigAlgVec*)key->alg)->name) == 0 &&
245 		   strcmp(key->owner, owner) == 0)
246 			return key;
247 	}
248 	return nil;
249 }
250 
251 static Chan*
252 signattach(char *spec)
253 {
254 	return devattach(0x03A3, spec);	 /* L'Σ' */
255 }
256 
257 static Walkqid*
258 signwalk(Chan *c, Chan *nc, char **name, int nname)
259 {
260 	return devwalk(c, nc, name, nname, signdir, nelem(signdir), devgen);
261 }
262 
263 static int
264 signstat(Chan *c, uchar *db, int n)
265 {
266 	return devstat(c, db, n, signdir, nelem(signdir), devgen);
267 }
268 
269 static Chan*
270 signopen(Chan *c, int omode)
271 {
272 	if(c->qid.type & QTDIR) {
273 		if(omode != OREAD)
274 			error(Eisdir);
275 		c->mode = openmode(omode);
276 		c->flag |= COPEN;
277 		c->offset = 0;
278 		return c;
279 	}
280 
281 	switch((ulong)c->qid.path){
282 	case Qctl:
283 		if(!iseve())
284 			error(Eperm);
285 		break;
286 
287 	case Qkey:
288 		if(omode != OREAD && !iseve())
289 			error(Eperm);
290 		break;
291 	}
292 
293 	c->mode = openmode(omode);
294 	c->flag |= COPEN;
295 	c->offset = 0;
296 	return c;
297 }
298 
299 static void
300 signclose(Chan *c)
301 {
302 	USED(c);
303 }
304 
305 static long
306 signread(Chan *c, void *va, long n, vlong offset)
307 {
308 	char *buf, *p;
309 	SigAlgVec *sa;
310 	Skeyset *sigs;
311 	Signerkey *key;
312 	int i;
313 
314 	if(c->qid.type & QTDIR)
315 		return devdirread(c, va, n, signdir, nelem(signdir), devgen);
316 	sigs = up->env->sigs;
317 	if(sigs == nil)
318 		return 0;
319 	switch((ulong)c->qid.path){
320 	case Qkey:
321 		buf = smalloc(Maxkey);
322 		if(waserror()){
323 			free(buf);
324 			nexterror();
325 		}
326 		qlock(&sigs->l);
327 		if(waserror()){
328 			qunlock(&sigs->l);
329 			nexterror();
330 		}
331 		p = buf;
332 		for(i=0; i<sigs->nkey; i++){
333 			key = sigs->keys[i];
334 			sa = key->alg;
335 			p = seprint(p, buf+Maxkey, "owner=%s alg=%s footprint=%ud expires=%lud\n",
336 				key->owner, sa->name, key->footprint, key->expires);
337 		}
338 		poperror();
339 		qunlock(&sigs->l);
340 		n = readstr(offset, va, n, buf);
341 		poperror();
342 		free(buf);
343 		return n;
344 
345 	case Qctl:
346 		return readnum(offset, va, n, sigs->nkey, NUMSIZE);
347 	}
348 	return 0;
349 }
350 
351 static long
352 signwrite(Chan *c, void *va, long n, vlong offset)
353 {
354 	char *buf;
355 	Skeyset *sigs;
356 	Signerkey *okey, *key;
357 	int i;
358 
359 	if(c->qid.type & QTDIR)
360 		error(Eisdir);
361 	USED(offset);
362 	switch((ulong)c->qid.path){
363 	case Qkey:
364 		if(n >= Maxkey)
365 			error(Etoobig);
366 		buf = smalloc(Maxkey);
367 		if(waserror()){
368 			free(buf);
369 			nexterror();
370 		}
371 		memmove(buf, va, n);
372 		buf[n] = 0;
373 
374 		key = strtopk(buf);
375 		if(key == nil)
376 			error("bad key syntax");
377 		poperror();
378 		free(buf);
379 
380 		if(waserror()){
381 			freeskey(key);
382 			nexterror();
383 		}
384 		sigs = up->env->sigs;
385 		if(sigs == nil){
386 			sigs = malloc(sizeof(*sigs));
387 			if(sigs == nil)
388 				error(Enomem);
389 			sigs->r.ref = 1;
390 			up->env->sigs = sigs;
391 		}
392 		qlock(&sigs->l);
393 		if(waserror()){
394 			qunlock(&sigs->l);
395 			nexterror();
396 		}
397 		for(i=0; i<sigs->nkey; i++){
398 			okey = sigs->keys[i];
399 			if(strcmp(okey->owner, key->owner) == 0){
400 				/* replace existing key */
401 				sigs->keys[i] = key;
402 				freeskey(okey);
403 				break;
404 			}
405 		}
406 		if(i >= sigs->nkey){
407 			if(sigs->nkey >= nelem(sigs->keys))
408 				error("too many keys");
409 			sigs->keys[sigs->nkey++] = key;
410 		}
411 		poperror();
412 		qunlock(&sigs->l);
413 		poperror();	/* key */
414 
415 		return n;
416 	case Qctl:
417 		error(Ebadctl);
418 		break;
419 	}
420 	return 0;
421 }
422 
423 Dev signdevtab = {
424 	0x03A3,	/* L'Σ',	/* U+03A3 */
425 	"sign",
426 
427 	devinit,
428 	signattach,
429 	signwalk,
430 	signstat,
431 	signopen,
432 	devcreate,
433 	signclose,
434 	signread,
435 	devbread,
436 	signwrite,
437 	devbwrite,
438 	devremove,
439 	devwstat
440 };
441