Changes lib9p to add helpers to perform authentication for clients and to add netsrv/threadnetsrv wrappers to handle network clients. respondstrerr() does a respond of %r, and the client is single threaded by default (i.e., srv does lock to serialize calls to service procedures, as they would be if [thread]netsrv() was never called. Notes: Sun Dec 11 16:46:06 EST 2005 rsc Finally applied this, more or less. The man page documents the listen stuff, which is called listensrv instead of netsrv (it's listen+srv). The auth stuff is pretty much the same as you submitted. Let me know that it works and then I'll add it to the man page. Thanks. Russ Reference: /n/sources/patch/applied/9p-auth-and-listen Date: Wed Nov 16 17:50:14 CET 2005 Reviewed-by: rsc --- /sys/include/9p.h Wed Nov 16 17:47:47 2005 +++ /sys/include/9p.h Wed Nov 16 17:47:45 2005 @@ -208,6 +208,11 @@ QLock rlock; uchar* wbuf; QLock wlock; + + QLock* slock; /* to lock service calls */ + char* keyspec; + long authqgen; + char* addr; }; void srv(Srv*); @@ -215,6 +220,7 @@ int postfd(char*, int); int chatty9p; void respond(Req*, char*); +void responderrstr(Req*); void threadpostmountsrv(Srv*, char*, char*, int); /* @@ -230,7 +236,16 @@ OMASK = 3 }; -void readstr(Req*, char*); -void readbuf(Req*, void*, long); +void readstr(Req*, char*); +void readbuf(Req*, void*, long); void walkandclone(Req*, char*(*walk1)(Fid*,char*,void*), char*(*clone)(Fid*,Fid*,void*), void*); +void destroyauthfid(Fid* fid); +int authattach(Req* r); +void authread(Req* r); +void authsrv(Req* r); +void authwrite(Req* r); +void authopen(Req* r); + +int threadnetsrv(Srv* srv, char* addr); +int netsrv(Srv* srv, char* addr); --- /sys/man/2/9p Wed Nov 16 17:48:01 2005 +++ /sys/man/2/9p Wed Nov 16 17:47:57 2005 @@ -11,6 +11,13 @@ readstr, respond, threadpostmountsrv, +netsrv, +threadnetsrv, +authattach, +authread, +authwrite, +authopen, +destroyauthfid, srv \- 9P file service .SH SYNOPSIS .ft L @@ -52,6 +59,9 @@ int outfd; int srvfd; int nopipe; + + char* keyspec; + char* addr; } Srv; .fi .PP @@ -61,6 +71,8 @@ int srv(Srv *s) void postmountsrv(Srv *s, char *name, char *mtpt, int flag) void threadpostmountsrv(Srv *s, char *name, char *mtpt, int flag) +int threadnetsrv(Srv* srv, char* addr) +int netsrv(Srv* srv, char* addr) int postfd(char *srvname, int fd) void respond(Req *r, char *error) void readstr(Req *r, char *src) @@ -74,6 +86,17 @@ .nf .ft L .ta \w'\fLvoid* 'u +void authsrv(Req* r); +int authattach(Req* r); +void authread(Req* r); +void authwrite(Req* r); +void authopen(Req* r); +void destroyauthfid(Fid* fid); +.fi +.PP +.nf +.ft L +.ta \w'\fLvoid* 'u void* emalloc9p(ulong n) void* erealloc9p(void *v, ulong n) char* estrdup9p(char *s) @@ -217,6 +240,29 @@ .I sysfatal (see .IR perror (2)). +.PP +.I Netsrv +and +.I threadnetsrv +are wrappers (using processes created with +.IR rfork (2) +and the thread library respectively) that start a listener process to accept +network calls at +.I addr +from remote clients. Both calles return the pid for that process. The listener +creates one more process per client connection, to run +.I srv +for the client. The +.I Srv +structure used for each client is a copy of the one supplied to +.I netsrv +( or +.IR threadnetsrv ). +The +.I addr +field of +.I Srv +is set to the network address for the client on each server. .SS Service functions The functions in a .B Srv @@ -335,6 +381,25 @@ .I ARGBEGIN (see .IR arg (2)). +The helper function +.I authsrv +may be used as the +.I auth +routine to authenticate clients using +.IR factotum (4). +In this case, +.IR authattach , +.IR authread , +.IR authwrite , +.IR authopen , +and +.IR destroyauthfile +should be used to handle fids for authentication files +(those with the +.B QTAUTH +bit set in their +.B qid.type +field). .TP .I Attach The --- /sys/src/lib9p/auth.c Thu Jan 1 00:00:00 1970 +++ /sys/src/lib9p/auth.c Wed Nov 16 17:48:14 2005 @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +typedef struct Authfile Authfile; + +enum { + Incr = 16, + Qauth= 0x0100000000000000ULL, +}; + +struct Authfile { + AuthRpc *rpc; + char *uid; + int authok; + int afd; + Qid qid; +}; + +static char Eauth[]= "authentication failed"; + + +void +authsrv(Req* r) +{ + Srv* srv; + Authfile* af; + int keylen; + char* key; + int fd, afd; + + srv = r->srv; + if (srv->keyspec == nil) + srv->keyspec = "proto=p9any role=server"; + + af = nil; + afd = -1; + if (r->ifcall.uname == nil || r->ifcall.uname[0] == 0) + goto fail; + af = emalloc9p(sizeof(Authfile)); + memset(af, 0, sizeof(Authfile)); + af->qid.type = QTAUTH; + af->qid.path = Qauth | srv->authqgen++; + if(access("/mnt/factotum", 0) < 0) + if((fd = open("/srv/factotum", ORDWR)) >= 0) + mount(fd, -1, "/mnt", MBEFORE, ""); + afd = open("/mnt/factotum/rpc", ORDWR); + if (afd < 0) + goto fail; + af->afd = afd; + af->rpc = auth_allocrpc(afd); + if (af->rpc == nil) + goto fail; + key = srv->keyspec; + keylen = strlen(key); + if(auth_rpc(af->rpc, "start", key, keylen) != ARok) + goto fail; + af->uid = estrdup9p(r->ifcall.uname); + + r->afid->qid = af->qid; + r->afid->omode = ORDWR; + r->ofcall.qid = r->afid->qid; + r->afid->aux = af; + respond(r, nil); + return; +fail: + if (af){ + auth_freerpc(af->rpc); + free(af); + } + if (afd >= 0) + close(afd); + respond(r, Eauth); +} + +static long +_authread(Authfile* af, void* data, long count) +{ + AuthInfo*ai; + + switch(auth_rpc(af->rpc, "read", nil, 0)){ + case ARdone: + ai = auth_getinfo(af->rpc); + if(ai == nil) + return -1; + auth_freeAI(ai); + if (chatty9p) + fprint(2, "user %s authenticated\n", af->uid); + af->authok = 1; + count = 0; + break; + case ARok: + if(count < af->rpc->narg) + return -1; + count = af->rpc->narg; + memmove(data, af->rpc->arg, count); + break; + case ARphase: + default: + count = -1; + } + return count; +} + +int +authattach(Req* r) +{ + Authfile* af; + char buf[1]; + + if (r->afid == nil){ + respond(r, Eauth); + return -1; + } + af = r->afid->aux; + if (af == nil){ + respond(r, Eauth); + return -1; + } + if (!af->authok && _authread(af, buf, 0) != 0){ + respond(r, Eauth); + return -1; + } + if (strcmp(af->uid, r->ifcall.uname) != 0){ + respond(r, Eauth); + return -1; + } + return 0; +} + +void +authread(Req* r) +{ + Fid* fid; + Authfile*af; + long n; + + fid = r->fid; + assert(fid->qid.type == QTAUTH); + af = fid->aux; + if (af == nil){ + respond(r, "not an auth fid"); + return; + } + n = _authread(af, r->ofcall.data, r->ifcall.count); + r->ofcall.count = n; + if (n < 0) + respond(r, Eauth); + else + respond(r, nil); +} + +void +authwrite(Req* r) +{ + Fid* fid; + Authfile*af; + void* data; + long count; + + fid = r->fid; + assert(fid->qid.type == QTAUTH); + af = fid->aux; + if (af == nil){ + respond(r, "not an auth fid"); + return; + } + data = r->ifcall.data; + count= r->ifcall.count; + if (auth_rpc(af->rpc, "write", data, count) != ARok){ + respond(r, Eauth); + return; + } + r->ofcall.count = count; + respond(r, nil); +} + +void +destroyauthfid(Fid* fid) +{ + Authfile*af; + + if(fid->qid.type&QTAUTH){ + af = fid->aux; + if (af){ + auth_freerpc(af->rpc); + close(af->afd); + free(af->uid); + free(af); + } + fid->aux = nil; + } +} + +void +authopen(Req* r) +{ + if (r->fid->qid.type == QTAUTH) + r->fid->omode = r->ifcall.mode&3; + respond(r, nil); +} --- /sys/src/lib9p/mkfile Wed Nov 16 17:48:30 2005 +++ /sys/src/lib9p/mkfile Wed Nov 16 17:48:27 2005 @@ -15,6 +15,9 @@ tpost.$O\ uid.$O\ util.$O\ + auth.$O\ + netsrv.$O\ + tnetsrv.$O\ HFILES=/sys/include/9p.h post.h --- /sys/src/lib9p/netsrv.c Thu Jan 1 00:00:00 1970 +++ /sys/src/lib9p/netsrv.c Wed Nov 16 17:48:46 2005 @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +static Srv* +clonesrv(Srv* from) +{ + Srv* to; + + /* If you call leak it might seem that we leak this + * srv and all its resources. + * That's not true. netsrvproc process + * releases to and its resources, but the parent + * process reuses the variable to allocate new + * servers, and that seems to confuse leak + * for a while. + */ + to = emalloc9p(sizeof(Srv)); + *to = *from; + to->infd = to->outfd = to->srvfd = -1; + to->fpool = nil; + to->rpool = nil; + to->rbuf = to->wbuf = nil; + to->addr = nil; + memset(&to->rlock, 0, sizeof(to->rlock)); + memset(&to->wlock, 0, sizeof(to->wlock)); + + return to; +} + +Srv* +newclientsrv(Srv* s, char* adir, int* lfdp) +{ + Srv* msrv; + char ldir[40]; + int dfd; + NetConnInfo* ni; // I love these names that break indent + + *lfdp = listen(adir, ldir); + if (*lfdp < 0) + sysfatal("listen: %r"); + dfd = accept(*lfdp, ldir); + if (dfd < 0){ + fprint(2, "%s: srvlistener: %r\n", argv0); + return nil; + } + msrv = clonesrv(s); + msrv->infd = msrv->outfd = dfd; + ni = getnetconninfo(ldir, *lfdp); + if (ni){ + msrv->addr = estrdup9p(ni->raddr); + freenetconninfo(ni); + } else + msrv->addr = nil; + return msrv; +} + +static void +netsrvproc(Srv* s) +{ + if (chatty9p) + fprint(2, "%d %s: new srv: %s\n", getpid(), argv0, s->addr); + srv(s); + if (chatty9p) + fprint(2, "%d %s: exiting: %s\n", getpid(), argv0, s->addr); + close(s->infd); + free(s->addr); + free(s); + _exits(0); +} + +static void +srvlistener(Srv* s) +{ + char adir[40]; + Srv* msrv; + int afd,lfd; + + afd = announce(s->addr, adir); + if (afd < 0) + sysfatal("announce: %r"); + rendezvous((ulong)srvlistener, 0); + for(;;){ + msrv = newclientsrv(s, adir, &lfd); + switch(rfork(RFPROC|RFMEM|RFNOWAIT)){ + case -1: + sysfatal("srvlistener: %r"); + case 0: + netsrvproc(msrv); + break; + default: + close(lfd); + } + } +} + +int +netsrv(Srv* srv, char* addr) +{ + int pid; + + srv->addr = estrdup9p(addr); + srv->slock = emalloc9p(sizeof(QLock)); + memset(srv->slock, 0, sizeof(QLock)); + switch(pid = rfork(RFPROC|RFMEM|RFNAMEG|RFNOWAIT)){ + case -1: + sysfatal("netsrv: %r"); + case 0: + srvlistener(srv); + _exits(0); + default: + rendezvous((ulong)srvlistener, 0); + } + return pid; +} --- /sys/src/lib9p/ramfs.c Wed Nov 16 17:49:05 2005 +++ /sys/src/lib9p/ramfs.c Wed Nov 16 17:49:03 2005 @@ -1,163 +1,237 @@ #include #include -#include #include +#include #include #include <9p.h> -static char Ebad[] = "something bad happened"; -static char Enomem[] = "no memory"; - typedef struct Ramfile Ramfile; + struct Ramfile { - char *data; - int ndata; + char* data; + int ndata; }; -void -fsread(Req *r) -{ - Ramfile *rf; - vlong offset; - long count; - rf = r->fid->file->aux; - offset = r->ifcall.offset; - count = r->ifcall.count; +static char Ebad[] = "something bad happened"; +static char Enomem[] = "no memory"; -//print("read %ld %lld\n", *count, offset); - if(offset >= rf->ndata){ - r->ofcall.count = 0; - respond(r, nil); +static void +fsattach(Req* r) +{ + if (r->srv->auth != nil && authattach(r) < 0) return; - } - - if(offset+count >= rf->ndata) - count = rf->ndata - offset; - - memmove(r->ofcall.data, rf->data+offset, count); - r->ofcall.count = count; respond(r, nil); } -void -fswrite(Req *r) +static void +fsopen(Req* r) { - void *v; - Ramfile *rf; - vlong offset; - long count; + Ramfile*rf; + File* file; - rf = r->fid->file->aux; - offset = r->ifcall.offset; - count = r->ifcall.count; - - if(offset+count >= rf->ndata){ - v = realloc(rf->data, offset+count); - if(v == nil){ - respond(r, Enomem); - return; + if (r->fid->qid.type&QTAUTH){ + authopen(r); + return; + } + file = r->fid->file; + if (r->ifcall.mode&OTRUNC){ + rf = file->aux; + if(rf){ + rf->ndata = 0; + file->length = 0; } - rf->data = v; - rf->ndata = offset+count; - r->fid->file->length = rf->ndata; } - memmove(rf->data+offset, r->ifcall.data, count); - r->ofcall.count = count; respond(r, nil); } -void -fscreate(Req *r) +static void +fscreate(Req* r) { + File *file; + char* name; + char* uid; + int mode; Ramfile *rf; - File *f; + File* f; - if(f = createfile(r->fid->file, r->ifcall.name, r->fid->uid, r->ifcall.perm, nil)){ + file = r->fid->file; + name = r->ifcall.name; + uid = r->fid->uid; + mode = r->fid->file->mode & 0x777 & r->ifcall.perm; + mode |= (r->ifcall.perm & ~0x777); + if(f = createfile(file, name, uid, mode, 0)){ rf = emalloc9p(sizeof *rf); f->aux = rf; + closefile(r->fid->file); r->fid->file = f; r->ofcall.qid = f->qid; respond(r, nil); + } else + responderrstr(); +} + +static void +fsread(Req* r) +{ + File* file; + long count; + vlong offset; + Ramfile*rf; + + if (r->fid->qid.type&QTAUTH){ + authread(r); return; } - respond(r, Ebad); + file = r->fid->file; + rf = file->aux; + offset = r->ifcall.offset; + count = r->ifcall.count; + if (offset >= rf->ndata){ + count = 0; + } else { + if(offset+count >= rf->ndata) + count = rf->ndata - offset; + memmove(r->ofcall.data, rf->data+offset, count); + } + r->ofcall.count = count; + respond(r, nil); } -void -fsopen(Req *r) +static void +fswrite(Req* r) { - Ramfile *rf; - - rf = r->fid->file->aux; + File* file; + long count; + vlong offset; + Ramfile*rf; + void* v; - if(rf && (r->ifcall.mode&OTRUNC)){ - rf->ndata = 0; - r->fid->file->length = 0; + if (r->fid->qid.type&QTAUTH){ + authwrite(r); + return; } + file = r->fid->file; + rf = file->aux; + offset = r->ifcall.offset; + count = r->ifcall.count; + if(offset+count >= rf->ndata){ + v = realloc(rf->data, offset+count); + if(v == nil){ + respond(r, Enomem); + return; + } else { + rf->data = v; + rf->ndata = offset+count; + file->length = rf->ndata; + } + } + if (r->ofcall.count = count) + memmove(rf->data+offset, r->ifcall.data, count); + respond(r, nil); +} +static void +fswstat(Req* r) +{ + if (r->d.name && r->d.name[0] && strcmp(r->fid->file->name, r->d.name)){ + free(r->fid->file->name); + r->fid->file->name = estrdup9p(r->d.name); + } + if (r->d.uid && r->d.uid[0]){ + free(r->fid->file->uid); + r->fid->file->uid = estrdup9p(r->d.uid); + } + if (r->d.gid && r->d.gid[0]){ + free(r->fid->file->gid); + r->fid->file->gid = estrdup9p(r->d.gid); + } + if (~(ulong)r->d.mode) + r->fid->file->mode = r->d.mode; respond(r, nil); } -void -fsdestroyfile(File *f) +static Srv sfs= +{ + .auth = authsrv, + .attach = fsattach, + .open = fsopen, + .create = fscreate, + .read = fsread, + .wstat = fswstat, + .write = fswrite, + .destroyfid = destroyauthfid, +}; + +static void +freefile(File *f) { Ramfile *rf; -//fprint(2, "clunk\n"); rf = f->aux; if(rf){ + f->aux = nil; free(rf->data); free(rf); } } -Srv fs = { - .open= fsopen, - .read= fsread, - .write= fswrite, - .create= fscreate, -}; - void usage(void) { - fprint(2, "usage: ramfs [-D] [-s srvname] [-m mtpt]\n"); + fprint(2, "usage: %s [-aD] [-s srv] [-m mnt] [-n addr]\n", argv0); exits("usage"); } -void -main(int argc, char **argv) -{ - char *srvname = nil; - char *mtpt = nil; - Qid q; - - fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile); - q = fs.tree->root->qid; - +/* Example not using the thread library: + * E , x/threadmain/c/main + * E , x/threadnetsrv/c/netsrv + * E , x/threadpostmountsrv/c/postmountsrv + */ + +void +threadmain(int argc, char **argv) +{ + char* mnt; + char* srv; + char* addr; + int pid; + + srv = nil; + mnt = nil; + addr = nil; ARGBEGIN{ + case 'a': + sfs.auth = nil; + break; case 'D': chatty9p++; break; case 's': - srvname = EARGF(usage()); + srv = EARGF(usage()); break; case 'm': - mtpt = EARGF(usage()); + mnt = EARGF(usage()); + break; + case 'n': + addr = EARGF(usage()); break; default: usage(); }ARGEND; - if(argc) + if(argc!= 0) usage(); - - if(chatty9p) - fprint(2, "ramsrv.nopipe %d srvname %s mtpt %s\n", fs.nopipe, srvname, mtpt); - if(srvname == nil && mtpt == nil) - sysfatal("you should at least specify a -s or -m option"); - - postmountsrv(&fs, srvname, mtpt, MREPL|MCREATE); - exits(0); + if (srv == nil && mnt == nil && addr == nil) + mnt = "/tmp"; + if (!chatty9p) + rfork(RFNOTEG); + sfs.tree = alloctree(nil, nil, DMDIR|0755, freefile); + if (addr != nil){ + pid = threadnetsrv(&sfs, addr); + if (chatty9p) + fprint(2, "proc %d listening\n", pid); + } + if (srv != nil || mnt != nil) + threadpostmountsrv(&sfs, srv, mnt, MREPL|MCREATE); } --- /sys/src/lib9p/srv.c Wed Nov 16 17:49:29 2005 +++ /sys/src/lib9p/srv.c Sun Dec 11 22:05:39 2005 @@ -246,6 +246,7 @@ else respond(r, nil); } + static int rflush(Req *r, char *error) { @@ -463,7 +464,7 @@ respond(r, Eunknownfid); return; } - if(r->ifcall.count < 0){ + if(r->ifcall.count == ~0){ respond(r, Ebotch); return; } @@ -509,7 +510,7 @@ respond(r, Eunknownfid); return; } - if(r->ifcall.count < 0){ + if(r->ifcall.count == ~0){ respond(r, Ebotch); return; } @@ -592,6 +593,7 @@ return; } if(r->fid->file){ + rlock(r->fid->file); r->d = r->fid->file->Dir; if(r->d.name) r->d.name = estrdup9p(r->d.name); @@ -601,6 +603,7 @@ r->d.gid = estrdup9p(r->d.gid); if(r->d.muid) r->d.muid = estrdup9p(r->d.muid); + runlock(r->fid->file); } if(srv->stat) srv->stat(r); @@ -668,9 +671,15 @@ respond(r, "wstat -- attempt to change muid"); return; } - if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){ - respond(r, "wstat -- attempt to change DMDIR bit"); - return; + if((ulong)~r->d.mode){ + if (((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){ + respond(r, "wstat -- attempt to change DMDIR bit"); + return; + } + if (r->fid->file && strcmp(r->fid->file->uid, r->fid->uid) != 0){ + respond(r, "wstat -- not owner"); + return; + } } srv->wstat(r); } @@ -704,6 +713,8 @@ respond(r, r->error); continue; } + if (srv->slock) + qlock(srv->slock); switch(r->ifcall.type){ default: respond(r, "unknown message"); @@ -722,6 +733,8 @@ case Tstat: sstat(srv, r); break; case Twstat: swstat(srv, r); break; } + if (srv->slock) + qunlock(srv->slock); } free(srv->rbuf); @@ -739,11 +752,20 @@ } void +responderrstr(Req* r) +{ + char errbuf[ERRMAX]; + + seprint(errbuf, errbuf+sizeof(errbuf), "%r"); + respond(r, errbuf); +} + +void respond(Req *r, char *error) { int i, m, n; - char errbuf[ERRMAX]; Srv *srv; + char errbuf[ERRMAX]; srv = r->srv; assert(srv != nil); @@ -806,7 +828,8 @@ for(i=0; inflush; i++) respond(r->flush[i], nil); free(r->flush); - + r->flush = nil; + r->nflush= 0; if(r->pool) closereq(r); else --- /sys/src/lib9p/tnetsrv.c Thu Jan 1 00:00:00 1970 +++ /sys/src/lib9p/tnetsrv.c Wed Nov 16 17:49:51 2005 @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +extern Srv* newclientsrv(Srv*, char*, int*); // common code at netsrv.c + +static void +netsrvproc(void* a) +{ + Srv* s = a; + + if (chatty9p) + fprint(2, "%d %s: new srv: %s\n", getpid(), argv0, s->addr); + srv(s); + if (chatty9p) + fprint(2, "%d %s: exiting: %s\n", getpid(), argv0, s->addr); + close(s->infd); + free(s->addr); + free(s); + threadexits(nil); +} + +static void +srvlistener(void *a) +{ + Srv* s = a; + int afd, lfd; + char adir[40]; + Srv* msrv; + + afd = announce(s->addr, adir); + if (afd < 0) + sysfatal("announce: %r"); + rendezvous((ulong)srvlistener, getpid()); + for(;;){ + msrv = newclientsrv(s, adir, &lfd); + proccreate(netsrvproc, msrv, mainstacksize); + close(lfd); + } +} + +int +threadnetsrv(Srv* srv, char* addr) +{ + + srv->addr = estrdup9p(addr); + srv->slock = emalloc9p(sizeof(QLock)); + memset(srv->slock, 0, sizeof(QLock)); + if (procrfork(srvlistener, srv, mainstacksize, RFNAMEG) < 0) + sysfatal("netsrv: %r"); + return rendezvous((ulong)srvlistener, 0); +}