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