shithub: pki

Download patch

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*);
--