From ab73f2bbf59c2cdccc21952d9a13466793262b4b Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 4 Nov 2005 15:36:40 +0000 Subject: [PATCH] Add real factotum. --- Makefile | 19 ++-- cpu.c | 118 +++++++++++++++++-- drawterm.h | 2 + exportfs/exportfs.c | 5 +- exportfs/exportsrv.c | 1 + include/user.h | 2 + kern/Makefile | 1 + kern/devcons.c | 39 +++---- kern/devlfd.c | 126 +++++++++++++++++++++ kern/devroot.c | 60 +++++++--- kern/devssl.c | 8 +- kern/devtab.c | 4 + libauth/Makefile | 21 ++++ libauth/attr.c | 174 ++++++++++++++++++++++++++++ libauth/auth_attr.c | 13 +++ libauth/auth_challenge.c | 117 +++++++++++++++++++ libauth/auth_getuserpasswd.c | 75 +++++++++++++ libauth/auth_proxy.c | 212 +++++++++++++++++++++++++++++++++++ libauth/auth_respond.c | 73 ++++++++++++ libauth/auth_rpc.c | 116 +++++++++++++++++++ libauth/auth_userpasswd.c | 50 +++++++++ libauth/authlocal.h | 1 + libauth/httpauth.c | 51 +++++++++ main.c | 2 + posix-factotum.c | 107 ++++++++++++++++++ win32-factotum.c | 22 ++++ 26 files changed, 1361 insertions(+), 58 deletions(-) create mode 100644 kern/devlfd.c create mode 100644 libauth/Makefile create mode 100644 libauth/attr.c create mode 100644 libauth/auth_attr.c create mode 100644 libauth/auth_challenge.c create mode 100644 libauth/auth_getuserpasswd.c create mode 100644 libauth/auth_proxy.c create mode 100644 libauth/auth_respond.c create mode 100644 libauth/auth_rpc.c create mode 100644 libauth/auth_userpasswd.c create mode 100644 libauth/authlocal.h create mode 100644 libauth/httpauth.c create mode 100644 posix-factotum.c create mode 100644 win32-factotum.c diff --git a/Makefile b/Makefile index 4d164ac..9cff118 100644 --- a/Makefile +++ b/Makefile @@ -8,27 +8,23 @@ OFILES=\ readcons.$O\ secstore.$O\ latin1.$O\ + $(OS)-factotum.$O\ -LIBS=\ +LIBS1=\ kern/libkern.a\ exportfs/libexportfs.a\ + libauth/libauth.a\ libauthsrv/libauthsrv.a\ libsec/libsec.a\ libmp/libmp.a\ libmemdraw/libmemdraw.a\ libmemlayer/libmemlayer.a\ libdraw/libdraw.a\ - libc/libc.a\ - kern/libkern.a\ gui-$(GUI)/libgui.a\ - libmemdraw/libmemdraw.a\ - libdraw/libdraw.a\ - kern/libkern.a\ libc/libc.a\ - libmemdraw/libmemdraw.a\ - libmemlayer/libmemlayer.a\ - libdraw/libdraw.a\ - libmachdep.a + +# stupid gcc +LIBS=$(LIBS1) $(LIBS1) $(LIBS1) libmachdep.a $(TARG): $(OFILES) $(LIBS) $(CC) $(LDFLAGS) -o $(TARG) $(OFILES) $(LIBS) $(LDADD) @@ -45,6 +41,9 @@ kern/libkern.a: exportfs/libexportfs.a: (cd exportfs; make) +libauth/libauth.a: + (cd libauth; make) + libauthsrv/libauthsrv.a: (cd libauthsrv; make) diff --git a/cpu.c b/cpu.c index 47e68ba..213a96f 100644 --- a/cpu.c +++ b/cpu.c @@ -17,7 +17,6 @@ #define Maxfdata 8192 #define MaxStr 128 -static char* getuser(void); static void fatal(int, char*, ...); static void catcher(void*, char*); static void usage(void); @@ -98,7 +97,9 @@ cpumain(int argc, char **argv) close(fd); } - user = readcons("user", getenv("USER"), 0); + user = getenv("USER"); + if(user == nil) + user = readcons("user", nil, 0); secstoreserver = nil; ARGBEGIN{ case 'a': @@ -126,6 +127,9 @@ cpumain(int argc, char **argv) } break; */ + case 'k': + keyspec = EARGF(usage()); + break; case 'u': user = EARGF(usage()); break; @@ -136,6 +140,15 @@ cpumain(int argc, char **argv) usage(); }ARGEND; + if((fd = dialfactotum()) < 0) + fprint(2, "dial factotum: %r\n"); + else if(sysmount(fd, -1, "/mnt/factotum", MREPL, "") < 0) + fprint(2, "mount factotum: %r\n"); + else if((fd = open("/mnt/factotum/ctl", OREAD)) < 0) + fprint(2, "open /mnt/factotum/ctl: %r\n"); + else + close(fd); + if(secstoreserver == nil) secstoreserver = authserver; @@ -436,6 +449,96 @@ gettickets(Ticketreq *tr, char *key, char *trbuf, char *tbuf) return mkserverticket(tr, key, tbuf); } +/* + * prompt user for a key. don't care about memory leaks, runs standalone + */ +static Attr* +promptforkey(char *params) +{ + char *v; + int fd; + Attr *a, *attr; + char *def; + + fd = open("/dev/cons", ORDWR); + if(fd < 0) + sysfatal("opening /dev/cons: %r"); + + attr = _parseattr(params); + fprint(fd, "\n!Adding key:"); + for(a=attr; a; a=a->next) + if(a->type != AttrQuery && a->name[0] != '!') + fprint(fd, " %q=%q", a->name, a->val); + fprint(fd, "\n"); + + for(a=attr; a; a=a->next){ + v = a->name; + if(a->type != AttrQuery || v[0]=='!') + continue; + def = nil; + if(strcmp(v, "user") == 0) + def = getuser(); + a->val = readcons(v, def, 0); + if(a->val == nil) + sysfatal("user terminated key input"); + a->type = AttrNameval; + } + for(a=attr; a; a=a->next){ + v = a->name; + if(a->type != AttrQuery || v[0]!='!') + continue; + def = nil; + if(strcmp(v+1, "user") == 0) + def = getuser(); + a->val = readcons(v+1, def, 1); + if(a->val == nil) + sysfatal("user terminated key input"); + a->type = AttrNameval; + } + fprint(fd, "!\n"); + close(fd); + return attr; +} + +/* + * send a key to the mounted factotum + */ +static int +sendkey(Attr *attr) +{ + int fd, rv; + char buf[1024]; + + fd = open("/mnt/factotum/ctl", ORDWR); + if(fd < 0) + sysfatal("opening /mnt/factotum/ctl: %r"); + rv = fprint(fd, "key %A\n", attr); + read(fd, buf, sizeof buf); + close(fd); + return rv; +} + +int +askuser(char *params) +{ + Attr *attr; + + fmtinstall('A', _attrfmt); + + attr = promptforkey(params); + if(attr == nil) + sysfatal("no key supplied"); + if(sendkey(attr) < 0) + sysfatal("sending key to factotum: %r"); + return 0; +} + +AuthInfo* +p9anyfactotum(int fd, int afd) +{ + return auth_proxy(fd, askuser, "proto=p9any role=client %s", keyspec); +} + AuthInfo* p9any(int fd) { @@ -444,11 +547,14 @@ p9any(int fd) char tbuf[TICKETLEN+TICKETLEN+AUTHENTLEN], trbuf[TICKREQLEN]; char authkey[DESKEYLEN]; Authenticator auth; - int i, v2, n; + int afd, i, v2, n; Ticketreq tr; Ticket t; AuthInfo *ai; + if((afd = open("/mnt/factotum/ctl", ORDWR)) >= 0) + return p9anyfactotum(fd, afd); + if((n = readstr(fd, buf, sizeof buf)) < 0) fatal(1, "cannot read p9any negotiation"); bbuf = buf; @@ -598,9 +704,3 @@ setamalg(char *s) return setam(s); } -char* -getuser(void) -{ - return getenv("USER"); -} - diff --git a/drawterm.h b/drawterm.h index d49e797..30e0926 100644 --- a/drawterm.h +++ b/drawterm.h @@ -8,3 +8,5 @@ extern int exportfs(int, int); extern char *user; extern char *getkey(char*, char*); extern char *findkey(char**, char*); +extern int dialfactotum(void); +extern char *getuser(void); diff --git a/exportfs/exportfs.c b/exportfs/exportfs.c index f3f1630..2d4786a 100644 --- a/exportfs/exportfs.c +++ b/exportfs/exportfs.c @@ -99,7 +99,7 @@ exportfs(int fd, int msgsz) fatal("convM2S format error"); } -//iprint("<- %F\n", &r->work); +if(0) iprint("<- %F\n", &r->work); DEBUG(DFD, "%F\n", &r->work); (fcalls[r->work.type])(r); } @@ -110,6 +110,7 @@ reply(Fcall *r, Fcall *t, char *err) { uchar *data; int m, n; +static QLock lk; t->tag = r->tag; t->fid = r->fid; @@ -120,7 +121,7 @@ reply(Fcall *r, Fcall *t, char *err) else t->type = r->type + 1; -//iprint("-> %F\n", t); +if(0) iprint("-> %F\n", t); DEBUG(DFD, "\t%F\n", t); data = malloc(messagesize); /* not mallocz; no need to clear */ diff --git a/exportfs/exportsrv.c b/exportfs/exportsrv.c index 6c57bb3..cfd3c21 100644 --- a/exportfs/exportsrv.c +++ b/exportfs/exportsrv.c @@ -431,6 +431,7 @@ slave(Fsrpc *f) Proc *p; int pid; static int nproc; + static QLock lk; for(;;) { for(p = Proclist; p; p = p->next) { diff --git a/include/user.h b/include/user.h index a3e2685..b56cb15 100644 --- a/include/user.h +++ b/include/user.h @@ -50,6 +50,8 @@ extern int dirwstat(char*, Dir*); extern int dirfwstat(int, Dir*); extern long dirread(int, Dir*, long); +extern int lfdfd(int); + /* * network dialing and authentication */ diff --git a/kern/Makefile b/kern/Makefile index 0a3400e..2f3f3e4 100644 --- a/kern/Makefile +++ b/kern/Makefile @@ -13,6 +13,7 @@ OFILES=\ devfs-$(OS).$O\ devip.$O\ devip-$(OS).$O\ + devlfd.$O\ devmnt.$O\ devmouse.$O\ devpipe.$O\ diff --git a/kern/devcons.c b/kern/devcons.c index ce9fb5a..aaa56c7 100644 --- a/kern/devcons.c +++ b/kern/devcons.c @@ -175,25 +175,6 @@ print(char *fmt, ...) return n; } -int -iprint(char *fmt, ...) -{ - int n, s; - va_list arg; - char buf[PRINTSIZE]; - - s = splhi(); - va_start(arg, fmt); - n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; - va_end(arg); - if(screenputs != nil && iprintscreenputs) - screenputs(buf, n); - uartputs(buf, n); - splx(s); - - return n; -} - void panic(char *fmt, ...) { @@ -1208,3 +1189,23 @@ writebintime(char *buf, int n) } +int +iprint(char *fmt, ...) +{ + int n, s; + va_list arg; + char buf[PRINTSIZE]; + + s = splhi(); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + if(screenputs != nil && iprintscreenputs) + screenputs(buf, n); +#undef write + write(2, buf, n); + splx(s); + + return n; +} + diff --git a/kern/devlfd.c b/kern/devlfd.c new file mode 100644 index 0000000..29a1ba4 --- /dev/null +++ b/kern/devlfd.c @@ -0,0 +1,126 @@ +#include "u.h" +#include +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#undef pread +#undef pwrite + +Chan* +lfdchan(int fd) +{ + Chan *c; + + c = newchan(); + c->type = devno('L', 0); + c->aux = (void*)fd; + c->name = newcname("fd"); + c->mode = ORDWR; + c->qid.type = 0; + c->qid.path = 0; + c->qid.vers = 0; + c->dev = 0; + c->offset = 0; + return c; +} + +int +lfdfd(int fd) +{ + return newfd(lfdchan(fd)); +} + +static Chan* +lfdattach(char *x) +{ + USED(x); + + error(Egreg); + return nil; +} + +static Walkqid* +lfdwalk(Chan *c, Chan *nc, char **name, int nname) +{ + USED(c); + USED(nc); + USED(name); + USED(nname); + + error(Egreg); + return nil; +} + +static int +lfdstat(Chan *c, uchar *dp, int n) +{ + USED(c); + USED(dp); + USED(n); + error(Egreg); + return -1; +} + +static Chan* +lfdopen(Chan *c, int omode) +{ + USED(c); + USED(omode); + + error(Egreg); + return nil; +} + +static void +lfdclose(Chan *c) +{ + close((int)c->aux); +} + +static long +lfdread(Chan *c, void *buf, long n, vlong off) +{ + USED(off); /* can't pread on pipes */ + n = read((int)c->aux, buf, n); + if(n < 0){ + iprint("error %d\n", errno); + oserror(); + } + return n; +} + +static long +lfdwrite(Chan *c, void *buf, long n, vlong off) +{ + USED(off); /* can't pread on pipes */ + + n = write((int)c->aux, buf, n); + if(n < 0){ + iprint("error %d\n", errno); + oserror(); + } + return n; +} + +Dev lfddevtab = { + 'L', + "lfd", + + devreset, + devinit, + devshutdown, + lfdattach, + lfdwalk, + lfdstat, + lfdopen, + devcreate, + lfdclose, + lfdread, + devbread, + lfdwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/kern/devroot.c b/kern/devroot.c index 7821556..94b1ab7 100644 --- a/kern/devroot.c +++ b/kern/devroot.c @@ -8,9 +8,12 @@ enum { Qdir = 0, Qboot = 0x1000, + Qmnt = 0x2000, + Qfactotum, Nrootfiles = 32, Nbootfiles = 32, + Nmntfiles = 2, }; typedef struct Dirlist Dirlist; @@ -26,6 +29,7 @@ struct Dirlist static Dirtab rootdir[Nrootfiles] = { "#/", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "boot", {Qboot, 0, QTDIR}, 0, DMDIR|0555, + "mnt", {Qmnt, 0, QTDIR}, 0, DMDIR|0555, }; static uchar *rootdata[Nrootfiles]; static Dirlist rootlist = @@ -33,7 +37,7 @@ static Dirlist rootlist = 0, rootdir, rootdata, - 2, + 3, Nrootfiles }; @@ -50,6 +54,19 @@ static Dirlist bootlist = Nbootfiles }; +static Dirtab mntdir[Nmntfiles] = { + "mnt", {Qmnt, 0, QTDIR}, 0, DMDIR|0555, + "factotum", {Qfactotum, 0, QTDIR}, 0, DMDIR|0555, +}; +static Dirlist mntlist = +{ + Qmnt, + mntdir, + nil, + 2, + Nmntfiles +}; + /* * add a file to the list */ @@ -97,7 +114,6 @@ rootreset(void) addrootdir("dev"); addrootdir("env"); addrootdir("fd"); - addrootdir("mnt"); addrootdir("net"); addrootdir("net.alt"); addrootdir("proc"); @@ -129,6 +145,13 @@ rootgen(Chan *c, char *name, Dirtab *dirt, int ndirt, int s, Dir *dp) return 1; } return devgen(c, name, rootlist.dir, rootlist.ndir, s, dp); + case Qmnt: + if(s == DEVDOTDOT){ + Qid tqiddir = {Qdir, 0, QTDIR}; + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); + return 1; + } + return devgen(c, name, mntlist.dir, mntlist.ndir, s, dp); case Qboot: if(s == DEVDOTDOT){ Qid tqiddir = {Qdir, 0, QTDIR}; @@ -139,22 +162,25 @@ rootgen(Chan *c, char *name, Dirtab *dirt, int ndirt, int s, Dir *dp) default: if(s == DEVDOTDOT){ Qid tqiddir = {Qdir, 0, QTDIR}; - if((int)c->qid.path < Qboot) - devdir(c, tqiddir, "#/", 0, eve, 0555, dp); - else { - tqiddir.path = Qboot; - devdir(c, tqiddir, "#/", 0, eve, 0555, dp); - } + tqiddir.path = c->qid.path&0xF000; + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); return 1; } if(s != 0) return -1; - if((int)c->qid.path < Qboot){ + switch((int)c->qid.path & 0xF000){ + case Qdir: t = c->qid.path-1; l = &rootlist; - }else{ + break; + case Qboot: t = c->qid.path - Qboot - 1; l = &bootlist; + break; + case Qmnt: + t = c->qid.path - Qmnt - 1; + l = &mntlist; + break; } if(t >= l->ndir) return -1; @@ -209,17 +235,19 @@ rootread(Chan *c, void *buf, long n, vlong off) switch(t){ case Qdir: case Qboot: + case Qmnt: return devdirread(c, buf, n, nil, 0, rootgen); } - if(t= l->ndir) error(Egreg); diff --git a/kern/devssl.c b/kern/devssl.c index 65ba8c0..ecbf66d 100644 --- a/kern/devssl.c +++ b/kern/devssl.c @@ -765,6 +765,7 @@ sslput(Dstate *s, Block * volatile b) int offset; if(waserror()){ +iprint("error: %s\n", up->errstr); if(b != nil) free(b); nexterror(); @@ -1071,8 +1072,9 @@ sslwrite(Chan *c, void *a, long n, vlong o) nexterror(); } qlock(&s->out.q); - p = a; +if(0) iprint("write %d %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n", + n, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); e = p + n; do { m = e - p; @@ -1092,7 +1094,9 @@ sslwrite(Chan *c, void *a, long n, vlong o) p += m; } while(p < e); - + p = a; +if(0) iprint("wrote %d %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n", + n, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); poperror(); qunlock(&s->out.q); return n; diff --git a/kern/devtab.c b/kern/devtab.c index b371d26..d4efefe 100644 --- a/kern/devtab.c +++ b/kern/devtab.c @@ -12,6 +12,8 @@ extern Dev mousedevtab; extern Dev drawdevtab; extern Dev ipdevtab; extern Dev fsdevtab; +extern Dev mntdevtab; +extern Dev lfddevtab; Dev *devtab[] = { &rootdevtab, @@ -22,6 +24,8 @@ Dev *devtab[] = { &drawdevtab, &ipdevtab, &fsdevtab, + &mntdevtab, + &lfddevtab, 0 }; diff --git a/libauth/Makefile b/libauth/Makefile new file mode 100644 index 0000000..4f9b492 --- /dev/null +++ b/libauth/Makefile @@ -0,0 +1,21 @@ +ROOT=.. +include ../Make.config + +LIB=libauth.a +OFILES=\ + attr.$O\ + auth_attr.$O\ + auth_challenge.$O\ + auth_getuserpasswd.$O\ + auth_proxy.$O\ + auth_respond.$O\ + auth_rpc.$O\ + auth_userpasswd.$O\ + +$(LIB): $(OFILES) + $(AR) r $(LIB) $(OFILES) + $(RANLIB) $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/libauth/attr.c b/libauth/attr.c new file mode 100644 index 0000000..5f35750 --- /dev/null +++ b/libauth/attr.c @@ -0,0 +1,174 @@ +#include +#include +#include + +int +_attrfmt(Fmt *fmt) +{ + char *b, buf[1024], *ebuf; + Attr *a; + + ebuf = buf+sizeof buf; + b = buf; + strcpy(buf, " "); + for(a=va_arg(fmt->args, Attr*); a; a=a->next){ + if(a->name == nil) + continue; + switch(a->type){ + case AttrQuery: + b = seprint(b, ebuf, " %q?", a->name); + break; + case AttrNameval: + b = seprint(b, ebuf, " %q=%q", a->name, a->val); + break; + case AttrDefault: + b = seprint(b, ebuf, " %q:=%q", a->name, a->val); + break; + } + } + return fmtstrcpy(fmt, buf+1); +} + +Attr* +_copyattr(Attr *a) +{ + Attr **la, *na; + + na = nil; + la = &na; + for(; a; a=a->next){ + *la = _mkattr(a->type, a->name, a->val, nil); + setmalloctag(*la, getcallerpc(&a)); + la = &(*la)->next; + } + *la = nil; + return na; +} + +Attr* +_delattr(Attr *a, char *name) +{ + Attr *fa; + Attr **la; + + for(la=&a; *la; ){ + if(strcmp((*la)->name, name) == 0){ + fa = *la; + *la = (*la)->next; + fa->next = nil; + _freeattr(fa); + }else + la=&(*la)->next; + } + return a; +} + +Attr* +_findattr(Attr *a, char *n) +{ + for(; a; a=a->next) + if(strcmp(a->name, n) == 0 && a->type != AttrQuery) + return a; + return nil; +} + +void +_freeattr(Attr *a) +{ + Attr *anext; + + for(; a; a=anext){ + anext = a->next; + free(a->name); + free(a->val); + a->name = (void*)~0; + a->val = (void*)~0; + a->next = (void*)~0; + free(a); + } +} + +Attr* +_mkattr(int type, char *name, char *val, Attr *next) +{ + Attr *a; + + a = malloc(sizeof(*a)); + if(a==nil) + sysfatal("_mkattr malloc: %r"); + a->type = type; + a->name = strdup(name); + a->val = strdup(val); + if(a->name==nil || a->val==nil) + sysfatal("_mkattr malloc: %r"); + a->next = next; + setmalloctag(a, getcallerpc(&type)); + return a; +} + +static Attr* +cleanattr(Attr *a) +{ + Attr *fa; + Attr **la; + + for(la=&a; *la; ){ + if((*la)->type==AttrQuery && _findattr(a, (*la)->name)){ + fa = *la; + *la = (*la)->next; + fa->next = nil; + _freeattr(fa); + }else + la=&(*la)->next; + } + return a; +} + +Attr* +_parseattr(char *s) +{ + char *p, *t, *tok[256]; + int i, ntok, type; + Attr *a; + + s = strdup(s); + if(s == nil) + sysfatal("_parseattr strdup: %r"); + + ntok = tokenize(s, tok, nelem(tok)); + a = nil; + for(i=ntok-1; i>=0; i--){ + t = tok[i]; + if(p = strchr(t, '=')){ + *p++ = '\0'; + // if(p-2 >= t && p[-2] == ':'){ + // p[-2] = '\0'; + // type = AttrDefault; + // }else + type = AttrNameval; + a = _mkattr(type, t, p, a); + setmalloctag(a, getcallerpc(&s)); + } + else if(t[strlen(t)-1] == '?'){ + t[strlen(t)-1] = '\0'; + a = _mkattr(AttrQuery, t, "", a); + setmalloctag(a, getcallerpc(&s)); + }else{ + /* really a syntax error, but better to provide some indication */ + a = _mkattr(AttrNameval, t, "", a); + setmalloctag(a, getcallerpc(&s)); + } + } + free(s); + return cleanattr(a); +} + +char* +_strfindattr(Attr *a, char *n) +{ + a = _findattr(a, n); + if(a == nil) + return nil; + return a->val; +} + diff --git a/libauth/auth_attr.c b/libauth/auth_attr.c new file mode 100644 index 0000000..8842590 --- /dev/null +++ b/libauth/auth_attr.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include "authlocal.h" + +Attr* +auth_attr(AuthRpc *rpc) +{ + if(auth_rpc(rpc, "attr", nil, 0) != ARok) + return nil; + return _parseattr(rpc->arg); +} diff --git a/libauth/auth_challenge.c b/libauth/auth_challenge.c new file mode 100644 index 0000000..298f5f1 --- /dev/null +++ b/libauth/auth_challenge.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include "authlocal.h" + +Chalstate* +auth_challenge(char *fmt, ...) +{ + char *p; + va_list arg; + Chalstate *c; + + quotefmtinstall(); /* just in case */ + va_start(arg, fmt); + p = vsmprint(fmt, arg); + va_end(arg); + if(p == nil) + return nil; + + c = mallocz(sizeof(*c), 1); + if(c == nil){ + free(p); + return nil; + } + + if((c->afd = open("/mnt/factotum/rpc", ORDWR)) < 0){ + Error: + auth_freechal(c); + free(p); + return nil; + } + + if((c->rpc=auth_allocrpc(c->afd)) == nil + || auth_rpc(c->rpc, "start", p, strlen(p)) != ARok + || auth_rpc(c->rpc, "read", nil, 0) != ARok) + goto Error; + + if(c->rpc->narg > sizeof(c->chal)-1){ + werrstr("buffer too small for challenge"); + goto Error; + } + memmove(c->chal, c->rpc->arg, c->rpc->narg); + c->nchal = c->rpc->narg; + free(p); + return c; +} + +AuthInfo* +auth_response(Chalstate *c) +{ + int ret; + AuthInfo *ai; + + ai = nil; + if(c->afd < 0){ + werrstr("auth_response: connection not open"); + return nil; + } + if(c->resp == nil){ + werrstr("auth_response: nil response"); + return nil; + } + if(c->nresp == 0){ + werrstr("auth_response: unspecified response length"); + return nil; + } + + if(c->user){ + if(auth_rpc(c->rpc, "write", c->user, strlen(c->user)) != ARok){ + /* + * if this fails we're out of phase with factotum. + * give up. + */ + goto Out; + } + } + + if(auth_rpc(c->rpc, "write", c->resp, c->nresp) != ARok){ + /* + * don't close the connection -- maybe we'll try again. + */ + return nil; + } + + switch(ret = auth_rpc(c->rpc, "read", nil, 0)){ + case ARok: + default: + werrstr("factotum protocol botch %d %s", ret, c->rpc->ibuf); + break; + case ARdone: + ai = auth_getinfo(c->rpc); + break; + } + +Out: + close(c->afd); + auth_freerpc(c->rpc); + c->afd = -1; + c->rpc = nil; + return ai; +} + +void +auth_freechal(Chalstate *c) +{ + if(c == nil) + return; + + if(c->afd >= 0) + close(c->afd); + if(c->rpc != nil) + auth_freerpc(c->rpc); + + memset(c, 0xBB, sizeof(*c)); + free(c); +} diff --git a/libauth/auth_getuserpasswd.c b/libauth/auth_getuserpasswd.c new file mode 100644 index 0000000..4d66dce --- /dev/null +++ b/libauth/auth_getuserpasswd.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include "authlocal.h" + +enum { + ARgiveup = 100, +}; + +static int +dorpc(AuthRpc *rpc, char *verb, char *val, int len, AuthGetkey *getkey) +{ + int ret; + + for(;;){ + if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey) + return ret; + if(getkey == nil) + return ARgiveup; /* don't know how */ + if((*getkey)(rpc->arg) < 0) + return ARgiveup; /* user punted */ + } +} + +UserPasswd* +auth_getuserpasswd(AuthGetkey *getkey, char *fmt, ...) +{ + AuthRpc *rpc; + char *f[3], *p, *params; + int fd; + va_list arg; + UserPasswd *up; + + up = nil; + rpc = nil; + params = nil; + + fd = open("/mnt/factotum/rpc", ORDWR); + if(fd < 0) + goto out; + rpc = auth_allocrpc(fd); + if(rpc == nil) + goto out; + quotefmtinstall(); /* just in case */ + va_start(arg, fmt); + params = vsmprint(fmt, arg); + va_end(arg); + if(params == nil) + goto out; + + if(dorpc(rpc, "start", params, strlen(params), getkey) != ARok + || dorpc(rpc, "read", nil, 0, getkey) != ARok) + goto out; + + rpc->arg[rpc->narg] = '\0'; + if(tokenize(rpc->arg, f, 2) != 2){ + werrstr("bad answer from factotum"); + goto out; + } + up = malloc(sizeof(*up)+rpc->narg+1); + if(up == nil) + goto out; + p = (char*)&up[1]; + strcpy(p, f[0]); + up->user = p; + p += strlen(p)+1; + strcpy(p, f[1]); + up->passwd = p; + +out: + free(params); + auth_freerpc(rpc); + close(fd); + return up; +} diff --git a/libauth/auth_proxy.c b/libauth/auth_proxy.c new file mode 100644 index 0000000..186031e --- /dev/null +++ b/libauth/auth_proxy.c @@ -0,0 +1,212 @@ +#include +#include +#include +#include +#include "authlocal.h" + +enum { + ARgiveup = 100, +}; + +static uchar* +gstring(uchar *p, uchar *ep, char **s) +{ + uint n; + + if(p == nil) + return nil; + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ; + if(p+n > ep) + return nil; + *s = malloc(n+1); + memmove((*s), p, n); + (*s)[n] = '\0'; + p += n; + return p; +} + +static uchar* +gcarray(uchar *p, uchar *ep, uchar **s, int *np) +{ + uint n; + + if(p == nil) + return nil; + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ; + if(p+n > ep) + return nil; + *s = malloc(n); + if(*s == nil) + return nil; + memmove((*s), p, n); + *np = n; + p += n; + return p; +} + +void +auth_freeAI(AuthInfo *ai) +{ + if(ai == nil) + return; + free(ai->cuid); + free(ai->suid); + free(ai->cap); + free(ai->secret); + free(ai); +} + +static uchar* +convM2AI(uchar *p, int n, AuthInfo **aip) +{ + uchar *e = p+n; + AuthInfo *ai; + + ai = mallocz(sizeof(*ai), 1); + if(ai == nil) + return nil; + + p = gstring(p, e, &ai->cuid); + p = gstring(p, e, &ai->suid); + p = gstring(p, e, &ai->cap); + p = gcarray(p, e, &ai->secret, &ai->nsecret); + if(p == nil) + auth_freeAI(ai); + else + *aip = ai; + return p; +} + +AuthInfo* +auth_getinfo(AuthRpc *rpc) +{ + AuthInfo *a; + + if(auth_rpc(rpc, "authinfo", nil, 0) != ARok) + return nil; + if(convM2AI((uchar*)rpc->arg, rpc->narg, &a) == nil){ + werrstr("bad auth info from factotum"); + return nil; + } + return a; +} + +static int +dorpc(AuthRpc *rpc, char *verb, char *val, int len, AuthGetkey *getkey) +{ + int ret; + + for(;;){ + if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey) + return ret; + if(getkey == nil) + return ARgiveup; /* don't know how */ + if((*getkey)(rpc->arg) < 0) + return ARgiveup; /* user punted */ + } +} + +/* + * this just proxies what the factotum tells it to. + */ +AuthInfo* +fauth_proxy(int fd, AuthRpc *rpc, AuthGetkey *getkey, char *params) +{ + char *buf; + int m, n, ret; + AuthInfo *a; + char oerr[ERRMAX]; + + rerrstr(oerr, sizeof oerr); + werrstr("UNKNOWN AUTH ERROR"); + + if(dorpc(rpc, "start", params, strlen(params), getkey) != ARok){ + werrstr("fauth_proxy start: %r"); + return nil; + } + + buf = malloc(AuthRpcMax); + if(buf == nil) + return nil; + for(;;){ + switch(dorpc(rpc, "read", nil, 0, getkey)){ + case ARdone: + free(buf); + a = auth_getinfo(rpc); + errstr(oerr, sizeof oerr); /* no error, restore whatever was there */ + return a; + case ARok: + if(write(fd, rpc->arg, rpc->narg) != rpc->narg){ + werrstr("auth_proxy write fd: %r"); + goto Error; + } + break; + case ARphase: + n = 0; + memset(buf, 0, AuthRpcMax); + while((ret = dorpc(rpc, "write", buf, n, getkey)) == ARtoosmall){ + if(atoi(rpc->arg) > AuthRpcMax) + break; + m = read(fd, buf+n, atoi(rpc->arg)-n); + if(m <= 0){ + if(m == 0) + werrstr("auth_proxy short read: %s", buf); + goto Error; + } + n += m; + } + if(ret != ARok){ + werrstr("auth_proxy rpc write: %s: %r", buf); + goto Error; + } + break; + default: + werrstr("auth_proxy rpc: %r"); + goto Error; + } + } +Error: + free(buf); + return nil; +} + +AuthInfo* +auth_proxy(int fd, AuthGetkey *getkey, char *fmt, ...) +{ + int afd; + char *p; + va_list arg; + AuthInfo *ai; + AuthRpc *rpc; + + quotefmtinstall(); /* just in case */ + va_start(arg, fmt); + p = vsmprint(fmt, arg); + va_end(arg); + + afd = open("/mnt/factotum/rpc", ORDWR); + if(afd < 0){ + werrstr("opening /mnt/factotum/rpc: %r"); + free(p); + return nil; + } + + rpc = auth_allocrpc(afd); + if(rpc == nil){ + free(p); + return nil; + } + + ai = fauth_proxy(fd, rpc, getkey, p); + free(p); + auth_freerpc(rpc); + close(afd); + return ai; +} + diff --git a/libauth/auth_respond.c b/libauth/auth_respond.c new file mode 100644 index 0000000..910f06b --- /dev/null +++ b/libauth/auth_respond.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include "authlocal.h" + +enum { + ARgiveup = 100, +}; + +static int +dorpc(AuthRpc *rpc, char *verb, char *val, int len, AuthGetkey *getkey) +{ + int ret; + + for(;;){ + if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey) + return ret; + if(getkey == nil) + return ARgiveup; /* don't know how */ + if((*getkey)(rpc->arg) < 0) + return ARgiveup; /* user punted */ + } +} + +int +auth_respond(void *chal, uint nchal, char *user, uint nuser, void *resp, uint nresp, AuthGetkey *getkey, char *fmt, ...) +{ + char *p, *s; + va_list arg; + int afd; + AuthRpc *rpc; + Attr *a; + + if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0) + return -1; + + if((rpc = auth_allocrpc(afd)) == nil){ + close(afd); + return -1; + } + + quotefmtinstall(); /* just in case */ + va_start(arg, fmt); + p = vsmprint(fmt, arg); + va_end(arg); + + if(p==nil + || dorpc(rpc, "start", p, strlen(p), getkey) != ARok + || dorpc(rpc, "write", chal, nchal, getkey) != ARok + || dorpc(rpc, "read", nil, 0, getkey) != ARok){ + free(p); + close(afd); + auth_freerpc(rpc); + return -1; + } + free(p); + + if(rpc->narg < nresp) + nresp = rpc->narg; + memmove(resp, rpc->arg, nresp); + + if((a = auth_attr(rpc)) != nil + && (s = _strfindattr(a, "user")) != nil && strlen(s) < nuser) + strcpy(user, s); + else if(nuser > 0) + user[0] = '\0'; + + _freeattr(a); + close(afd); + auth_freerpc(rpc); + return nresp; +} diff --git a/libauth/auth_rpc.c b/libauth/auth_rpc.c new file mode 100644 index 0000000..4333a73 --- /dev/null +++ b/libauth/auth_rpc.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include "authlocal.h" + +static struct { + char *verb; + int val; +} tab[] = { + "ok", ARok, + "done", ARdone, + "error", ARerror, + "needkey", ARneedkey, + "badkey", ARbadkey, + "phase", ARphase, + "toosmall", ARtoosmall, + "error", ARerror, +}; + +static int +classify(char *buf, uint n, AuthRpc *rpc) +{ + int i, len; + + for(i=0; i= len && memcmp(buf, tab[i].verb, len) == 0 && (n==len || buf[len]==' ')){ + if(n==len){ + rpc->narg = 0; + rpc->arg = ""; + }else{ + rpc->narg = n - (len+1); + rpc->arg = (char*)buf+len+1; + } + return tab[i].val; + } + } + werrstr("malformed rpc response: %s", buf); + return ARrpcfailure; +} + +AuthRpc* +auth_allocrpc(int afd) +{ + AuthRpc *rpc; + + rpc = mallocz(sizeof(*rpc), 1); + if(rpc == nil) + return nil; + rpc->afd = afd; + return rpc; +} + +void +auth_freerpc(AuthRpc *rpc) +{ + free(rpc); +} + +uint +auth_rpc(AuthRpc *rpc, char *verb, void *a, int na) +{ + int l, n, type; + char *f[4]; + + l = strlen(verb); + if(na+l+1 > AuthRpcMax){ + werrstr("rpc too big"); + return ARtoobig; + } + + memmove(rpc->obuf, verb, l); + rpc->obuf[l] = ' '; + memmove(rpc->obuf+l+1, a, na); + if((n=write(rpc->afd, rpc->obuf, l+1+na)) != l+1+na){ + if(n >= 0) + werrstr("auth_rpc short write"); + return ARrpcfailure; + } + + if((n=read(rpc->afd, rpc->ibuf, AuthRpcMax)) < 0) + return ARrpcfailure; + rpc->ibuf[n] = '\0'; + + /* + * Set error string for good default behavior. + */ + switch(type = classify(rpc->ibuf, n, rpc)){ + default: + werrstr("unknown rpc type %d (bug in auth_rpc.c)", type); + break; + case ARok: + break; + case ARrpcfailure: + break; + case ARerror: + if(rpc->narg == 0) + werrstr("unspecified rpc error"); + else + werrstr("%s", rpc->arg); + break; + case ARneedkey: + werrstr("needkey %s", rpc->arg); + break; + case ARbadkey: + if(getfields(rpc->arg, f, nelem(f), 0, "\n") < 2) + werrstr("badkey %s", rpc->arg); + else + werrstr("badkey %s", f[1]); + break; + case ARphase: + werrstr("phase error %s", rpc->arg); + break; + } + return type; +} diff --git a/libauth/auth_userpasswd.c b/libauth/auth_userpasswd.c new file mode 100644 index 0000000..34a38c2 --- /dev/null +++ b/libauth/auth_userpasswd.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include "authlocal.h" + +/* + * compute the proper response. We encrypt the ascii of + * challenge number, with trailing binary zero fill. + * This process was derived empirically. + * this was copied from inet's guard. + */ +static void +netresp(char *key, long chal, char *answer) +{ + uchar buf[8]; + + memset(buf, 0, 8); + sprint((char *)buf, "%lud", chal); + if(encrypt(key, buf, 8) < 0) + abort(); + chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3]; + sprint(answer, "%.8lux", chal); +} + +AuthInfo* +auth_userpasswd(char *user, char *passwd) +{ + char key[DESKEYLEN], resp[16]; + AuthInfo *ai; + Chalstate *ch; + + /* + * Probably we should have a factotum protocol + * to check a raw password. For now, we use + * p9cr, which is simplest to speak. + */ + if((ch = auth_challenge("user=%q proto=p9cr role=server", user)) == nil) + return nil; + + passtokey(key, passwd); + netresp(key, atol(ch->chal), resp); + memset(key, 0, sizeof key); + + ch->resp = resp; + ch->nresp = strlen(resp); + ai = auth_response(ch); + auth_freechal(ch); + return ai; +} diff --git a/libauth/authlocal.h b/libauth/authlocal.h new file mode 100644 index 0000000..2c52bf8 --- /dev/null +++ b/libauth/authlocal.h @@ -0,0 +1 @@ +extern AuthInfo* _fauth_proxy(int fd, AuthRpc *rpc, AuthGetkey *getkey, char *params); diff --git a/libauth/httpauth.c b/libauth/httpauth.c new file mode 100644 index 0000000..9d1b0d2 --- /dev/null +++ b/libauth/httpauth.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +/* deprecated. + This is the mechanism that put entries in /sys/lib/httpd.rewrite + and passwords on the authserver in /sys/lib/httppasswords, which + was awkward to administer. Instead, use local .httplogin files, + which are implemented in sys/src/cmd/ip/httpd/authorize.c */ + +int +httpauth(char *name, char *password) +{ + int afd; + Ticketreq tr; + Ticket t; + char key[DESKEYLEN]; + char buf[512]; + + afd = authdial(nil, nil); + if(afd < 0) + return -1; + + /* send ticket request to AS */ + memset(&tr, 0, sizeof(tr)); + strcpy(tr.uid, name); + tr.type = AuthHttp; + convTR2M(&tr, buf); + if(write(afd, buf, TICKREQLEN) != TICKREQLEN){ + close(afd); + return -1; + } + if(_asrdresp(afd, buf, TICKETLEN) < 0){ + close(afd); + return -1; + } + close(afd); + + /* + * use password and try to decrypt the + * ticket. If it doesn't work we've got a bad password, + * give up. + */ + passtokey(key, password); + convM2T(buf, &t, key); + if(t.num != AuthHr || strcmp(t.cuid, tr.uid)) + return -1; + + return 0; +} diff --git a/main.c b/main.c index 5ccc081..c4e94ee 100644 --- a/main.c +++ b/main.c @@ -36,6 +36,8 @@ main(int argc, char **argv) char buf[1024], *s; int n; + eve = getuser(); + sizebug(); fmtinstall('r', errfmt); diff --git a/posix-factotum.c b/posix-factotum.c new file mode 100644 index 0000000..5fd760c --- /dev/null +++ b/posix-factotum.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "drawterm.h" + +#undef socket +#undef connect +#undef getenv +#undef access + +char* +getuser(void) +{ + static char user[64]; + struct passwd *pw; + + pw = getpwuid(getuid()); + if(pw == nil) + return "none"; + strecpy(user, user+sizeof user, pw->pw_name); + return user; +} +/* + * Absent other hints, it works reasonably well to use + * the X11 display name as the name space identifier. + * This is how sam's B has worked since the early days. + * Since most programs using name spaces are also using X, + * this still seems reasonable. Terminal-only sessions + * can set $NAMESPACE. + */ +static char* +nsfromdisplay(void) +{ + int fd; + Dir *d; + char *disp, *p; + + if((disp = getenv("DISPLAY")) == nil){ + werrstr("$DISPLAY not set"); + return nil; + } + + /* canonicalize: xxx:0.0 => xxx:0 */ + p = strrchr(disp, ':'); + if(p){ + p++; + while(isdigit((uchar)*p)) + p++; + if(strcmp(p, ".0") == 0) + *p = 0; + } + + return smprint("/tmp/ns.%s.%s", getuser(), disp); +} + +char* +getns(void) +{ + char *ns; + + ns = getenv("NAMESPACE"); + if(ns == nil) + ns = nsfromdisplay(); + if(ns == nil){ + werrstr("$NAMESPACE not set, %r"); + return nil; + } + return ns; +} + +int +dialfactotum(void) +{ + int fd; + struct sockaddr_un su; + char *name; + + name = smprint("%s/factotum", getns()); + + if(name == nil || access(name, 0) < 0) + return -1; + memset(&su, 0, sizeof su); + su.sun_family = AF_UNIX; + if(strlen(name)+1 > sizeof su.sun_path){ + werrstr("socket name too long"); + return -1; + } + strcpy(su.sun_path, name); + if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ + werrstr("socket: %r"); + return -1; + } + if(connect(fd, (struct sockaddr*)&su, sizeof su) < 0){ + werrstr("connect %s: %r", name); + close(fd); + return -1; + } + + return lfdfd(fd); +} + diff --git a/win32-factotum.c b/win32-factotum.c new file mode 100644 index 0000000..91f71b7 --- /dev/null +++ b/win32-factotum.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include +#include +#include "drawterm.h" + +#undef getenv + +char* +getuser(void) +{ + return getenv("USER"); +} + +int +dialfactotum(void) +{ + return -1; +} +