ref: 065d312a1728a00c7a12f8cc64f05b8b210d8426
dir: /fs.c/
#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);
}