shithub: pki

ref: a7d243ef71a1b27166c158ba148da2363402f328
dir: /fs.c/

View raw version
#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(c == nil)
		return Sreject;
	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 'n':	mntname = EARGF(usage());	break;
	case 's':	srvname = EARGF(usage());	break;
	case 'r':	loadroots(EARGF(usage()));	break;
	case 't':	loadthumbs(EARGF(usage()));	break;
	case 'd':	if(debug++) chatty9p++;		break;
	default:	usage();			break;
	}ARGEND;

	postmountsrv(&pkisrv, srvname, mntname, MREPL);
}