ref: 6ee1f2deb589851d71b7ea4eca3ef21135bd1569
dir: /factotum.c/
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include <mp.h>
#include <libsec.h>
static char *user;
static Attr *keyring[32];
static Attr*
findkey(char *key, char *value)
{
int i;
char *p;
for(i = 0; i < nelem(keyring); i++){
if(keyring[i] == nil)
continue;
if((p = _strfindattr(keyring[i], key)) == nil)
continue;
if(strcmp(p, value) != 0)
continue;
return keyring[i];
}
return nil;
}
static char*
setkey(char *key, char *value, Attr *new)
{
int i;
Attr **empty;
char *p;
empty = nil;
for(i = 0; i < nelem(keyring); i++){
if(empty == nil && keyring[i] == nil)
empty = &keyring[i];
if((p = _strfindattr(keyring[i], key)) == nil)
continue;
if(strcmp(p, value) != 0)
continue;
_freeattr(keyring[i]);
keyring[i] = _copyattr(new);
return nil;
}
if(empty == nil)
return "keyring full";
*empty = _copyattr(new);
return nil;
}
static char*
delkey(char *key, char *value)
{
int i;
char *p;
for(i = 0; i < nelem(keyring); i++){
if(keyring[i] == nil)
continue;
if((p = _strfindattr(keyring[i], key)) == nil)
continue;
if(strcmp(p, value) != 0)
continue;
_freeattr(keyring[i]);
keyring[i] = nil;
return nil;
}
return "no such key";
}
static long
dumpkey(char *buf, long n)
{
int i;
char *e, *p;
e = buf + n;
p = buf;
for(i = 0; i < nelem(keyring); i++){
if(keyring[i] == nil)
continue;
p = seprint(p, e, "%A\n", keyring[i]);
}
return p - buf;
}
enum {
Sneedparam,
Shaveparam,
};
typedef struct State State;
struct State {
uchar buf[SHA2_256dlen];
Attr *creds;
uint phase;
};
static uint
aws4write(State *s, char *v, uint l)
{
char input[512];
char *args[3];
int n;
char buf[256];
switch(s->phase){
case Shaveparam:
werrstr("%s", "read the results");
return ARphase;
case Sneedparam:
/* date region service */
snprint(input, sizeof input, "%.*s", (int)l, v);
n = tokenize(input, args, nelem(args));
if(n != nelem(args)){
werrstr("%s", "invalid rpc format");
return ARerror;
}
/* All lights are green */
snprint(buf, sizeof buf, "AWS4%s", _strfindattr(s->creds, "!secret"));
hmac_sha2_256((uchar*)args[0], strlen(args[0]), (uchar*)buf, strlen(buf), s->buf, nil);
hmac_sha2_256((uchar*)args[1], strlen(args[1]), s->buf, SHA2_256dlen, (uchar*)buf, nil);
hmac_sha2_256((uchar*)args[2], strlen(args[2]), (uchar*)buf, SHA2_256dlen, s->buf, nil);
hmac_sha2_256((uchar*)"aws4_request", 12, s->buf, SHA2_256dlen, s->buf, nil);
memset(input, 0, sizeof input);
memset(buf, 0, sizeof buf);
s->phase = Shaveparam;
return ARok;
}
return ARrpcfailure;
}
static uint
aws4read(State *s, char *v, uint *l)
{
switch(s->phase){
case Sneedparam:
werrstr("%s", "write the params");
return ARphase;
case Shaveparam:
assert(*l > SHA2_256dlen);
*l = SHA2_256dlen;
memmove(v, s->buf, SHA2_256dlen);
memset(s->buf, 0, SHA2_256dlen);
s->phase = Sneedparam;
return ARok;
}
return ARrpcfailure;
}
enum {
Qroot,
Qrpc,
Qctl,
Nqid,
Qfile = Qrpc,
};
static int
attrfmt(Fmt *fmt)
{
Attr *a;
int first = 1;
for(a=va_arg(fmt->args, Attr*); a != nil; a=a->next){
if(a->name == nil)
continue;
switch(a->type){
default:
continue;
case AttrQuery:
fmtprint(fmt, first+" %q?", a->name);
break;
case AttrNameval:
case AttrDefault:
if(a->name[0] == '!')
fmtprint(fmt, first+" %q=", a->name);
else
fmtprint(fmt, first+" %q=%q", a->name, a->val);
break;
}
first = 0;
}
return 0;
}
struct {
char *name;
int mode;
int type;
} qtab[Nqid] = {
"/",
DMDIR|0500,
QTDIR,
"rpc",
0600,
0,
"ctl",
0600,
0,
};
static int
dirgen(int n, Dir *dir, void*)
{
n += Qfile; /* offset to make dirread9p happy */
if(n >= Nqid)
return -1;
dir->name = estrdup9p(qtab[n].name);
dir->uid = estrdup9p(user);
dir->gid = estrdup9p(user);
dir->muid = estrdup9p("");
dir->qid = (Qid){n, 0, qtab[n].type};
dir->mode = qtab[n].mode;
return 0;
}
static void
fsstat(Req *r)
{
int path;
path = r->fid->qid.path;
assert(r->fid->qid.path < Nqid);
dirgen(path-Qfile, &r->d, nil);
respond(r, nil);
}
char Enonexist[] = "file does not exist";
char Ewalk[] = "walk in non directory";
static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
int i;
ulong path;
path = fid->qid.path;
switch(path){
case Qroot:
if(strcmp(name, "..") == 0)
name = "/";
for(i = path; i<Nqid; i++){
if(strcmp(name, qtab[i].name) != 0)
continue;
*qid = (Qid){i, 0, qtab[i].type};
fid->qid = *qid;
return nil;
}
return Enonexist;
default:
return Ewalk;
}
}
enum {
FSwrite,
FSread,
};
typedef struct Xfid Xfid;
struct Xfid {
State proto;
uint expect;
uint nmsg;
char msg[512];
};
static void
fsopen(Req *r)
{
if(r->fid->qid.path == Qrpc)
r->fid->aux = mallocz(sizeof(Xfid), 1);
respond(r, nil);
}
static void
fsclose(Fid *f)
{
if(f->qid.path == Qrpc)
free(f->aux);
}
static void
fsread(Req *r)
{
ulong path;
char buf[1024];
long n;
Xfid *x;
path = r->fid->qid.path;
switch(path){
case Qroot:
dirread9p(r, dirgen, nil);
respond(r, nil);
break;
case Qctl:
n = dumpkey(buf, sizeof buf);
readbuf(r, buf, n);
respond(r, nil);
break;
case Qrpc:
x = r->fid->aux;
if(x->expect != FSread){
respond(r, "error read without a rpc verb write first");
break;
}
/* more of a pipe... */
r->ifcall.offset = 0;
readbuf(r, x->msg, x->nmsg);
respond(r, nil);
x->expect = FSwrite;
break;
default:
respond(r, "not implemented");
break;
}
}
static void
fswrite(Req *r)
{
ulong path;
char buf[512];
char *p;
Xfid *x;
uint res;
char outbuf[512];
uint noutbuf;
Attr *new, *k;
path = r->fid->qid.path;
r->ofcall.count = snprint(buf, sizeof buf, "%.*s", r->ifcall.count, r->ifcall.data);
switch(path){
case Qctl:
if(strncmp(buf, "key ", 4) == 0){
new = _parseattr(buf+4);
if((p = _strfindattr(new, "proto")) == nil || strcmp(p, "aws4") != 0)
respond(r, "proto!=aws4");
else if((p = _strfindattr(new, "!secret")) == nil || strlen(p) == 0)
respond(r, "no !secret=");
else if((p = _strfindattr(new, "access")) == nil || strlen(p) == 0)
respond(r, "no access=");
else {
respond(r, setkey("access", p, new));
_freeattr(new);
}
} else if(strncmp(buf, "delkey ", 7) == 0){
new = _parseattr(buf+7);
p = _strfindattr(new, "access");
if(p == nil || strlen(p) == 0){
respond(r, "access= not specified");
return;
}
respond(r, delkey("access", p));
} else {
respond(r, "unknown ctl msg");
}
memset(buf, 0, sizeof buf);
break;
case Qrpc:
x = r->fid->aux;
if(x->expect != FSwrite){
respond(r, "error write while there's data for you to read");
return;
}
if(strncmp(buf, "start ", 6) == 0){
new = _parseattr(buf+6);
p = _strfindattr(new, "access");
if(p == nil || strlen(p) == 0){
werrstr("%s", "no access=");
res = ARerror;
} else if((k = findkey("access", p)) == nil){
werrstr("%s", "can not find matching key");
res = ARerror;
} else {
x->proto.creds = k;
res = ARok;
}
} else if(strncmp(buf, "write ", 6) == 0)
res = aws4write(&x->proto, buf+6, r->ifcall.count-6);
else if(strncmp(buf, "read", 4) == 0){
noutbuf = sizeof outbuf;
res = aws4read(&x->proto, outbuf, &noutbuf);
if(res == ARok){
x->nmsg = 2+1+SHA2_256dlen;
memcpy(x->msg, "ok ", 3);
memcpy(x->msg+3, outbuf, SHA2_256dlen);
respond(r, nil);
x->expect = FSread;
return;
}
} else {
respond(r, "invalid rpc verb");
return;
}
switch(res){
case ARok:
x->nmsg = snprint(x->msg, sizeof x->msg, "ok");
break;
case ARphase:
x->nmsg = snprint(x->msg, sizeof x->msg, "phase %r");
break;
default:
x->nmsg = snprint(x->msg, sizeof x->msg, "error %r");
break;
}
x->expect = FSread;
respond(r, nil);
break;
default:
respond(r, "not implemented");
}
}
static void
fsattach(Req *r)
{
r->fid->qid = (Qid){Qroot, 0, QTDIR};
r->ofcall.qid = r->fid->qid;
respond(r, nil);
}
Srv fs = {
.read=fsread,
.write=fswrite,
.attach=fsattach,
.stat=fsstat,
.walk1=fswalk1,
.open=fsopen,
.destroyfid=fsclose,
};
/* copied from auth/factotum */
/* don't allow other processes to debug us and steal keys */
static void
private(void)
{
int fd;
char buf[32];
static char pmsg[] = "Warning! %s can't protect itself from debugging: %r\n";
static char smsg[] = "Warning! %s can't turn off swapping: %r\n";
snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
fd = open(buf, OWRITE|OCEXEC);
if(fd < 0){
fprint(2, pmsg, argv0);
return;
}
if(fprint(fd, "private") < 0)
fprint(2, pmsg, argv0);
if(fprint(fd, "noswap") < 0)
fprint(2, smsg, argv0);
close(fd);
}
_Noreturn void
usage(void)
{
fprint(2, "Usage: %s [-D] [-s srv] [-m mntpt]\n", argv0);
exits("usage");
}
void
main(int argc, char **argv)
{
char *srv, *mntpt;
int doprivate;
srv = nil;
mntpt = "/mnt/factotum";
doprivate = 1;
ARGBEGIN{
case 'D':
chatty9p++;
break;
case 's':
srv = EARGF(usage());
break;
case 'm':
mntpt = EARGF(usage());
break;
case 'p':
doprivate = 0;
break;
default:
usage();
}ARGEND
if(doprivate)
private();
user = getenv("user");
if(user == nil)
sysfatal("no $user");
quotefmtinstall();
fmtinstall('A', attrfmt);
postmountsrv(&fs, srv, mntpt, MBEFORE);
exits(nil);
}