diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /emu/port/devsign.c | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'emu/port/devsign.c')
| -rw-r--r-- | emu/port/devsign.c | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/emu/port/devsign.c b/emu/port/devsign.c new file mode 100644 index 00000000..f42d1dbb --- /dev/null +++ b/emu/port/devsign.c @@ -0,0 +1,440 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include <isa.h> +#include "runt.h" +#include "mp.h" +#include "libsec.h" +#include "../../libkeyring/keys.h" + +/* + * experimental version of signed modules + */ + +enum +{ + Qdir, + Qkey, + Qctl, + + Maxkey = 2048 +}; + +static Dirtab signdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "signerkey", {Qkey}, 0, 0644, + "signerctl", {Qctl}, 0, 0600, +}; + +typedef struct Get Get; +struct Get { + uchar* p; + uchar* ep; +}; + +#define G32(b) ((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3]) + +static int vc(Get*); +static int vs(void*, int, Get*, int); +static Signerkey* findsignerkey(Skeyset*, char*, int, char*); +extern vlong osusectime(void); + +int +verifysigner(uchar *sign, int len, uchar *data, ulong ndata) +{ + Get sig; + int alg; + ulong issued, expires, now; + int footprint, r, n; + uchar buf[128], digest[SHA1dlen]; + DigestState *ds; + volatile struct {BigInt b;} b; + volatile struct {BigInt s;} s; + SigAlgVec *sa; + Signerkey *key; + Skeyset *sigs; + + /* alg[1] issued[4] expires[4] footprint[2] signer[n] sig[m] */ + sigs = up->env->sigs; + if(sigs == nil) + return 1; /* not enforcing signed modules */ + sig.p = sign; + sig.ep = sign+len; + alg = vc(&sig); + if(alg != 2) + return 0; /* we do only SHA1/RSA */ + sa = findsigalg("rsa"); + if(sa == nil) + return 0; + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + now = osusectime()/1000000; + issued = G32(buf); + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + if(issued != 0 && now < issued) + return 0; + expires = G32(buf); + if(expires != 0 && now >= expires) + return 0; + footprint = vc(&sig) << 8; + footprint |= vc(&sig); + if(footprint < 0) + return 0; + r = 0; + b.b = nil; + s.s = nil; + qlock(&sigs->l); + if(waserror()) + goto out; + if((n = vs(buf, sizeof(buf)-NUMSIZE-1, &sig, -1)) < 0) /* owner */ + goto out; + buf[n] = 0; + key = findsignerkey(sigs, sa->name, footprint, (char*)buf); + if(key == nil) + goto out; + n += snprint((char*)buf+n, NUMSIZE, " %lud", expires); + ds = sha1(buf, n, nil, nil); + sha1(data, ndata, digest, ds); + b.b = betomp(digest, SHA1dlen, nil); + if(b.b == nil) + goto out; + s.s = betomp(sig.p, sig.ep-sig.p, nil); + if(s.s == nil) + goto out; + r = (*sa->verify)(b.b, s.s, key->pk); +out: + qunlock(&sigs->l); + if(b.b != nil) + mpfree(b.b); + if(s.s != nil) + mpfree(s.s); + return r; +} + +int +mustbesigned(char *path, uchar *code, ulong length, Dir *dir) +{ + USED(code); USED(length); +if(0)print("load %s: %d %C\n", path, up->env->sigs!=nil, dir==nil?'?':dir->type); + /* allow only signed modules and those in #/; already loaded modules are reloaded from cache */ + return up->env->sigs != nil && (dir == nil || dir->type != '/'); +} + +static int +vc(Get *g) +{ + return g->p < g->ep? *g->p++: -1; +} + +static int +vs(void *s, int lim, Get *g, int n) +{ + int nr; + + if(n < 0){ + if(g->p >= g->ep) + return -1; + n = *g->p++; + lim--; + } + if(n > lim) + return -1; + nr = g->ep - g->p; + if(n > nr) + return -1; + if(s != nil) + memmove(s, g->p, n); + g->p += n; + return n; +} + +static char* +cstring(char *str, char **strp) +{ + char *p, *s; + int n; + + p = strchr(str, '\n'); + if(p == 0) + p = str + strlen(str); + n = p - str; + s = malloc(n+1); + if(s == nil) + return nil; + memmove(s, str, n); + s[n] = 0; + + if(strp){ + if(*p) + p++; + *strp = p; + } + + return s; +} + +static SigAlgVec* +cstrtoalg(char *str, char **strp) +{ + int n; + char *p, name[KNAMELEN]; + + p = strchr(str, '\n'); + if(p == 0){ + p = str + strlen(str); + if(strp) + *strp = p; + } else { + if(strp) + *strp = p+1; + } + + n = p - str; + if(n >= sizeof(name)) + return nil; + strncpy(name, str, n); + name[n] = 0; + return findsigalg(name); +} + +static Signerkey* +strtopk(char *buf) +{ + SigAlgVec *sa; + char *p; + Signerkey *key; + + key = malloc(sizeof(*key)); + if(key == nil) + return nil; + key->r.ref = 1; + sa = cstrtoalg(buf, &p); + if(sa == nil){ + free(key); + return nil; + } + key->alg = sa; + key->pkfree = sa->pkfree; + key->owner = cstring(p, &p); + if(key->owner == nil){ + free(key); + return nil; + } + key->pk = (*sa->str2pk)(p, &p); + if(key->pk == nil){ + free(key->owner); + free(key); + return nil; + } + return key; +} + +static Signerkey* +findsignerkey(Skeyset *sigs, char *alg, int footprint, char *owner) +{ + int i; + Signerkey *key; + + for(i=0; i<sigs->nkey; i++){ + key = sigs->keys[i]; + if(key->footprint == footprint && + strcmp(alg, ((SigAlgVec*)key->alg)->name) == 0 && + strcmp(key->owner, owner) == 0) + return key; + } + return nil; +} + +static Chan* +signattach(char *spec) +{ + return devattach(0x03A3, spec); /* L'Σ' */ +} + +static Walkqid* +signwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, signdir, nelem(signdir), devgen); +} + +static int +signstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, signdir, nelem(signdir), devgen); +} + +static Chan* +signopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + switch((ulong)c->qid.path){ + case Qctl: + if(!iseve()) + error(Eperm); + break; + + case Qkey: + if(omode != OREAD && !iseve()) + error(Eperm); + break; + } + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +signclose(Chan *c) +{ + USED(c); +} + +static long +signread(Chan *c, void *va, long n, vlong offset) +{ + char *buf, *p; + SigAlgVec *sa; + Skeyset *sigs; + Signerkey *key; + int i; + + if(c->qid.type & QTDIR) + return devdirread(c, va, n, signdir, nelem(signdir), devgen); + sigs = up->env->sigs; + if(sigs == nil) + return 0; + switch((ulong)c->qid.path){ + case Qkey: + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + qlock(&sigs->l); + if(waserror()){ + qunlock(&sigs->l); + nexterror(); + } + p = buf; + for(i=0; i<sigs->nkey; i++){ + key = sigs->keys[i]; + sa = key->alg; + p = seprint(p, buf+Maxkey, "owner=%s alg=%s footprint=%ud expires=%lud\n", + key->owner, sa->name, key->footprint, key->expires); + } + poperror(); + qunlock(&sigs->l); + n = readstr(offset, va, n, buf); + poperror(); + free(buf); + return n; + + case Qctl: + return readnum(offset, va, n, sigs->nkey, NUMSIZE); + } + return 0; +} + +static long +signwrite(Chan *c, void *va, long n, vlong offset) +{ + char *buf; + Skeyset *sigs; + Signerkey *okey, *key; + int i; + + if(c->qid.type & QTDIR) + error(Eisdir); + USED(offset); + switch((ulong)c->qid.path){ + case Qkey: + if(n >= Maxkey) + error(Etoobig); + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + memmove(buf, va, n); + buf[n] = 0; + + key = strtopk(buf); + if(key == nil) + error("bad key syntax"); + poperror(); + free(buf); + + if(waserror()){ + freeskey(key); + nexterror(); + } + sigs = up->env->sigs; + if(sigs == nil){ + sigs = malloc(sizeof(*sigs)); + if(sigs == nil) + error(Enomem); + sigs->r.ref = 1; + up->env->sigs = sigs; + } + qlock(&sigs->l); + if(waserror()){ + qunlock(&sigs->l); + nexterror(); + } + for(i=0; i<sigs->nkey; i++){ + okey = sigs->keys[i]; + if(strcmp(okey->owner, key->owner) == 0){ + /* replace existing key */ + sigs->keys[i] = key; + freeskey(okey); + break; + } + } + if(i >= sigs->nkey){ + if(sigs->nkey >= nelem(sigs->keys)) + error("too many keys"); + sigs->keys[sigs->nkey++] = key; + } + poperror(); + qunlock(&sigs->l); + poperror(); /* key */ + + return n; + case Qctl: + error(Ebadctl); + break; + } + return 0; +} + +Dev signdevtab = { + 0x03A3, /* L'Σ', /* U+03A3 */ + "sign", + + devinit, + signattach, + signwalk, + signstat, + signopen, + devcreate, + signclose, + signread, + devbread, + signwrite, + devbwrite, + devremove, + devwstat +}; |
