ref: 28981fdd9499edc1223e393204d5b735c2210a2c
author: Ori Bernstein <ori@eigenstate.org>
date: Thu Oct 17 10:41:17 EDT 2024
initial commit
--- /dev/null
+++ b/fs.c
@@ -1,0 +1,527 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include <mp.h>
+#include <libsec.h>
+
+#include "pki.h"
+
+enum {
+ Qroot,
+ Qnew,
+ Qvrf,
+};
+
+#define QTYPE(qpath) ((qpath)&0xf)
+#define QIDX(qpath) ((qpath)>>4)
+#define Q(id, type) (((id)<<4)|(type))
+
+char Egreg[] = "really, greg?";
+char Ephase[] = "phase error";
+char Eproto[] = "protocol error";
+char Eimpl[] = "not implemented";
+char Enomem[] = "out of memory";
+char Enoexist[] = "file does not exist";
+char Ewalkf[] = "walk into file";
+char Egarble[] = "garbled message";
+char Ecrtfmt[] = "unknown certificate format";
+char Ecrtcnt[] = "invalid certificate count";
+char Ecrtsz[] = "invalid certificate size";
+char Ebadcrt[] = "could not parse certificate";
+char Ersrc[] = "invalid resource type";
+#define Ephase (abort(), "wut")
+#define Ecrtsz (abort(), "wut")
+char *srvname = "pki";
+char *mntname = "/mnt/pki";
+int debug;
+
+Vrf **activevrf;
+int nactivevrf;
+int activevrfsz;
+vlong nextqpath;
+
+char *phasenames[] = {
+ "Snone",
+ "Srspec",
+ "Scsize",
+ "Scdata",
+ "Saccept",
+ "Sreject",
+ "Serror",
+};
+
+int
+xphase(Vrf *v, int phase)
+{
+ if(debug)
+ fprint(2, "phase: %s → %s\n", phasenames[v->phase], phasenames[phase]);
+ return phase;
+}
+
+int
+loadcrt(Vrf *v)
+{
+ CertX509 *c;
+ void *der;
+ int len;
+
+ der = v->rbuf;
+ len = v->rbufsz;
+ if(v->rfmt == CFpem)
+ if(decodePEM((char*)v->rbuf, "CERTIFICATE", &len, &der) == nil)
+ return -1;
+ if((c = X509decode(der, len)) == nil)
+ return -1;
+ if(v->icert)
+ addcert(v->tab, c, 0);
+ else
+ v->vrf = c;
+ if(v->rbuf != der)
+ free(der);
+ free(v->rbuf);
+ v->rbuf = nil;
+ v->nrbuf = 0;
+ return 0;
+}
+
+int
+checkchain(CertTab *tab, CertX509 *c, char *name)
+{
+ if(strcmp(c->subject, name) != 0)
+ return Sreject;
+ if(vfcert(tab, c) == 0)
+ return Saccept;
+ else
+ return Sreject;
+}
+
+char*
+certfill(Vrf *v, char *chunk, int nchunk)
+{
+ if(nchunk < 0 || v->nrbuf + nchunk > v->rbufsz)
+ return Ecrtsz;
+ memcpy(v->rbuf + v->nrbuf, chunk, nchunk);
+ v->nrbuf += nchunk;
+ if(v->nrbuf == v->rbufsz){
+ v->rbuf[v->rbufsz] = 0;
+ if(loadcrt(v) == -1){
+ v->phase = xphase(v, Serror);
+ return Ebadcrt;
+ }
+ v->phase = xphase(v, Scsize);
+ }
+ return nil;
+}
+
+char*
+mkvrf(Fid *fid, Qid *q)
+{
+ Vrf *v, **a;
+
+ if(activevrfsz == nactivevrf){
+ if(activevrfsz > 100)
+ return nil;
+ if((a = realloc(activevrf, (activevrfsz+1) * sizeof(Vrf*))) == nil)
+ return nil;
+ activevrfsz++;
+ activevrf = a;
+ }
+ if((v = mallocz(sizeof(Vrf), 1)) == nil)
+ return Enomem;
+ nextqpath++;
+ q->path = Q(nextqpath, Qvrf);
+ q->vers = 0;
+ q->type = 0;
+ memset(v, 0, sizeof(Vrf));
+ v->phase = Snone;
+ v->qid = *q;
+ v->cfmt = CFnone;
+ v->ncrt = 0;
+ v->rbuf = nil;
+ v->nrbuf = 0;
+ v->tab = mktab(roottab, 512);
+ fid->aux = v;
+ activevrf[nactivevrf++] = v;
+ incref(v);
+ return nil;
+}
+
+void
+vrfopen(Vrf *v)
+{
+ v->phase = xphase(v, Srspec);
+}
+
+char*
+vrfspec(Req *r)
+{
+ char *f[3], buf[IOUNIT];
+ Vrf *v;
+ int nf;
+
+ v = r->fid->aux;
+ if(v == nil || v->phase != Srspec)
+ return Ephase;
+
+ memcpy(buf, r->ifcall.data, r->ifcall.count);
+ buf[r->ifcall.count] = 0;
+ nf = tokenize(buf, f, nelem(f));
+ if(nf != nelem(f))
+ return Egarble;
+
+ if(strcmp(f[0], "verify") != 0)
+ return Egarble;
+
+ if(strcmp(f[1], "host") == 0)
+ v->ntype = CRhost;
+ else
+ return Ecrtfmt;
+
+ v->name = strdup(f[2]);
+ if(v->name == nil)
+ return Enomem;
+
+ v->phase = xphase(v, Scsize);
+ r->ofcall.count = r->ifcall.count;
+ return nil;
+}
+
+char*
+vrfsize(Req *r)
+{
+ char buf[65], *f[3], *p, *e;
+ int n, nbuf, nf;
+ Vrf *v;
+
+ v = r->fid->aux;
+ if(v == nil || v->phase != Scsize)
+ return Ephase;
+
+ nbuf = nelem(buf)-1;
+ if(r->ifcall.count < nbuf)
+ nbuf = r->ifcall.count;
+ for(n = 0; n < nbuf; n++){
+ buf[n] = r->ifcall.data[n];
+ if(buf[n] == '\n' || buf[n] == '\0'){
+ buf[n++] = 0;
+ break;
+ }
+ }
+ buf[n] = 0;
+ if((p = strchr(buf, '\n')) != nil)
+ *p = 0;
+ nf = tokenize(buf, f, nelem(f));
+ if(nf == 1 && strcmp(f[0], "done") == 0){
+ v->phase = checkchain(v->tab, v->vrf, v->name);
+ return nil;
+ }
+
+ if(nf != nelem(f))
+ return Egarble;
+
+ if(strcmp(f[0], "cert") == 0 && v->vrf == nil)
+ v->icert = 0;
+ else if(strcmp(f[0], "icert") == 0)
+ v->icert = 1;
+ else
+ return Egarble;
+
+ if(strcmp(f[1], "pem") == 0)
+ v->rfmt = CFpem;
+ else if(strcmp(f[1], "der") == 0)
+ v->rfmt = CFder;
+ else
+ return Egarble;
+
+ v->rbufsz = strtol(f[2], &e, 10);
+ if(*e != 0 || v->rbufsz < 0 || v->rbufsz > 1024*1024)
+ return Egarble;
+
+ if((v->rbuf = malloc(v->rbufsz + 1)) == nil)
+ return Enomem;
+ v->phase = xphase(v, Scdata);
+ e = certfill(v, r->ifcall.data + n, r->ifcall.count - n);
+ if(e != nil)
+ r->ofcall.count = r->ifcall.count;
+ return e;
+
+}
+
+char*
+vrfcert(Req *r)
+{
+ Vrf *v;
+ char *e;
+
+ v = r->fid->aux;
+ if(v == nil || v->phase != Scdata)
+ return Ephase;
+ e = certfill(v, r->ifcall.data, r->ifcall.count);
+ if(e != nil)
+ r->ofcall.count = r->ifcall.count;
+ return e;
+}
+
+char*
+vrflookup(char *name, Qid *q)
+{
+ vlong id, qpath;
+ char *e;
+ int i;
+
+ id = strtoll(name, &e, 10);
+ if(*e != 0)
+ return nil;
+ qpath = Q(id, Qvrf);
+ for(i = 0; i < nactivevrf; i++){
+ if(activevrf[i]->qid.path == qpath){
+ *q = activevrf[i]->qid;
+ return nil;
+ }
+ }
+ return Enoexist;
+}
+
+void
+vrfdrop(Vrf *v)
+{
+ int i;
+
+ for(i = 0; i < nactivevrf; i++)
+ if(v == activevrf[i])
+ break;
+ assert(i != nactivevrf);
+ activevrf[i] = activevrf[nactivevrf-1];
+ nactivevrf--;
+}
+
+int
+qidstat(Dir *d, vlong qpath)
+{
+ d->uid = estrdup9p("vrf");
+ d->gid = estrdup9p("vrf");
+ d->muid = estrdup9p("vrf");
+ d->mode = 0444;
+ d->atime = 0;
+ d->mtime = 0;
+ switch(QTYPE(qpath)){
+ case Qroot:
+ d->qid = (Qid){Qroot, 0, QTDIR};
+ d->name = estrdup9p("/");
+ break;
+ case Qnew:
+ d->qid = (Qid){Qnew, 0, 0};
+ d->name = estrdup9p("new");
+ break;
+ case Qvrf:
+ d->qid = (Qid){qpath, 0, 0};
+ d->name = smprint("%lld", QIDX(qpath));
+ break;
+ }
+ return 0;
+}
+
+
+int
+rootgen(int i, Dir *d, void *)
+{
+ if(i == 0)
+ qidstat(d, Qnew);
+ else if(i >= 1 && i <= nactivevrf)
+ qidstat(d, activevrf[i-1]->qid.path);
+ else
+ return -1;
+ return 0;
+}
+
+
+char*
+vrfwrite(Vrf *v, Req *r)
+{
+ switch(v->phase){
+ case Srspec: return vrfspec(r); break;
+ case Scsize: return vrfsize(r); break;
+ case Scdata: return vrfcert(r); break;
+ default: return Ephase; break;
+ }
+}
+
+char*
+vrfread(Vrf *v, Req *r)
+{
+ switch(v->phase){
+ case Saccept: readstr(r, "accept"); break;
+ case Sreject: readstr(r, "reject"); break;
+ case Serror: return Eproto; break;
+ default: return Ephase; break;
+ }
+ return nil;
+}
+
+void
+fsattach(Req *r)
+{
+ r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
+ r->fid->qid = r->ofcall.qid;
+ r->fid->aux = nil;
+ respond(r, nil);
+}
+
+void
+fsopen(Req *r)
+{
+ Vrf *v;
+
+ if(QTYPE(r->fid->qid.path) == Qvrf){
+ v = r->fid->aux;
+ v->phase = Srspec;
+ }
+ respond(r, nil);
+}
+
+void
+fsread(Req *r)
+{
+ char *e;
+
+ e = nil;
+ switch(QTYPE(r->fid->qid.path)){
+ case Qroot:
+ dirread9p(r, rootgen, nil);
+ break;
+ case Qnew:
+ e = Egreg;
+ break;
+ default:
+ e = vrfread(r->fid->aux, r);
+ break;
+ }
+ respond(r, e);
+}
+
+void
+fswrite(Req *r)
+{
+ char *e;
+
+ switch(QTYPE(r->fid->qid.path)){
+ case Qroot:
+ case Qnew:
+ e = Egreg;
+ break;
+ default:
+ e = vrfwrite(r->fid->aux, r);
+ break;
+ }
+ respond(r, e);
+
+}
+
+void
+fsremove(Req *r)
+{
+ char *e;
+
+ e = nil;
+ switch(QTYPE(r->fid->qid.path)){
+ case Qroot:
+ case Qnew:
+ e = Egreg;
+ break;
+ default:
+ break;
+ }
+ respond(r, e);
+
+
+}
+
+void
+fsstat(Req *r)
+{
+ if(qidstat(&r->d, r->fid->qid.path) == -1)
+ respond(r, Ephase);
+ else
+ respond(r, nil);
+}
+
+char*
+fswalk1(Fid *fid, char *name, Qid *q)
+{
+ char *e;
+
+ e = nil;
+ switch(QTYPE(fid->qid.path)){
+ case Qroot:
+ if(strcmp(name, "..") == 0)
+ *q = (Qid){Q(0, Qroot), 0, QTDIR};
+ else if(strcmp(name, "new") == 0)
+ e = mkvrf(fid, q);
+ else
+ e = vrflookup(name, q);
+ break;
+ default:
+ if(strcmp(name, "..") == 0)
+ *q = (Qid){Q(0, Qroot), 0, QTDIR};
+ else
+ e = Ewalkf;
+ break;
+ }
+ if(e != nil)
+ fid->qid = *q;
+ return e;
+}
+
+char*
+fsclone(Fid *old, Fid *new)
+{
+ if(old->aux != nil){
+ new->aux = old->aux;
+ incref((Vrf*)new->aux);
+ }
+ return nil;
+}
+
+void
+fsdestroyfid(Fid *f)
+{
+ if(f->aux != nil && decref((Vrf*)f->aux) == 0)
+ vrfdrop(f->aux);
+}
+
+Srv pkisrv = {
+ .attach = fsattach,
+ .open = fsopen,
+ .read = fsread,
+ .write = fswrite,
+ .remove = fsremove,
+ .stat = fsstat,
+ .walk1 = fswalk1,
+ .clone = fsclone,
+ .destroyfid = fsdestroyfid,
+};
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-r root] certs...\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ roottab = mktab(nil, 512);
+
+ ARGBEGIN{
+ case 'r': loadcerts(roottab, EARGF(usage()), 1); break;
+ case 'n': mntname = EARGF(usage()); break;
+ case 's': srvname = EARGF(usage()); break;
+ case 'd': debug++; chatty9p++; break;
+ default: usage(); break;
+ }ARGEND;
+
+ postmountsrv(&pkisrv, srvname, mntname, MREPL);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,10 @@
+</$objtype/mkfile
+
+TARG=pki
+OFILES=\
+ pki.$O\
+ fs.$O\
+
+HFILES=pki.h
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/pki.c
@@ -1,0 +1,170 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <mp.h>
+#include <libsec.h>
+
+#include "pki.h"
+
+CertTab *roottab;
+
+static u32int
+strhash(char *s)
+{
+ u32int h;
+
+ h = 33;
+ for(; *s; s++)
+ h = h*33 + *s;
+ return h;
+}
+
+CertTab*
+mktab(CertTab *up, int nent)
+{
+ CertTab *tab;
+ Issuer **ent;
+
+ tab = malloc(sizeof(CertTab));
+ ent = mallocz(nent*sizeof(Issuer*), 1);
+ if(tab == nil || ent == nil){
+ free(ent);
+ free(tab);
+ return nil;
+ }
+ tab->up = up;
+ tab->ent = ent;
+ tab->nent = nent;
+ return tab;
+}
+
+static void
+freetab(CertTab *tab)
+{
+ free(tab->ent);
+ free(tab);
+}
+
+static Issuer*
+ctabget(CertTab *tab, char *name)
+{
+ Issuer *ci;
+ u32int h;
+
+ h = strhash(name) % tab->nent;
+ for(ci = tab->ent[h]; ci != nil; ci = ci->next)
+ if(strcmp(ci->name, name) == 0)
+ return ci;
+ if(tab->up != nil)
+ return ctabget(tab->up, name);
+ return nil;
+}
+
+int
+addcert(CertTab *tab, CertX509 *crt, int isroot)
+{
+ Issuer *ci;
+ CertEnt *c;
+ u32int h;
+
+ ci = nil;
+ if((c = mallocz(sizeof(CertEnt), 1)) == nil)
+ goto Error;
+ h = strhash(crt->subject) % tab->nent;
+ c->c = crt;
+ for(ci = tab->ent[h]; ci != nil; ci = ci->next)
+ if(strcmp(ci->name, c->c->subject) == 0)
+ goto Found;
+ if((ci = mallocz(sizeof(Issuer), 1)) == nil)
+ goto Error;
+ if((ci->name = strdup(c->c->subject)) == nil)
+ goto Error;
+ ci->next = tab->ent[h];
+ tab->ent[h] = ci;
+Found:
+ c->isroot = isroot;
+ c->next = ci->certs;
+ ci->certs = c;
+ return 0;
+Error:
+ free(ci);
+ free(c);
+ return -1;
+}
+
+static int
+expired(CertX509 *crt)
+{
+ vlong now;
+
+ now = time(nil);
+ return now < crt->validity_start || now > crt->validity_end;
+}
+
+static int
+vfsig(CertX509 *crt, CertX509 *vrf)
+{
+ char *r;
+ r = X509verify(crt, vrf);
+ if(r != nil){
+ werrstr("%s", r);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+matchesname(CertX509 *crt, CertX509 *vrf)
+{
+ USED(crt, vrf);
+ return 1;
+}
+
+static int
+vfconstraints(CertX509 *crt, CertX509 *vrf)
+{
+ if(expired(crt)){
+ werrstr("certificate revoked");
+ return -1;
+ }
+ if(!matchesname(crt, vrf)){
+ werrstr("name constraint failure");
+ return -1;
+ }
+ return 0;
+}
+
+int
+vfcert(CertTab *tab, CertX509 *crt)
+{
+ CertEnt *ce;
+ Issuer *ci;
+
+ if((ci = ctabget(tab, crt->issuer)) == nil)
+ return -1;
+ for(ce = ci->certs; ce != nil; ce = ce->next){
+ if(vfsig(crt, ce->c) != 0)
+ continue;
+ if(vfconstraints(crt, ce->c) != 0)
+ continue;
+ if(!ce->isroot && vfcert(tab, ce->c) == -1)
+ continue;
+ return 0;
+ }
+ return -1;
+}
+
+void
+loadcerts(CertTab *tab, char *path, int isroot)
+{
+ PEMChain *pc, *e;
+ CertX509 *c;
+
+ if((pc = readcertchain(path)) == nil)
+ sysfatal("load pem chain: %r");
+ for(e = pc; e != nil; e = e->next){
+ if((c = X509decode(e->pem, e->pemlen)) == nil)
+ sysfatal("decode cert: %r");
+ addcert(tab, c, isroot);
+ }
+}
--- /dev/null
+++ b/pki.h
@@ -1,0 +1,68 @@
+typedef struct Vrf Vrf;
+typedef struct CertTab CertTab;
+typedef struct CertEnt CertEnt;
+typedef struct Issuer Issuer;
+
+
+enum State {
+ Snone,
+ Srspec,
+ Scsize,
+ Scdata,
+ Saccept,
+ Sreject,
+ Serror,
+};
+
+enum {
+ CFnone,
+ CFder,
+ CFpem,
+};
+
+enum {
+ CRnone,
+ CRhost,
+};
+
+struct CertEnt {
+ CertEnt *next;
+ CertX509 *c;
+ int isroot;
+};
+
+struct Issuer {
+ Issuer *next;
+ CertEnt *certs;
+ char *name;
+};
+
+struct CertTab {
+ CertTab *up;
+ Issuer **ent;
+ int nent;
+};
+
+struct Vrf {
+ Ref;
+ Qid qid;
+ int phase;
+ int ntype;
+ char *name;
+ int cfmt;
+
+ int rfmt;
+ int rbufsz;
+ int nrbuf;
+ uchar *rbuf;
+ int ncrt;
+ int icert;
+ CertTab *tab;
+ CertX509 *vrf;
+};
+
+extern CertTab *roottab;
+void loadcerts(CertTab*, char*, int);
+int addcert(CertTab*, CertX509*, int);
+CertTab* mktab(CertTab*, int);
+int vfcert(CertTab*, CertX509*);
--
⑨