commit 2277c5d7bbe1f9595fad512d8f790708473a9bf1 from: rsc date: Sun Mar 21 04:33:13 2004 UTC Small tweaks Lots of new code imported. commit - a770daa795754cb600ad3fab2fdd2961147006c4 commit + 2277c5d7bbe1f9595fad512d8f790708473a9bf1 blob - /dev/null blob + 6703d607b5eba0827fc287ae394f0b16b56fa139 (mode 644) --- /dev/null +++ include/9p.h @@ -0,0 +1,244 @@ +#ifndef __9P_H__ +#define __9P_H__ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +#pragma src "/sys/src/lib9p" +#pragma lib "lib9p.a" +*/ + +/* + * Maps from ulongs to void*s. + */ +typedef struct Intmap Intmap; + +Intmap* allocmap(void (*inc)(void*)); +void freemap(Intmap*, void (*destroy)(void*)); +void* lookupkey(Intmap*, ulong); +void* insertkey(Intmap*, ulong, void*); +int caninsertkey(Intmap*, ulong, void*); +void* deletekey(Intmap*, ulong); + +/* + * Fid and Request structures. + */ +typedef struct Fid Fid; +typedef struct Req Req; +typedef struct Fidpool Fidpool; +typedef struct Reqpool Reqpool; +typedef struct File File; +typedef struct Filelist Filelist; +typedef struct Tree Tree; +typedef struct Readdir Readdir; +typedef struct Srv Srv; + +struct Fid +{ + ulong fid; + char omode; /* -1 = not open */ + File* file; + char* uid; + Qid qid; + void* aux; + +/* below is implementation-specific; don't use */ + Readdir* rdir; + Ref ref; + Fidpool* pool; + vlong diroffset; + long dirindex; +}; + +struct Req +{ + ulong tag; + void* aux; + Fcall ifcall; + Fcall ofcall; + Dir d; + Req* oldreq; + Fid* fid; + Fid* afid; + Fid* newfid; + Srv* srv; + +/* below is implementation-specific; don't use */ + QLock lk; + Ref ref; + Reqpool* pool; + uchar* buf; + uchar type; + uchar responded; + char* error; + void* rbuf; + Req** flush; + int nflush; +}; + +/* + * Pools to maintain Fid <-> fid and Req <-> tag maps. + */ + +struct Fidpool { + Intmap *map; + void (*destroy)(Fid*); + Srv *srv; +}; + +struct Reqpool { + Intmap *map; + void (*destroy)(Req*); + Srv *srv; +}; + +Fidpool* allocfidpool(void (*destroy)(Fid*)); +void freefidpool(Fidpool*); +Fid* allocfid(Fidpool*, ulong); +Fid* lookupfid(Fidpool*, ulong); +void closefid(Fid*); +Fid* removefid(Fidpool*, ulong); + +Reqpool* allocreqpool(void (*destroy)(Req*)); +void freereqpool(Reqpool*); +Req* allocreq(Reqpool*, ulong); +Req* lookupreq(Reqpool*, ulong); +void closereq(Req*); +Req* removereq(Reqpool*, ulong); + +typedef int Dirgen(int, Dir*, void*); +void dirread9p(Req*, Dirgen*, void*); + +/* + * File trees. + */ +struct File { + Ref ref; + Dir dir; + File *parent; + void *aux; + +/* below is implementation-specific; don't use */ + RWLock rwlock; + Filelist *filelist; + Tree *tree; + int nchild; + int allocd; +}; + +struct Tree { + File *root; + void (*destroy)(File *file); + +/* below is implementation-specific; don't use */ + Lock genlock; + ulong qidgen; + ulong dirqidgen; +}; + +Tree* alloctree(char*, char*, ulong, void(*destroy)(File*)); +void freetree(Tree*); +File* createfile(File*, char*, char*, ulong, void*); +int removefile(File*); +void closefile(File*); +File* walkfile(File*, char*); +Readdir* opendirfile(File*); +long readdirfile(Readdir*, uchar*, long); +void closedirfile(Readdir*); + +/* + * Kernel-style command parser + */ +typedef struct Cmdbuf Cmdbuf; +typedef struct Cmdtab Cmdtab; +Cmdbuf* parsecmd(char *a, int n); +void respondcmderror(Req*, Cmdbuf*, char*, ...); +Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int); +/* +#pragma varargck argpos respondcmderr 3 +*/ +struct Cmdbuf +{ + char *buf; + char **f; + int nf; +}; + +struct Cmdtab +{ + int index; /* used by client to switch on result */ + char *cmd; /* command name */ + int narg; /* expected #args; 0 ==> variadic */ +}; + +/* + * File service loop. + */ +struct Srv { + Tree* tree; + void (*destroyfid)(Fid*); + void (*destroyreq)(Req*); + void (*end)(Srv*); + void* aux; + + void (*attach)(Req*); + void (*auth)(Req*); + void (*open)(Req*); + void (*create)(Req*); + void (*read)(Req*); + void (*write)(Req*); + void (*remove)(Req*); + void (*flush)(Req*); + void (*stat)(Req*); + void (*wstat)(Req*); + void (*walk)(Req*); + char* (*clone)(Fid*, Fid*); + char* (*walk1)(Fid*, char*, Qid*); + + int infd; + int outfd; + int nopipe; + int srvfd; + int leavefdsopen; /* magic for acme win */ + +/* below is implementation-specific; don't use */ + Fidpool* fpool; + Reqpool* rpool; + uint msize; + + uchar* rbuf; + QLock rlock; + uchar* wbuf; + QLock wlock; +}; + +void srv(Srv*); +void postmountsrv(Srv*, char*, char*, int); +int postfd(char*, int); +int chatty9p; +void respond(Req*, char*); +void threadpostmountsrv(Srv*, char*, char*, int); + +/* + * Helper. Assumes user is same as group. + */ +int hasperm(File*, char*, int); + +void* emalloc9p(ulong); +void* erealloc9p(void*, ulong); +char* estrdup9p(char*); + +enum { + OMASK = 3 +}; + +void readstr(Req*, char*); +void readbuf(Req*, void*, long); +void walkandclone(Req*, char*(*walk1)(Fid*,char*,void*), char*(*clone)(Fid*,Fid*,void*), void*); + +#ifdef __cplusplus +} +#endif +#endif blob - /dev/null blob + f0ecdeaf57888a440dde4a39d082563c2948dadd (mode 644) --- /dev/null +++ include/auth.h @@ -0,0 +1,159 @@ +#ifndef __AUTH_H__ +#define __AUTH_H__ 1 + +#ifdef __cplusplus +extern "C" { +#endif +/* +#pragma src "/sys/src/libauth" +#pragma lib "libauth.a" +*/ + +/* + * Interface for typical callers. + */ + +typedef struct AuthInfo AuthInfo; +typedef struct Chalstate Chalstate; +typedef struct Chapreply Chapreply; +typedef struct MSchapreply MSchapreply; +typedef struct UserPasswd UserPasswd; +typedef struct AuthRpc AuthRpc; + +enum +{ + MAXCHLEN= 256, /* max challenge length */ + MAXNAMELEN= 256, /* maximum name length */ + MD5LEN= 16, + + ARok = 0, /* rpc return values */ + ARdone, + ARerror, + ARneedkey, + ARbadkey, + ARwritenext, + ARtoosmall, + ARtoobig, + ARrpcfailure, + ARphase, + + AuthRpcMax = 4096, +}; + +struct AuthRpc +{ + int afd; + char ibuf[AuthRpcMax]; + char obuf[AuthRpcMax]; + char *arg; + uint narg; +}; + +struct AuthInfo +{ + char *cuid; /* caller id */ + char *suid; /* server id */ + char *cap; /* capability (only valid on server side) */ + int nsecret; /* length of secret */ + uchar *secret; /* secret */ +}; + +struct Chalstate +{ + char *user; + char chal[MAXCHLEN]; + int nchal; + void *resp; + int nresp; + +/* for implementation only */ + int afd; /* to factotum */ + AuthRpc *rpc; /* to factotum */ + char userbuf[MAXNAMELEN]; /* temp space if needed */ + int userinchal; /* user was sent to obtain challenge */ +}; + +struct Chapreply /* for protocol "chap" */ +{ + uchar id; + char resp[MD5LEN]; +}; + +struct MSchapreply /* for protocol "mschap" */ +{ + char LMresp[24]; /* Lan Manager response */ + char NTresp[24]; /* NT response */ +}; + +struct UserPasswd +{ + char *user; + char *passwd; +}; + +extern int newns(char*, char*); +extern int addns(char*, char*); + +extern int noworld(char*); +extern int amount(int, char*, int, char*); + +/* these two may get generalized away -rsc */ +extern int login(char*, char*, char*); +extern int httpauth(char*, char*); + +typedef struct Attr Attr; +enum { + AttrNameval, /* name=val -- when matching, must have name=val */ + AttrQuery, /* name? -- when matching, must be present */ + AttrDefault, /* name:=val -- when matching, if present must match INTERNAL */ +}; +struct Attr +{ + int type; + Attr *next; + char *name; + char *val; +}; + +typedef int AuthGetkey(char*); + +int _attrfmt(Fmt*); +Attr *_copyattr(Attr*); +Attr *_delattr(Attr*, char*); +Attr *_findattr(Attr*, char*); +void _freeattr(Attr*); +Attr *_mkattr(int, char*, char*, Attr*); +Attr *_parseattr(char*); +char *_strfindattr(Attr*, char*); +/* +#pragma varargck type "A" Attr* +*/ + +extern AuthInfo* fauth_proxy(int, AuthRpc *rpc, AuthGetkey *getkey, char *params); +extern AuthInfo* auth_proxy(int fd, AuthGetkey *getkey, char *fmt, ...); +extern int auth_getkey(char*); +extern int (*amount_getkey)(char*); +extern void auth_freeAI(AuthInfo *ai); +extern int auth_chuid(AuthInfo *ai, char *ns); +extern Chalstate *auth_challenge(char*, ...); +extern AuthInfo* auth_response(Chalstate*); +extern int auth_respond(void*, uint, char*, uint, void*, uint, AuthGetkey *getkey, char*, ...); +extern void auth_freechal(Chalstate*); +extern AuthInfo* auth_userpasswd(char *user, char *passwd); +extern UserPasswd* auth_getuserpasswd(AuthGetkey *getkey, char*, ...); +extern AuthInfo* auth_getinfo(AuthRpc *rpc); +extern AuthRpc* auth_allocrpc(int afd); +extern Attr* auth_attr(AuthRpc *rpc); +extern void auth_freerpc(AuthRpc *rpc); +extern uint auth_rpc(AuthRpc *rpc, char *verb, void *a, int n); +extern int auth_wep(char*, char*, ...); +/* +#pragma varargck argpos auth_proxy 3 +#pragma varargck argpos auth_challenge 1 +#pragma varargck argpos auth_respond 3 +#pragma varargck argpos auth_getuserpasswd 2 +*/ +#ifdef __cplusplus +} +#endif +#endif blob - /dev/null blob + 1149a212082856737ca55ffb17d99ecba8b4e8d8 (mode 644) --- /dev/null +++ include/authsrv.h @@ -0,0 +1,177 @@ +#ifndef __AUTHSRV_H__ +#define __AUTHSRV_H__ 1 +#ifdef __cplusplus +extern "C" { +#endif +/* +#pragma src "/sys/src/libauthsrv" +#pragma lib "libauthsrv.a" +*/ + +/* + * Interface for talking to authentication server. + */ +typedef struct Ticket Ticket; +typedef struct Ticketreq Ticketreq; +typedef struct Authenticator Authenticator; +typedef struct Nvrsafe Nvrsafe; +typedef struct Passwordreq Passwordreq; +typedef struct OChapreply OChapreply; +typedef struct OMSchapreply OMSchapreply; + +enum +{ + ANAMELEN= 28, /* maximum size of name in previous proto */ + AERRLEN= 64, /* maximum size of errstr in previous proto */ + DOMLEN= 48, /* length of an authentication domain name */ + DESKEYLEN= 7, /* length of a des key for encrypt/decrypt */ + CHALLEN= 8, /* length of a plan9 sk1 challenge */ + NETCHLEN= 16, /* max network challenge length (used in AS protocol) */ + CONFIGLEN= 14, + SECRETLEN= 32, /* max length of a secret */ + + KEYDBOFF= 8, /* length of random data at the start of key file */ + OKEYDBLEN= ANAMELEN+DESKEYLEN+4+2, /* length of an entry in old key file */ + KEYDBLEN= OKEYDBLEN+SECRETLEN, /* length of an entry in key file */ + OMD5LEN= 16, +}; + +/* encryption numberings (anti-replay) */ +enum +{ + AuthTreq=1, /* ticket request */ + AuthChal=2, /* challenge box request */ + AuthPass=3, /* change password */ + AuthOK=4, /* fixed length reply follows */ + AuthErr=5, /* error follows */ + AuthMod=6, /* modify user */ + AuthApop=7, /* apop authentication for pop3 */ + AuthOKvar=9, /* variable length reply follows */ + AuthChap=10, /* chap authentication for ppp */ + AuthMSchap=11, /* MS chap authentication for ppp */ + AuthCram=12, /* CRAM verification for IMAP (RFC2195 & rfc2104) */ + AuthHttp=13, /* http domain login */ + AuthVNC=14, /* VNC server login (deprecated) */ + + + AuthTs=64, /* ticket encrypted with server's key */ + AuthTc, /* ticket encrypted with client's key */ + AuthAs, /* server generated authenticator */ + AuthAc, /* client generated authenticator */ + AuthTp, /* ticket encrypted with client's key for password change */ + AuthHr, /* http reply */ +}; + +struct Ticketreq +{ + char type; + char authid[ANAMELEN]; /* server's encryption id */ + char authdom[DOMLEN]; /* server's authentication domain */ + char chal[CHALLEN]; /* challenge from server */ + char hostid[ANAMELEN]; /* host's encryption id */ + char uid[ANAMELEN]; /* uid of requesting user on host */ +}; +#define TICKREQLEN (3*ANAMELEN+CHALLEN+DOMLEN+1) + +struct Ticket +{ + char num; /* replay protection */ + char chal[CHALLEN]; /* server challenge */ + char cuid[ANAMELEN]; /* uid on client */ + char suid[ANAMELEN]; /* uid on server */ + char key[DESKEYLEN]; /* nonce DES key */ +}; +#define TICKETLEN (CHALLEN+2*ANAMELEN+DESKEYLEN+1) + +struct Authenticator +{ + char num; /* replay protection */ + char chal[CHALLEN]; + ulong id; /* authenticator id, ++'d with each auth */ +}; +#define AUTHENTLEN (CHALLEN+4+1) + +struct Passwordreq +{ + char num; + char old[ANAMELEN]; + char new[ANAMELEN]; + char changesecret; + char secret[SECRETLEN]; /* new secret */ +}; +#define PASSREQLEN (2*ANAMELEN+1+1+SECRETLEN) + +struct OChapreply +{ + uchar id; + char uid[ANAMELEN]; + char resp[OMD5LEN]; +}; + +struct OMSchapreply +{ + char uid[ANAMELEN]; + char LMresp[24]; /* Lan Manager response */ + char NTresp[24]; /* NT response */ +}; + +/* + * convert to/from wire format + */ +extern int convT2M(Ticket*, char*, char*); +extern void convM2T(char*, Ticket*, char*); +extern void convM2Tnoenc(char*, Ticket*); +extern int convA2M(Authenticator*, char*, char*); +extern void convM2A(char*, Authenticator*, char*); +extern int convTR2M(Ticketreq*, char*); +extern void convM2TR(char*, Ticketreq*); +extern int convPR2M(Passwordreq*, char*, char*); +extern void convM2PR(char*, Passwordreq*, char*); + +/* + * convert ascii password to DES key + */ +extern int opasstokey(char*, char*); +extern int passtokey(char*, char*); + +/* + * Nvram interface + */ +enum { + NVwrite = 1<<0, /* always prompt and rewrite nvram */ + NVwriteonerr = 1<<1, /* prompt and rewrite nvram when corrupt */ +}; + +struct Nvrsafe +{ + char machkey[DESKEYLEN]; + uchar machsum; + char authkey[DESKEYLEN]; + uchar authsum; + char config[CONFIGLEN]; + uchar configsum; + char authid[ANAMELEN]; + uchar authidsum; + char authdom[DOMLEN]; + uchar authdomsum; +}; + +extern uchar nvcsum(void*, int); +extern int readnvram(Nvrsafe*, int); + +/* + * call up auth server + */ +extern int authdial(char *netroot, char *authdom); + +/* + * exchange messages with auth server + */ +extern int _asgetticket(int, char*, char*); +extern int _asrdresp(int, char*, int); +extern int sslnegotiate(int, Ticket*, char**, char**); +extern int srvsslnegotiate(int, Ticket*, char**, char**); +#ifdef __cplusplus +} +#endif +#endif blob - /dev/null blob + 876c0c7370f2ca57579346a9c9333cea5df810b9 (mode 644) --- /dev/null +++ include/mp.h @@ -0,0 +1,153 @@ +#ifndef __MP_H__ +#define __MP_H__ 1 +#ifdef __cplusplus +extern "C" { +#endif + +/* +#pragma src "/sys/src/libmp" +#pragma lib "libmp.a" +*/ + +#define _MPINT 1 + +typedef long mpdigit; + +// the code assumes mpdigit to be at least an int +// mpdigit must be an atomic type. mpdigit is defined +// in the architecture specific u.h + +typedef struct mpint mpint; + +struct mpint +{ + int sign; // +1 or -1 + int size; // allocated digits + int top; // significant digits + mpdigit *p; + char flags; +}; + +enum +{ + MPstatic= 0x01, + Dbytes= sizeof(mpdigit), // bytes per digit + Dbits= Dbytes*8 // bits per digit +}; + +// allocation +void mpsetminbits(int n); // newly created mpint's get at least n bits +mpint* mpnew(int n); // create a new mpint with at least n bits +void mpfree(mpint *b); +void mpbits(mpint *b, int n); // ensure that b has at least n bits +void mpnorm(mpint *b); // dump leading zeros +mpint* mpcopy(mpint *b); +void mpassign(mpint *old, mpint *new); + +// random bits +mpint* mprand(int bits, void (*gen)(uchar*, int), mpint *b); + +// conversion +mpint* strtomp(char*, char**, int, mpint*); // ascii +int mpfmt(Fmt*); +char* mptoa(mpint*, int, char*, int); +mpint* letomp(uchar*, uint, mpint*); // byte array, little-endian +int mptole(mpint*, uchar*, uint, uchar**); +mpint* betomp(uchar*, uint, mpint*); // byte array, little-endian +int mptobe(mpint*, uchar*, uint, uchar**); +uint mptoui(mpint*); // unsigned int +mpint* uitomp(uint, mpint*); +int mptoi(mpint*); // int +mpint* itomp(int, mpint*); +uvlong mptouv(mpint*); // unsigned vlong +mpint* uvtomp(uvlong, mpint*); +vlong mptov(mpint*); // vlong +mpint* vtomp(vlong, mpint*); + +// divide 2 digits by one +void mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient); + +// in the following, the result mpint may be +// the same as one of the inputs. +void mpadd(mpint *b1, mpint *b2, mpint *sum); // sum = b1+b2 +void mpsub(mpint *b1, mpint *b2, mpint *diff); // diff = b1-b2 +void mpleft(mpint *b, int shift, mpint *res); // res = b<>shift +void mpmul(mpint *b1, mpint *b2, mpint *prod); // prod = b1*b2 +void mpexp(mpint *b, mpint *e, mpint *m, mpint *res); // res = b**e mod m +void mpmod(mpint *b, mpint *m, mpint *remainder); // remainder = b mod m + +// quotient = dividend/divisor, remainder = dividend % divisor +void mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder); + +// return neg, 0, pos as b1-b2 is neg, 0, pos +int mpcmp(mpint *b1, mpint *b2); + +// extended gcd return d, x, and y, s.t. d = gcd(a,b) and ax+by = d +void mpextendedgcd(mpint *a, mpint *b, mpint *d, mpint *x, mpint *y); + +// res = b**-1 mod m +void mpinvert(mpint *b, mpint *m, mpint *res); + +// bit counting +int mpsignif(mpint*); // number of sigificant bits in mantissa +int mplowbits0(mpint*); // k, where n = 2**k * q for odd q + +// well known constants +extern mpint *mpzero, *mpone, *mptwo; + +// sum[0:alen] = a[0:alen-1] + b[0:blen-1] +// prereq: alen >= blen, sum has room for alen+1 digits +void mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum); + +// diff[0:alen-1] = a[0:alen-1] - b[0:blen-1] +// prereq: alen >= blen, diff has room for alen digits +void mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff); + +// p[0:n] += m * b[0:n-1] +// prereq: p has room for n+1 digits +void mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p); + +// p[0:n] -= m * b[0:n-1] +// prereq: p has room for n+1 digits +int mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p); + +// p[0:alen*blen-1] = a[0:alen-1] * b[0:blen-1] +// prereq: alen >= blen, p has room for m*n digits +void mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p); + +// sign of a - b or zero if the same +int mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen); + +// divide the 2 digit dividend by the one digit divisor and stick in quotient +// we assume that the result is one digit - overflow is all 1's +void mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient); + +// playing with magnitudes +int mpmagcmp(mpint *b1, mpint *b2); +void mpmagadd(mpint *b1, mpint *b2, mpint *sum); // sum = b1+b2 +void mpmagsub(mpint *b1, mpint *b2, mpint *sum); // sum = b1+b2 + +// chinese remainder theorem +typedef struct CRTpre CRTpre; // precomputed values for converting + // twixt residues and mpint +typedef struct CRTres CRTres; // residue form of an mpint + +struct CRTres +{ + int n; // number of residues + mpint *r[1]; // residues +}; + +CRTpre* crtpre(int, mpint**); // precompute conversion values +CRTres* crtin(CRTpre*, mpint*); // convert mpint to residues +void crtout(CRTpre*, CRTres*, mpint*); // convert residues to mpint +void crtprefree(CRTpre*); +void crtresfree(CRTres*); + + +/* #pragma varargck type "B" mpint* */ +#ifdef __cplusplus +} +#endif +#endif blob - /dev/null blob + 0da51b518542503e23e5ecd7fe11b908982ad981 (mode 644) --- /dev/null +++ include/nfs3.h @@ -0,0 +1,801 @@ +/* + * NFS mounter V3; see RFC 1813 + */ +/* +#pragma lib "libsunrpc.a" +#pragma src "/sys/src/libsunrpc" +*/ +#define _NFS3H_ /* sorry */ + +enum { + NfsMount1HandleSize = 32, + NfsMount3MaxPathSize = 1024, + NfsMount3MaxNameSize = 255, + NfsMount3MaxHandleSize = 64, + NfsMount3Program = 100005, + NfsMount3Version = 3, + NfsMount1Program = 100005, + NfsMount1Version = 1 +}; +typedef struct NfsMount3TNull NfsMount3TNull; +typedef struct NfsMount3RNull NfsMount3RNull; +typedef struct NfsMount3TMnt NfsMount3TMnt; +typedef struct NfsMount3RMnt NfsMount3RMnt; +typedef struct NfsMount3TDump NfsMount3TDump; +typedef struct NfsMount3Entry NfsMount3Entry; +typedef struct NfsMount3RDump NfsMount3RDump; +typedef struct NfsMount3TUmnt NfsMount3TUmnt; +typedef struct NfsMount3RUmnt NfsMount3RUmnt; +typedef struct NfsMount3Export NfsMount3Export; +typedef struct NfsMount3TUmntall NfsMount3TUmntall; +typedef struct NfsMount3RUmntall NfsMount3RUmntall; +typedef struct NfsMount3TExport NfsMount3TExport; +typedef struct NfsMount3RExport NfsMount3RExport; + +typedef enum +{ + NfsMount3CallTNull, + NfsMount3CallRNull, + NfsMount3CallTMnt, + NfsMount3CallRMnt, + NfsMount3CallTDump, + NfsMount3CallRDump, + NfsMount3CallTUmnt, + NfsMount3CallRUmnt, + NfsMount3CallTUmntall, + NfsMount3CallRUmntall, + NfsMount3CallTExport, + NfsMount3CallRExport +} NfsMount3CallType; + +struct NfsMount3TNull { + SunCall call; +}; + +struct NfsMount3RNull { + SunCall call; +}; + +struct NfsMount3TMnt { + SunCall call; + char *path; +}; + +struct NfsMount3RMnt { + SunCall call; + uint status; + uchar *handle; + uint len; + u32int *auth; + u32int nauth; +}; + +struct NfsMount3TDump { + SunCall call; +}; + +struct NfsMount3Entry { + char *host; + char *path; +}; + +struct NfsMount3RDump { + SunCall call; + uchar *data; + u32int count; +}; + +struct NfsMount3TUmnt { + SunCall call; + char *path; +}; + +struct NfsMount3RUmnt { + SunCall call; +}; + +struct NfsMount3Export { + char *path; + char **g; + u32int ng; +}; + +struct NfsMount3TUmntall { + SunCall call; +}; + +struct NfsMount3RUmntall { + SunCall call; +}; + +struct NfsMount3TExport { + SunCall call; +}; + +struct NfsMount3RExport { + SunCall call; + uchar *data; + u32int count; +}; + +uint nfsmount3exportgroupsize(uchar*); +uint nfsmount3exportsize(NfsMount3Export*); +int nfsmount3exportpack(uchar*, uchar*, uchar**, NfsMount3Export*); +int nfsmount3exportunpack(uchar*, uchar*, uchar**, char**, char***, NfsMount3Export*); +int nfsmount3entrypack(uchar*, uchar*, uchar**, NfsMount3Entry*); +int nfsmount3entryunpack(uchar*, uchar*, uchar**, NfsMount3Entry*); +uint nfsmount3entrysize(NfsMount3Entry*); + +extern SunProg nfsmount3prog; + +/* + * NFS V3; see RFC 1813 + */ +enum { + Nfs3MaxHandleSize = 64, + Nfs3CookieVerfSize = 8, + Nfs3CreateVerfSize = 8, + Nfs3WriteVerfSize = 8, + Nfs3AccessRead = 1, + Nfs3AccessLookup = 2, + Nfs3AccessModify = 4, + Nfs3AccessExtend = 8, + Nfs3AccessDelete = 16, + Nfs3AccessExecute = 32, + Nfs3FsHasLinks = 1, + Nfs3FsHasSymlinks = 2, + Nfs3FsHomogeneous = 8, + Nfs3FsCanSetTime = 16, + + Nfs3Version = 3, + Nfs3Program = 100003, +}; +typedef enum +{ + Nfs3Ok = 0, + Nfs3ErrNotOwner = 1, + Nfs3ErrNoEnt = 2, + Nfs3ErrIo = 5, + Nfs3ErrNxio = 6, + Nfs3ErrNoMem = 12, + Nfs3ErrAcces = 13, + Nfs3ErrExist = 17, + Nfs3ErrXDev = 18, + Nfs3ErrNoDev = 19, + Nfs3ErrNotDir = 20, + Nfs3ErrIsDir = 21, + Nfs3ErrInval = 22, + Nfs3ErrFbig = 27, + Nfs3ErrNoSpc = 28, + Nfs3ErrRoFs = 30, + Nfs3ErrMLink = 31, + Nfs3ErrNameTooLong = 63, + Nfs3ErrNotEmpty = 66, + Nfs3ErrDQuot = 69, + Nfs3ErrStale = 70, + Nfs3ErrRemote = 71, + Nfs3ErrBadHandle = 10001, + Nfs3ErrNotSync = 10002, + Nfs3ErrBadCookie = 10003, + Nfs3ErrNotSupp = 10004, + Nfs3ErrTooSmall = 10005, + Nfs3ErrServerFault = 10006, + Nfs3ErrBadType = 10007, + Nfs3ErrJukebox = 10008, + Nfs3ErrFprintNotFound = 10009, + Nfs3ErrAborted = 10010, +} Nfs3Status; + +void nfs3Errstr(Nfs3Status); + +typedef enum +{ + Nfs3FileReg = 1, + Nfs3FileDir = 2, + Nfs3FileBlock = 3, + Nfs3FileChar = 4, + Nfs3FileSymlink = 5, + Nfs3FileSocket = 6, + Nfs3FileFifo = 7, +} Nfs3FileType; + +enum +{ + Nfs3ModeSetUid = 0x800, + Nfs3ModeSetGid = 0x400, + Nfs3ModeSticky = 0x200, +}; + +typedef enum +{ + Nfs3CallTNull, + Nfs3CallRNull, + Nfs3CallTGetattr, + Nfs3CallRGetattr, + Nfs3CallTSetattr, + Nfs3CallRSetattr, + Nfs3CallTLookup, + Nfs3CallRLookup, + Nfs3CallTAccess, + Nfs3CallRAccess, + Nfs3CallTReadlink, + Nfs3CallRReadlink, + Nfs3CallTRead, + Nfs3CallRRead, + Nfs3CallTWrite, + Nfs3CallRWrite, + Nfs3CallTCreate, + Nfs3CallRCreate, + Nfs3CallTMkdir, + Nfs3CallRMkdir, + Nfs3CallTSymlink, + Nfs3CallRSymlink, + Nfs3CallTMknod, + Nfs3CallRMknod, + Nfs3CallTRemove, + Nfs3CallRRemove, + Nfs3CallTRmdir, + Nfs3CallRRmdir, + Nfs3CallTRename, + Nfs3CallRRename, + Nfs3CallTLink, + Nfs3CallRLink, + Nfs3CallTReadDir, + Nfs3CallRReadDir, + Nfs3CallTReadDirPlus, + Nfs3CallRReadDirPlus, + Nfs3CallTFsStat, + Nfs3CallRFsStat, + Nfs3CallTFsInfo, + Nfs3CallRFsInfo, + Nfs3CallTPathconf, + Nfs3CallRPathconf, + Nfs3CallTCommit, + Nfs3CallRCommit, +} Nfs3CallType; + +typedef struct Nfs3Handle Nfs3Handle; +typedef struct Nfs3Time Nfs3Time; +typedef struct Nfs3Attr Nfs3Attr; +typedef struct Nfs3WccAttr Nfs3WccAttr; +typedef struct Nfs3Wcc Nfs3Wcc; +typedef enum +{ + Nfs3SetTimeDont = 0, + Nfs3SetTimeServer = 1, + Nfs3SetTimeClient = 2, +} Nfs3SetTime; + +typedef struct Nfs3SetAttr Nfs3SetAttr; +typedef struct Nfs3TNull Nfs3TNull; +typedef struct Nfs3RNull Nfs3RNull; +typedef struct Nfs3TGetattr Nfs3TGetattr; +typedef struct Nfs3RGetattr Nfs3RGetattr; +typedef struct Nfs3TSetattr Nfs3TSetattr; +typedef struct Nfs3RSetattr Nfs3RSetattr; +typedef struct Nfs3TLookup Nfs3TLookup; +typedef struct Nfs3RLookup Nfs3RLookup; +typedef struct Nfs3TAccess Nfs3TAccess; +typedef struct Nfs3RAccess Nfs3RAccess; +typedef struct Nfs3TReadlink Nfs3TReadlink; +typedef struct Nfs3RReadlink Nfs3RReadlink; +typedef struct Nfs3TRead Nfs3TRead; +typedef struct Nfs3RRead Nfs3RRead; +typedef enum +{ + Nfs3SyncNone = 0, + Nfs3SyncData = 1, + Nfs3SyncFile = 2, +} Nfs3Sync; + +typedef struct Nfs3TWrite Nfs3TWrite; +typedef struct Nfs3RWrite Nfs3RWrite; +typedef enum +{ + Nfs3CreateUnchecked = 0, + Nfs3CreateGuarded = 1, + Nfs3CreateExclusive = 2, +} Nfs3Create; + +typedef struct Nfs3TCreate Nfs3TCreate; +typedef struct Nfs3RCreate Nfs3RCreate; +typedef struct Nfs3TMkdir Nfs3TMkdir; +typedef struct Nfs3RMkdir Nfs3RMkdir; +typedef struct Nfs3TSymlink Nfs3TSymlink; +typedef struct Nfs3RSymlink Nfs3RSymlink; +typedef struct Nfs3TMknod Nfs3TMknod; +typedef struct Nfs3RMknod Nfs3RMknod; +typedef struct Nfs3TRemove Nfs3TRemove; +typedef struct Nfs3RRemove Nfs3RRemove; +typedef struct Nfs3TRmdir Nfs3TRmdir; +typedef struct Nfs3RRmdir Nfs3RRmdir; +typedef struct Nfs3TRename Nfs3TRename; +typedef struct Nfs3RRename Nfs3RRename; +typedef struct Nfs3TLink Nfs3TLink; +typedef struct Nfs3RLink Nfs3RLink; +typedef struct Nfs3TReadDir Nfs3TReadDir; +typedef struct Nfs3Entry Nfs3Entry; +typedef struct Nfs3RReadDir Nfs3RReadDir; +typedef struct Nfs3TReadDirPlus Nfs3TReadDirPlus; +typedef struct Nfs3EntryPlus Nfs3EntryPlus; +typedef struct Nfs3RReadDirPlus Nfs3RReadDirPlus; +typedef struct Nfs3TFsStat Nfs3TFsStat; +typedef struct Nfs3RFsStat Nfs3RFsStat; +typedef struct Nfs3TFsInfo Nfs3TFsInfo; +typedef struct Nfs3RFsInfo Nfs3RFsInfo; +typedef struct Nfs3TPathconf Nfs3TPathconf; +typedef struct Nfs3RPathconf Nfs3RPathconf; +typedef struct Nfs3TCommit Nfs3TCommit; +typedef struct Nfs3RCommit Nfs3RCommit; + +struct Nfs3Handle { + uchar h[Nfs3MaxHandleSize]; + u32int len; +}; + +struct Nfs3Time { + u32int sec; + u32int nsec; +}; + +struct Nfs3Attr { + Nfs3FileType type; + u32int mode; + u32int nlink; + u32int uid; + u32int gid; + u64int size; + u64int used; + u32int major; + u32int minor; + u64int fsid; + u64int fileid; + Nfs3Time atime; + Nfs3Time mtime; + Nfs3Time ctime; +}; + +struct Nfs3WccAttr { + u64int size; + Nfs3Time mtime; + Nfs3Time ctime; +}; + +struct Nfs3Wcc { + u1int haveWccAttr; + Nfs3WccAttr wccAttr; + u1int haveAttr; + Nfs3Attr attr; +}; + +struct Nfs3SetAttr { + u1int setMode; + u32int mode; + u1int setUid; + u32int uid; + u1int setGid; + u32int gid; + u1int setSize; + u64int size; + Nfs3SetTime setAtime; + Nfs3Time atime; + Nfs3SetTime setMtime; + Nfs3Time mtime; +}; + +struct Nfs3TNull { + SunCall call; +}; + +struct Nfs3RNull { + SunCall call; +}; + +struct Nfs3TGetattr { + SunCall call; + Nfs3Handle handle; +}; + +struct Nfs3RGetattr { + SunCall call; + Nfs3Status status; + Nfs3Attr attr; +}; + +struct Nfs3TSetattr { + SunCall call; + Nfs3Handle handle; + Nfs3SetAttr attr; + u1int checkCtime; + Nfs3Time ctime; +}; + +struct Nfs3RSetattr { + SunCall call; + Nfs3Status status; + Nfs3Wcc wcc; +}; + +struct Nfs3TLookup { + SunCall call; + Nfs3Handle handle; + char *name; +}; + +struct Nfs3RLookup { + SunCall call; + Nfs3Status status; + Nfs3Handle handle; + u1int haveAttr; + Nfs3Attr attr; + u1int haveDirAttr; + Nfs3Attr dirAttr; +}; + +struct Nfs3TAccess { + SunCall call; + Nfs3Handle handle; + u32int access; +}; + +struct Nfs3RAccess { + SunCall call; + Nfs3Status status; + u1int haveAttr; + Nfs3Attr attr; + u32int access; +}; + +struct Nfs3TReadlink { + SunCall call; + Nfs3Handle handle; +}; + +struct Nfs3RReadlink { + SunCall call; + Nfs3Status status; + u1int haveAttr; + Nfs3Attr attr; + char *data; +}; + +struct Nfs3TRead { + SunCall call; + Nfs3Handle handle; + u64int offset; + u32int count; +}; + +struct Nfs3RRead { + SunCall call; + Nfs3Status status; + u1int haveAttr; + Nfs3Attr attr; + u32int count; + u1int eof; + uchar *data; + u32int ndata; +}; + +struct Nfs3TWrite { + SunCall call; + Nfs3Handle handle; + u64int offset; + u32int count; + Nfs3Sync stable; + uchar *data; + u32int ndata; +}; + +struct Nfs3RWrite { + SunCall call; + Nfs3Status status; + Nfs3Wcc wcc; + u32int count; + Nfs3Sync committed; + uchar verf[Nfs3WriteVerfSize]; +}; + +struct Nfs3TCreate { + SunCall call; + Nfs3Handle handle; + char *name; + Nfs3Create mode; + Nfs3SetAttr attr; + uchar verf[Nfs3CreateVerfSize]; +}; + +struct Nfs3RCreate { + SunCall call; + Nfs3Status status; + u1int haveHandle; + Nfs3Handle handle; + u1int haveAttr; + Nfs3Attr attr; + Nfs3Wcc dirWcc; +}; + +struct Nfs3TMkdir { + SunCall call; + Nfs3Handle handle; + char *name; + Nfs3SetAttr attr; +}; + +struct Nfs3RMkdir { + SunCall call; + Nfs3Status status; + u1int haveHandle; + Nfs3Handle handle; + u1int haveAttr; + Nfs3Attr attr; + Nfs3Wcc dirWcc; +}; + +struct Nfs3TSymlink { + SunCall call; + Nfs3Handle handle; + char *name; + Nfs3SetAttr attr; + char *data; +}; + +struct Nfs3RSymlink { + SunCall call; + Nfs3Status status; + u1int haveHandle; + Nfs3Handle handle; + u1int haveAttr; + Nfs3Attr attr; + Nfs3Wcc dirWcc; +}; + +struct Nfs3TMknod { + SunCall call; + Nfs3Handle handle; + char *name; + Nfs3FileType type; + Nfs3SetAttr attr; + u32int major; + u32int minor; +}; + +struct Nfs3RMknod { + SunCall call; + Nfs3Status status; + u1int haveHandle; + Nfs3Handle handle; + u1int haveAttr; + Nfs3Attr attr; + Nfs3Wcc dirWcc; +}; + +struct Nfs3TRemove { + SunCall call; + Nfs3Handle handle; + char *name; +}; + +struct Nfs3RRemove { + SunCall call; + Nfs3Status status; + Nfs3Wcc wcc; +}; + +struct Nfs3TRmdir { + SunCall call; + Nfs3Handle handle; + char *name; +}; + +struct Nfs3RRmdir { + SunCall call; + Nfs3Status status; + Nfs3Wcc wcc; +}; + +struct Nfs3TRename { + SunCall call; + struct { + Nfs3Handle handle; + char *name; + } from; + struct { + Nfs3Handle handle; + char *name; + } to; +}; + +struct Nfs3RRename { + SunCall call; + Nfs3Status status; + Nfs3Wcc fromWcc; + Nfs3Wcc toWcc; +}; + +struct Nfs3TLink { + SunCall call; + Nfs3Handle handle; + struct { + Nfs3Handle handle; + char *name; + } link; +}; + +struct Nfs3RLink { + SunCall call; + Nfs3Status status; + u1int haveAttr; + Nfs3Attr attr; + Nfs3Wcc dirWcc; +}; + +struct Nfs3TReadDir { + SunCall call; + Nfs3Handle handle; + u64int cookie; + uchar verf[Nfs3CookieVerfSize]; + u32int count; +}; + +struct Nfs3RReadDir { + SunCall call; + Nfs3Status status; + u1int haveAttr; + Nfs3Attr attr; + uchar verf[Nfs3CookieVerfSize]; + uchar *data; + u32int count; + u1int eof; +}; + +struct Nfs3TReadDirPlus { + SunCall call; + Nfs3Handle handle; + u64int cookie; + uchar verf[Nfs3CookieVerfSize]; + u32int dirCount; + u32int maxCount; +}; + +struct Nfs3Entry { + u64int fileid; + char *name; + u64int cookie; + u1int haveAttr; + Nfs3Attr attr; + u1int haveHandle; + Nfs3Handle handle; +}; + +struct Nfs3RReadDirPlus { + SunCall call; + Nfs3Status status; + u1int haveAttr; + Nfs3Attr attr; + uchar verf[Nfs3CookieVerfSize]; + uchar *data; + u32int count; + u1int eof; +}; + +struct Nfs3TFsStat { + SunCall call; + Nfs3Handle handle; +}; + +struct Nfs3RFsStat { + SunCall call; + Nfs3Status status; + u1int haveAttr; + Nfs3Attr attr; + u64int totalBytes; + u64int freeBytes; + u64int availBytes; + u64int totalFiles; + u64int freeFiles; + u64int availFiles; + u32int invarSec; +}; + +struct Nfs3TFsInfo { + SunCall call; + Nfs3Handle handle; +}; + +struct Nfs3RFsInfo { + SunCall call; + Nfs3Status status; + u1int haveAttr; + Nfs3Attr attr; + u32int readMax; + u32int readPref; + u32int readMult; + u32int writeMax; + u32int writePref; + u32int writeMult; + u32int readDirPref; + u64int maxFileSize; + Nfs3Time timePrec; + u32int flags; +}; + +struct Nfs3TPathconf { + SunCall call; + Nfs3Handle handle; +}; + +struct Nfs3RPathconf { + SunCall call; + Nfs3Status status; + u1int haveAttr; + Nfs3Attr attr; + u32int maxLink; + u32int maxName; + u1int noTrunc; + u1int chownRestricted; + u1int caseInsensitive; + u1int casePreserving; +}; + +struct Nfs3TCommit { + SunCall call; + Nfs3Handle handle; + u64int offset; + u32int count; +}; + +struct Nfs3RCommit { + SunCall call; + Nfs3Status status; + Nfs3Wcc wcc; + uchar verf[Nfs3WriteVerfSize]; +}; + +char *nfs3statusstr(Nfs3Status); +char *nfs3typestr(SunCallType); +char *nfs3settimestr(Nfs3SetTime); +char *nfs3syncstr(Nfs3Sync); + +void nfs3handleprint(Fmt*, Nfs3Handle*); +u32int nfs3handlesize(Nfs3Handle*); +int nfs3handlepack(uchar*, uchar*, uchar**, Nfs3Handle*); +int nfs3handleunpack(uchar*, uchar*, uchar**, Nfs3Handle*); + +void nfs3timeprint(Fmt*, Nfs3Time*); +u32int nfs3timesize(Nfs3Time*); +int nfs3timepack(uchar*, uchar*, uchar**, Nfs3Time*); +int nfs3timeunpack(uchar*, uchar*, uchar**, Nfs3Time*); + +void nfs3attrprint(Fmt*, Nfs3Attr*); +u32int nfs3attrsize(Nfs3Attr*); +int nfs3attrpack(uchar*, uchar*, uchar**, Nfs3Attr*); +int nfs3attrunpack(uchar*, uchar*, uchar**, Nfs3Attr*); + +void nfs3wccattrprint(Fmt*, Nfs3WccAttr*); +u32int nfs3wccattrsize(Nfs3WccAttr*); +int nfs3wccattrpack(uchar*, uchar*, uchar**, Nfs3WccAttr*); +int nfs3wccattrunpack(uchar*, uchar*, uchar**, Nfs3WccAttr*); + +void nfs3wccprint(Fmt*, Nfs3Wcc*); +u32int nfs3wccsize(Nfs3Wcc*); +int nfs3wccpack(uchar*, uchar*, uchar**, Nfs3Wcc*); +int nfs3wccunpack(uchar*, uchar*, uchar**, Nfs3Wcc*); + +void nfs3setattrprint(Fmt*, Nfs3SetAttr*); +u32int nfs3setattrsize(Nfs3SetAttr*); +int nfs3setattrpack(uchar*, uchar*, uchar**, Nfs3SetAttr*); +int nfs3setattrunpack(uchar*, uchar*, uchar**, Nfs3SetAttr*); + +extern SunProg nfs3prog; + +void nfs3entryprint(Fmt*, Nfs3Entry*); +u32int nfs3entrysize(Nfs3Entry*); +int nfs3entrypack(uchar*, uchar*, uchar**, Nfs3Entry*); +int nfs3entryunpack(uchar*, uchar*, uchar**, Nfs3Entry*); + +void nfs3entryplusprint(Fmt*, Nfs3Entry*); +u32int nfs3entryplussize(Nfs3Entry*); +int nfs3entrypluspack(uchar*, uchar*, uchar**, Nfs3Entry*); +int nfs3entryplusunpack(uchar*, uchar*, uchar**, Nfs3Entry*); + blob - /dev/null blob + 4f108aa0bf3d2efd39f02f44c0145a06b42f4fcf (mode 644) --- /dev/null +++ include/sunrpc.h @@ -0,0 +1,400 @@ +/* + * Sun RPC; see RFC 1057 + */ + +/* +#pragma lib "libsunrpc.a" +#pragma src "/sys/src/libsunrpc" +*/ + +typedef uchar u1int; + +typedef struct SunAuthInfo SunAuthInfo; +typedef struct SunAuthUnix SunAuthUnix; +typedef struct SunRpc SunRpc; +typedef struct SunCall SunCall; + +enum +{ + /* Authinfo.flavor */ + SunAuthNone = 0, + SunAuthSys, + SunAuthShort, + SunAuthDes, +}; + +typedef enum { + SunAcceptError = 0x10000, + SunRejectError = 0x20000, + SunAuthError = 0x40000, + + /* Reply.status */ + SunSuccess = 0, + + SunProgUnavail = SunAcceptError | 1, + SunProgMismatch, + SunProcUnavail, + SunGarbageArgs, + SunSystemErr, + + SunRpcMismatch = SunRejectError | 0, + + SunAuthBadCred = SunAuthError | 1, + SunAuthRejectedCred, + SunAuthBadVerf, + SunAuthRejectedVerf, + SunAuthTooWeak, + SunAuthInvalidResp, + SunAuthFailed, +} SunStatus; + +struct SunAuthInfo +{ + uint flavor; + uchar *data; + uint ndata; +}; + +struct SunAuthUnix +{ + u32int stamp; + char *sysname; + u32int uid; + u32int gid; + u32int g[16]; + u32int ng; +}; + +struct SunRpc +{ + u32int xid; + uint iscall; + + /* + * only sent on wire in call + * caller fills in for the reply unpackers. + */ + u32int proc; + + /* call */ + // uint proc; + u32int prog, vers; + SunAuthInfo cred; + SunAuthInfo verf; + uchar *data; + uint ndata; + + /* reply */ + u32int status; + // SunAuthInfo verf; + u32int low, high; + // uchar *data; + // uint ndata; +}; + +typedef enum +{ + SunCallTypeTNull, + SunCallTypeRNull, +} SunCallType; + +struct SunCall +{ + SunRpc rpc; + SunCallType type; +}; + +void sunerrstr(SunStatus); + +void sunrpcprint(Fmt*, SunRpc*); +uint sunrpcsize(SunRpc*); +SunStatus sunrpcpack(uchar*, uchar*, uchar**, SunRpc*); +SunStatus sunrpcunpack(uchar*, uchar*, uchar**, SunRpc*); + +void sunauthinfoprint(Fmt*, SunAuthInfo*); +uint sunauthinfosize(SunAuthInfo*); +int sunauthinfopack(uchar*, uchar*, uchar**, SunAuthInfo*); +int sunauthinfounpack(uchar*, uchar*, uchar**, SunAuthInfo*); + +void sunauthunixprint(Fmt*, SunAuthUnix*); +uint sunauthunixsize(SunAuthUnix*); +int sunauthunixpack(uchar*, uchar*, uchar**, SunAuthUnix*); +int sunauthunixunpack(uchar*, uchar*, uchar**, SunAuthUnix*); + +int sunenumpack(uchar*, uchar*, uchar**, int*); +int sunenumunpack(uchar*, uchar*, uchar**, int*); +int sunuint1pack(uchar*, uchar*, uchar**, u1int*); +int sunuint1unpack(uchar*, uchar*, uchar**, u1int*); + +int sunstringpack(uchar*, uchar*, uchar**, char**, u32int); +int sunstringunpack(uchar*, uchar*, uchar**, char**, u32int); +uint sunstringsize(char*); + +int sunuint32pack(uchar*, uchar*, uchar**, u32int*); +int sunuint32unpack(uchar*, uchar*, uchar**, u32int*); +int sunuint64pack(uchar*, uchar*, uchar**, u64int*); +int sunuint64unpack(uchar*, uchar*, uchar**, u64int*); + +int sunvaropaquepack(uchar*, uchar*, uchar**, uchar**, u32int*, u32int); +int sunvaropaqueunpack(uchar*, uchar*, uchar**, uchar**, u32int*, u32int); +uint sunvaropaquesize(u32int); + +int sunfixedopaquepack(uchar*, uchar*, uchar**, uchar*, u32int); +int sunfixedopaqueunpack(uchar*, uchar*, uchar**, uchar*, u32int); +uint sunfixedopaquesize(u32int); + +/* + * Sun RPC Program + */ +typedef struct SunProc SunProc; +typedef struct SunProg SunProg; +struct SunProg +{ + uint prog; + uint vers; + SunProc *proc; + int nproc; +}; + +struct SunProc +{ + int (*pack)(uchar*, uchar*, uchar**, SunCall*); + int (*unpack)(uchar*, uchar*, uchar**, SunCall*); + uint (*size)(SunCall*); + void (*fmt)(Fmt*, SunCall*); + uint sizeoftype; +}; + +SunStatus suncallpack(SunProg*, uchar*, uchar*, uchar**, SunCall*); +SunStatus suncallunpack(SunProg*, uchar*, uchar*, uchar**, SunCall*); +SunStatus suncallunpackalloc(SunProg*, SunCallType, uchar*, uchar*, uchar**, SunCall**); +void suncallsetup(SunCall*, SunProg*, uint); +uint suncallsize(SunProg*, SunCall*); + +/* + * Formatting +#pragma varargck type "B" SunRpc* +#pragma varargck type "C" SunCall* + */ + +int sunrpcfmt(Fmt*); +int suncallfmt(Fmt*); +void sunfmtinstall(SunProg*); + + +/* + * Sun RPC Server + */ +typedef struct SunMsg SunMsg; +typedef struct SunSrv SunSrv; + +enum +{ + SunStackSize = 32768, +}; + +struct SunMsg +{ + uchar *data; + int count; + SunSrv *srv; + SunRpc rpc; + SunProg *pg; + SunCall *call; + Channel *creply; /* chan(SunMsg*) */ +}; + +struct SunSrv +{ + int chatty; + int cachereplies; + int alwaysreject; + int localonly; + int localparanoia; + SunProg **map; + Channel *crequest; + +/* implementation use only */ + Channel **cdispatch; + SunProg **prog; + int nprog; + void *cache; + Channel *creply; + Channel *cthread; +}; + +SunSrv *sunsrv(void); + +void sunsrvprog(SunSrv *srv, SunProg *prog, Channel *c); +int sunsrvannounce(SunSrv *srv, char *address); +int sunsrvudp(SunSrv *srv, char *address); +int sunsrvnet(SunSrv *srv, char *address); +int sunsrvfd(SunSrv *srv, int fd); +void sunsrvthreadcreate(SunSrv *srv, void (*fn)(void*), void*); +void sunsrvclose(SunSrv*); + +int sunmsgreply(SunMsg*, SunCall*); +int sunmsgdrop(SunMsg*); +int sunmsgreplyerror(SunMsg*, SunStatus); + +/* + * Sun RPC Client + */ +typedef struct SunClient SunClient; + +struct SunClient +{ + int fd; + int chatty; + int needcount; + ulong maxwait; + ulong xidgen; + int nsend; + int nresend; + struct { + ulong min; + ulong max; + ulong avg; + } rtt; + Channel *dying; + Channel *rpcchan; + Channel *timerchan; + Channel *flushchan; + Channel *readchan; + SunProg **prog; + int nprog; + int timertid; + int nettid; +}; + +SunClient *sundial(char*); + +int sunclientrpc(SunClient*, ulong, SunCall*, SunCall*, uchar**); +void sunclientclose(SunClient*); +void sunclientflushrpc(SunClient*, ulong); +void sunclientprog(SunClient*, SunProg*); + + +/* + * Provided by callers. + * Should remove dependence on this, but hard. + */ +void *emalloc(ulong); +void *erealloc(void*, ulong); + + +/* + * Sun RPC port mapper; see RFC 1057 Appendix A + */ + +typedef struct PortMap PortMap; +typedef struct PortTNull PortTNull; +typedef struct PortRNull PortRNull; +typedef struct PortTSet PortTSet; +typedef struct PortRSet PortRSet; +typedef struct PortTUnset PortTUnset; +typedef struct PortRUnset PortRUnset; +typedef struct PortTGetport PortTGetport; +typedef struct PortRGetport PortRGetport; +typedef struct PortTDump PortTDump; +typedef struct PortRDump PortRDump; +typedef struct PortTCallit PortTCallit; +typedef struct PortRCallit PortRCallit; + +typedef enum +{ + PortCallTNull, + PortCallRNull, + PortCallTSet, + PortCallRSet, + PortCallTUnset, + PortCallRUnset, + PortCallTGetport, + PortCallRGetport, + PortCallTDump, + PortCallRDump, + PortCallTCallit, + PortCallRCallit, +} PortCallType; + +enum +{ + PortProgram = 100000, + PortVersion = 2, + + PortProtoTcp = 6, /* protocol number for TCP/IP */ + PortProtoUdp = 17 /* protocol number for UDP/IP */ +}; + +struct PortMap { + u32int prog; + u32int vers; + u32int prot; + u32int port; +}; + +struct PortTNull { + SunCall call; +}; + +struct PortRNull { + SunCall call; +}; + +struct PortTSet { + SunCall call; + PortMap map; +}; + +struct PortRSet { + SunCall call; + u1int b; +}; + +struct PortTUnset { + SunCall call; + PortMap map; +}; + +struct PortRUnset { + SunCall call; + u1int b; +}; + +struct PortTGetport { + SunCall call; + PortMap map; +}; + +struct PortRGetport { + SunCall call; + u32int port; +}; + +struct PortTDump { + SunCall call; +}; + +struct PortRDump { + SunCall call; + PortMap *map; + int nmap; +}; + +struct PortTCallit { + SunCall call; + u32int prog; + u32int vers; + u32int proc; + uchar *data; + u32int count; +}; + +struct PortRCallit { + SunCall call; + u32int port; + uchar *data; + u32int count; +}; + +extern SunProg portprog; blob - fbfb9f14fdc59adc724d81f01441547013723fba blob + d4b4cad83e152a40020d96e612c506e031dab5bf --- src/cmd/acme/acme.c +++ src/cmd/acme/acme.c @@ -27,8 +27,6 @@ char wdir[512] = "."; Reffont *reffonts[2]; int snarffd = -1; int mainpid; -int plumbsendfd; -int plumbeditfd; enum{ NSnarf = 1000 /* less than 1024, I/O buffer size */ @@ -180,6 +178,8 @@ threadmain(int argc, char *argv[]) exits("keyboard"); } mainpid = getpid(); + startplumbing(); +/* plumbeditfd = plumbopen("edit", OREAD|OCEXEC); if(plumbeditfd < 0) fprint(2, "acme: can't initialize plumber: %r\n"); @@ -188,6 +188,7 @@ threadmain(int argc, char *argv[]) threadcreate(plumbproc, nil, STACK); } plumbsendfd = plumbopen("send", OWRITE|OCEXEC); +*/ fsysinit(); @@ -355,6 +356,7 @@ acmeerrorinit(void) threadcreate(acmeerrorproc, nil, STACK); } +/* void plumbproc(void *v) { @@ -369,6 +371,7 @@ plumbproc(void *v) sendp(cplumb, m); } } +*/ void keyboardthread(void *v) @@ -674,7 +677,7 @@ waitthread(void *v) textsetselect(t, 0, 0); } if(w->msg[0]) - warning(c->md, "%s: %s\n", c->name, w->msg); + warning(c->md, "%S: %s\n", c->name, w->msg); flushimage(display, 1); } qunlock(&row.lk); blob - 9101ca34fe8f40218f4a43fb9f64bcd18aa6ec1f blob + 94cfa38314bd8e724ad7924f39c662e171407604 --- src/cmd/acme/dat.h +++ src/cmd/acme/dat.h @@ -524,8 +524,6 @@ char *home; char *fontnames[2]; Image *tagcols[NCOL]; Image *textcols[NCOL]; -int plumbsendfd; -int plumbeditfd; extern char wdir[]; /* must use extern because no dimension given */ int editing; int erroutfd; blob - e86af6ec1de57bb8821fbfc96c1be9c4d10d961b blob + d7c9a9b077c0a0a1890ec6dbcaaf4cf04b0651b5 --- src/cmd/acme/elog.c +++ src/cmd/acme/elog.c @@ -170,7 +170,7 @@ eloginsert(File *f, int q0, Rune *r, int nr) elogflush(f); } /* try to merge with previous */ - if(f->elog.type==Insert && q0==f->elog.q0 && (q0+nr)-f->elog.q0elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nrelog.r+f->elog.nr, r, nr); f->elog.nr += nr; return; blob - d81153ecf67a4243f59468f3bd538022c8237807 blob + 74f9f47ced2be1ef35faca6300f8ec6837e8276f --- src/cmd/acme/exec.c +++ src/cmd/acme/exec.c @@ -1472,6 +1472,7 @@ Hard: } } threadexecl(cpid, sfd, "rc", "rc", "-c", t, nil); + warning(nil, "exec rc: %r\n"); Fail: /* threadexec hasn't happened, so send a zero */ blob - a73a7ec05ab83afc4535e81cfc087bb78441df54 blob + 9fba7d7ad79ec07cd4e8ee6de84a0b62b65b0626 --- src/cmd/acme/fns.h +++ src/cmd/acme/fns.h @@ -87,6 +87,7 @@ Rune* skipbl(Rune*, int, int*); Rune* findbl(Rune*, int, int*); char* edittext(Window*, int, Rune*, int); void flushwarnings(int); +void startplumbing(void); #define runemalloc(a) (Rune*)emalloc((a)*sizeof(Rune)) #define runerealloc(a, b) (Rune*)erealloc((a), (b)*sizeof(Rune)) blob - 6b2592883429f687f6eaaca88721ddc53cb15da2 blob + f6c4d4ee5c8abff98f844c983e6dd1aae1463954 --- src/cmd/acme/look.c +++ src/cmd/acme/look.c @@ -8,15 +8,50 @@ #include #include #include +#define Fid FsFid +#include #include +#undef Fid #include "dat.h" #include "fns.h" +FsFid *plumbsendfid; +FsFid *plumbeditfid; + Window* openfile(Text*, Expand*); int nuntitled; void +plumbproc(void *v) +{ + Plumbmsg *m; + + USED(v); + threadsetname("plumbproc"); + for(;;){ + m = plumbrecvfid(plumbeditfid); + if(m == nil) + threadexits(nil); + sendp(cplumb, m); + } +} + +void +startplumbing(void) +{ + plumbeditfid = plumbopenfid("edit", OREAD|OCEXEC); + if(plumbeditfid == nil) + fprint(2, "acme: can't initialize plumber: %r\n"); + else{ + cplumb = chancreate(sizeof(Plumbmsg*), 0); + threadcreate(plumbproc, nil, STACK); + } + plumbsendfid = plumbopenfid("send", OWRITE|OCEXEC); +} + + +void look3(Text *t, uint q0, uint q1, int external) { int n, c, f, expanded; @@ -79,7 +114,7 @@ look3(Text *t, uint q0, uint q1, int external) free(r); goto Return; } - if(plumbsendfd >= 0){ + if(plumbsendfid != nil){ /* send whitespace-delimited word to plumber */ m = emalloc(sizeof(Plumbmsg)); m->src = estrdup("acme"); @@ -121,7 +156,7 @@ look3(Text *t, uint q0, uint q1, int external) m->data = runetobyte(r, q1-q0); m->ndata = strlen(m->data); free(r); - if(m->ndata= 0){ + if(m->ndata= 0){ plumbfree(m); goto Return; } blob - /dev/null blob + 7aed917f52a27e4f384673158244d293cfba9f4d (mode 644) --- /dev/null +++ src/cmd/factotum/BUGS @@ -0,0 +1 @@ +key, delkey, wipe should be in ctl not rpc. blob - /dev/null blob + f555c39484e63dff1bf88b6119d649d7544763b6 (mode 644) --- /dev/null +++ src/cmd/factotum/apop.c @@ -0,0 +1,348 @@ +/* + * APOP, CRAM - MD5 challenge/response authentication + * + * The client does not authenticate the server, hence no CAI. + * + * Protocol: + * + * S -> C: random@domain + * C -> S: hex-response + * S -> C: ok + * + * Note that this is the protocol between factotum and the local + * program, not between the two factotums. The information + * exchanged here is wrapped in the APOP protocol by the local + * programs. + * + * If S sends "bad [msg]" instead of "ok", that is a hint that the key is bad. + * The protocol goes back to "C -> S: user". + */ + +#include "std.h" +#include "dat.h" + +static int +apopcheck(Key *k) +{ + if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){ + werrstr("need user and !password attributes"); + return -1; + } + return 0; +} + +static int +apopclient(Conv *c) +{ + char *chal, *pw, *res; + int astype, nchal, npw, ntry, ret; + uchar resp[MD5dlen]; + Attr *attr; + DigestState *ds; + Key *k; + + chal = nil; + k = nil; + res = nil; + ret = -1; + attr = c->attr; + + if(c->proto == &apop) + astype = AuthApop; + else if(c->proto == &cram) + astype = AuthCram; + else{ + werrstr("bad proto"); + goto out; + } + + c->state = "find key"; + k = keyfetch(c, "%A %s", attr, c->proto->keyprompt); + if(k == nil) + goto out; + + c->state = "read challenge"; + if((nchal = convreadm(c, &chal)) < 0) + goto out; + + for(ntry=1;; ntry++){ + if(c->attr != attr) + freeattr(c->attr); + c->attr = addattrs(copyattr(attr), k->attr); + if((pw = strfindattr(k->privattr, "!password")) == nil){ + werrstr("key has no password (cannot happen?)"); + goto out; + } + npw = strlen(pw); + + switch(astype){ + case AuthApop: + ds = md5((uchar*)chal, nchal, nil, nil); + md5((uchar*)pw, npw, resp, ds); + break; + case AuthCram: + hmac_md5((uchar*)chal, nchal, (uchar*)pw, npw, resp, nil); + break; + } + + /* C->S: APOP user hex-response\n */ + if(ntry == 1) + c->state = "write user"; + else{ + sprint(c->statebuf, "write user (auth attempt #%d)", ntry); + c->state = c->statebuf; + } + if(convprint(c, "%s", strfindattr(k->attr, "user")) < 0) + goto out; + + c->state = "write response"; + if(convprint(c, "%.*H", sizeof resp, resp) < 0) + goto out; + + c->state = "read result"; + if(convreadm(c, &res) < 0) + goto out; + + if(strcmp(res, "ok") == 0) + break; + + if(strncmp(res, "bad ", 4) != 0){ + werrstr("bad result: %s", res); + goto out; + } + + c->state = "replace key"; + if((k = keyreplace(c, k, "%s", res+4)) == nil){ + c->state = "auth failed"; + werrstr("%s", res+4); + goto out; + } + free(res); + res = nil; + } + + werrstr("succeeded"); + ret = 0; + +out: + keyclose(k); + free(chal); + if(c->attr != attr) + freeattr(attr); + return ret; +} + +/* shared with auth dialing routines */ +typedef struct ServerState ServerState; +struct ServerState +{ + int asfd; + Key *k; + Ticketreq tr; + Ticket t; + char *dom; + char *hostid; +}; + +enum +{ + APOPCHALLEN = 128, +}; + +static int apopchal(ServerState*, int, char[APOPCHALLEN]); +static int apopresp(ServerState*, char*, char*); + +static int +apopserver(Conv *c) +{ + char chal[APOPCHALLEN], *user, *resp; + ServerState s; + int astype, ret; + Attr *a; + + ret = -1; + user = nil; + resp = nil; + memset(&s, 0, sizeof s); + s.asfd = -1; + + if(c->proto == &apop) + astype = AuthApop; + else if(c->proto == &cram) + astype = AuthCram; + else{ + werrstr("bad proto"); + goto out; + } + + c->state = "find key"; + if((s.k = plan9authkey(c->attr)) == nil) + goto out; + + a = copyattr(s.k->attr); + a = delattr(a, "proto"); + c->attr = addattrs(c->attr, a); + freeattr(a); + + c->state = "authdial"; + s.hostid = strfindattr(s.k->attr, "user"); + s.dom = strfindattr(s.k->attr, "dom"); + if((s.asfd = xioauthdial(nil, s.dom)) < 0){ + werrstr("authdial %s: %r", s.dom); + goto out; + } + + c->state = "authchal"; + if(apopchal(&s, astype, chal) < 0) + goto out; + + c->state = "write challenge"; + if(convprint(c, "%s", chal) < 0) + goto out; + + for(;;){ + c->state = "read user"; + if(convreadm(c, &user) < 0) + goto out; + + c->state = "read response"; + if(convreadm(c, &resp) < 0) + goto out; + + c->state = "authwrite"; + switch(apopresp(&s, user, resp)){ + case -1: + goto out; + case 0: + c->state = "write status"; + if(convprint(c, "bad authentication failed") < 0) + goto out; + break; + case 1: + c->state = "write status"; + if(convprint(c, "ok") < 0) + goto out; + goto ok; + } + free(user); + free(resp); + user = nil; + resp = nil; + } + +ok: + ret = 0; + c->attr = addcap(c->attr, c->sysuser, &s.t); + +out: + keyclose(s.k); + free(user); + free(resp); +// xioclose(s.asfd); + return ret; +} + +static int +apopchal(ServerState *s, int astype, char chal[APOPCHALLEN]) +{ + char trbuf[TICKREQLEN]; + Ticketreq tr; + + memset(&tr, 0, sizeof tr); + + tr.type = astype; + + if(strlen(s->hostid) >= sizeof tr.hostid){ + werrstr("hostid too long"); + return -1; + } + strcpy(tr.hostid, s->hostid); + + if(strlen(s->dom) >= sizeof tr.authdom){ + werrstr("domain too long"); + return -1; + } + strcpy(tr.authdom, s->dom); + + convTR2M(&tr, trbuf); + if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + return -1; + + if(xioasrdresp(s->asfd, chal, APOPCHALLEN) <= 5) + return -1; + + s->tr = tr; + return 0; +} + +static int +apopresp(ServerState *s, char *user, char *resp) +{ + char tabuf[TICKETLEN+AUTHENTLEN]; + char trbuf[TICKREQLEN]; + int len; + Authenticator a; + Ticket t; + Ticketreq tr; + + tr = s->tr; + if(memrandom(tr.chal, CHALLEN) < 0) + return -1; + + if(strlen(user) >= sizeof tr.uid){ + werrstr("uid too long"); + return -1; + } + strcpy(tr.uid, user); + + convTR2M(&tr, trbuf); + if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + return -1; + + len = strlen(resp); + if(xiowrite(s->asfd, resp, len) != len) + return -1; + + if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN) + return 0; + + convM2T(tabuf, &t, s->k->priv); + if(t.num != AuthTs + || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){ + werrstr("key mismatch with auth server"); + return -1; + } + + convM2A(tabuf+TICKETLEN, &a, t.key); + if(a.num != AuthAc + || memcmp(a.chal, tr.chal, sizeof a.chal) != 0 + || a.id != 0){ + werrstr("key2 mismatch with auth server"); + return -1; + } + + s->t = t; + return 1; +} + +static Role +apoproles[] = +{ + "client", apopclient, + "server", apopserver, + 0 +}; + +Proto apop = { +.name= "apop", +.roles= apoproles, +.checkkey= apopcheck, +.keyprompt= "user? !password?", +}; + +Proto cram = { +.name= "cram", +.roles= apoproles, +.checkkey= apopcheck, +.keyprompt= "user? !password?", +}; blob - /dev/null blob + 94483364776015eab07eb7763030f1803fd5ec65 (mode 644) --- /dev/null +++ src/cmd/factotum/attr.c @@ -0,0 +1,228 @@ +#include "std.h" +#include "dat.h" + +Attr* +addattr(Attr *a, char *fmt, ...) +{ + char buf[1024]; + va_list arg; + Attr *b; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof buf, fmt, arg); + va_end(arg); + b = _parseattr(buf); + a = addattrs(a, b); + setmalloctag(a, getcallerpc(&a)); + _freeattr(b); + return a; +} + +/* + * add attributes in list b to list a. If any attributes are in + * both lists, replace those in a by those in b. + */ +Attr* +addattrs(Attr *a, Attr *b) +{ + int found; + Attr **l, *aa; + + for(; b; b=b->next){ + switch(b->type){ + case AttrNameval: + for(l=&a; *l; ){ + if(strcmp((*l)->name, b->name) != 0){ + l=&(*l)->next; + continue; + } + aa = *l; + *l = aa->next; + aa->next = nil; + freeattr(aa); + } + *l = mkattr(AttrNameval, b->name, b->val, nil); + break; + case AttrQuery: + found = 0; + for(l=&a; *l; l=&(*l)->next) + if((*l)->type==AttrNameval && strcmp((*l)->name, b->name) == 0) + found++; + if(!found) + *l = mkattr(AttrQuery, b->name, b->val, nil); + break; + } + } + return a; +} + +void +setmalloctaghere(void *v) +{ + setmalloctag(v, getcallerpc(&v)); +} + +Attr* +sortattr(Attr *a) +{ + int i; + Attr *anext, *a0, *a1, **l; + + if(a == nil || a->next == nil) + return a; + + /* cut list in halves */ + a0 = nil; + a1 = nil; + i = 0; + for(; a; a=anext){ + anext = a->next; + if(i++%2){ + a->next = a0; + a0 = a; + }else{ + a->next = a1; + a1 = a; + } + } + + /* sort */ + a0 = sortattr(a0); + a1 = sortattr(a1); + + /* merge */ + l = &a; + while(a0 || a1){ + if(a1==nil){ + anext = a0; + a0 = a0->next; + }else if(a0==nil){ + anext = a1; + a1 = a1->next; + }else if(strcmp(a0->name, a1->name) < 0){ + anext = a0; + a0 = a0->next; + }else{ + anext = a1; + a1 = a1->next; + } + *l = anext; + l = &(*l)->next; + } + *l = nil; + return a; +} + +int +attrnamefmt(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; + b = seprint(b, ebuf, " %q?", a->name); + } + return fmtstrcpy(fmt, buf+1); +} + +static int +hasqueries(Attr *a) +{ + for(; a; a=a->next) + if(a->type == AttrQuery) + return 1; + return 0; +} + +char *ignored[] = { + "role", +}; + +static int +ignoreattr(char *s) +{ + int i; + + for(i=0; inext, name)) + if(strcmp(a->val, val) == 0) + return 1; + for(a=_findattr(a1, name); a; a=_findattr(a->next, name)) + if(strcmp(a->val, val) == 0) + return 1; + return 0; +} + +int +matchattr(Attr *pat, Attr *a0, Attr *a1) +{ + int type; + + for(; pat; pat=pat->next){ + type = pat->type; + if(ignoreattr(pat->name)) + type = AttrDefault; + switch(type){ + case AttrQuery: /* name=something be present */ + if(!hasname(a0, a1, pat->name)) + return 0; + break; + case AttrNameval: /* name=val must be present */ + if(!hasnameval(a0, a1, pat->name, pat->val)) + return 0; + break; + case AttrDefault: /* name=val must be present if name=anything is present */ + if(hasname(a0, a1, pat->name) && !hasnameval(a0, a1, pat->name, pat->val)) + return 0; + break; + } + } + return 1; +} + +Attr* +parseattrfmtv(char *fmt, va_list arg) +{ + char *s; + Attr *a; + + s = vsmprint(fmt, arg); + if(s == nil) + sysfatal("vsmprint: out of memory"); + a = parseattr(s); + free(s); + return a; +} + +Attr* +parseattrfmt(char *fmt, ...) +{ + va_list arg; + Attr *a; + + va_start(arg, fmt); + a = parseattrfmtv(fmt, arg); + va_end(arg); + return a; +} blob - /dev/null blob + dcc411cea4af0a3860a830e9c2d10918413a155c (mode 644) --- /dev/null +++ src/cmd/factotum/chap.c @@ -0,0 +1,424 @@ +/* + * CHAP, MSCHAP + * + * The client does not authenticate the server, hence no CAI + * + * Protocol: + * + * S -> C: random 8-byte challenge + * C -> S: user in UTF-8 + * C -> S: Chapreply or MSchapreply structure + * S -> C: ok or 'bad why' + * + * The chap protocol requires the client to give it id=%d, the id of + * the PPP message containing the challenge, which is used + * as part of the response. Because the client protocol is message-id + * specific, there is no point in looping to try multiple keys. + * + * The MS chap protocol actually uses two different hashes, an + * older insecure one called the LM (Lan Manager) hash, and a newer + * more secure one called the NT hash. By default we send back only + * the NT hash, because the LM hash can help an eavesdropper run + * a brute force attack. If the key has an lm attribute, then we send only the + * LM hash. + */ + +#include "std.h" +#include "dat.h" + +enum { + ChapChallen = 8, + + MShashlen = 16, + MSchallen = 8, + MSresplen = 24, +}; + +static int +chapcheck(Key *k) +{ + if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){ + werrstr("need user and !password attributes"); + return -1; + } + return 0; +} + +static void +nthash(uchar hash[MShashlen], char *passwd) +{ + uchar buf[512]; + int i; + + for(i=0; *passwd && i= 'a' && buf[i] <= 'z') + buf[i] += 'A' - 'a'; + + memset(hash, 0, 16); + memcpy(hash, stdtext, 8); + memcpy(hash+8, stdtext, 8); + + desencrypt(hash, buf); + desencrypt(hash+8, buf+7); +} + +static void +mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]) +{ + int i; + uchar buf[21]; + + memset(buf, 0, sizeof(buf)); + memcpy(buf, hash, MShashlen); + + for(i=0; i<3; i++) { + memmove(resp+i*MSchallen, chal, MSchallen); + desencrypt(resp+i*MSchallen, buf+i*7); + } +} + +static int +chapclient(Conv *c) +{ + int id, astype, nchal, npw, ret; + uchar *chal; + char *s, *pw, *user, *res; + Attr *attr; + Key *k; + Chapreply cr; + MSchapreply mscr; + DigestState *ds; + + ret = -1; + chal = nil; + k = nil; + attr = c->attr; + + if(c->proto == &chap){ + astype = AuthChap; + s = strfindattr(attr, "id"); + if(s == nil || *s == 0){ + werrstr("need id=n attr in start message"); + goto out; + } + id = strtol(s, &s, 10); + if(*s != 0 || id < 0 || id >= 256){ + werrstr("bad id=n attr in start message"); + goto out; + } + cr.id = id; + }else if(c->proto == &mschap) + astype = AuthMSchap; + else{ + werrstr("bad proto"); + goto out; + } + + c->state = "find key"; + k = keyfetch(c, "%A %s", attr, c->proto->keyprompt); + if(k == nil) + goto out; + + c->attr = addattrs(copyattr(attr), k->attr); + + c->state = "read challenge"; + if((nchal = convreadm(c, (char**)&chal)) < 0) + goto out; + if(astype == AuthMSchap && nchal != MSchallen) + c->state = "write user"; + if((user = strfindattr(k->attr, "user")) == nil){ + werrstr("key has no user (cannot happen?)"); + goto out; + } + if(convprint(c, "%s", user) < 0) + goto out; + + c->state = "write response"; + if((pw = strfindattr(k->privattr, "!password")) == nil){ + werrstr("key has no password (cannot happen?)"); + goto out; + } + npw = strlen(pw); + + if(astype == AuthChap){ + ds = md5(&cr.id, 1, 0, 0); + md5((uchar*)pw, npw, 0, ds); + md5(chal, nchal, (uchar*)cr.resp, ds); + if(convwrite(c, &cr, sizeof cr) < 0) + goto out; + }else{ + uchar hash[MShashlen]; + + memset(&mscr, 0, sizeof mscr); + if(strfindattr(k->attr, "lm")){ + lmhash(hash, pw); + mschalresp((uchar*)mscr.LMresp, hash, chal); + }else{ + nthash(hash, pw); + mschalresp((uchar*)mscr.NTresp, hash, chal); + } + if(convwrite(c, &mscr, sizeof mscr) < 0) + goto out; + } + + c->state = "read result"; + if(convreadm(c, &res) < 0) + goto out; + if(strcmp(res, "ok") == 0){ + ret = 0; + werrstr("succeeded"); + goto out; + } + if(strncmp(res, "bad ", 4) != 0){ + werrstr("bad result: %s", res); + goto out; + } + + c->state = "replace key"; + keyevict(c, k, "%s", res+4); + werrstr("%s", res+4); + +out: + free(res); + keyclose(k); + free(chal); + if(c->attr != attr) + freeattr(attr); + return ret; +} + +/* shared with auth dialing routines */ +typedef struct ServerState ServerState; +struct ServerState +{ + int asfd; + Key *k; + Ticketreq tr; + Ticket t; + char *dom; + char *hostid; +}; + +static int chapchal(ServerState*, int, char[ChapChallen]); +static int chapresp(ServerState*, char*, char*); + +static int +chapserver(Conv *c) +{ + char chal[ChapChallen], *user, *resp; + ServerState s; + int astype, ret; + Attr *a; + + ret = -1; + user = nil; + resp = nil; + memset(&s, 0, sizeof s); + s.asfd = -1; + + if(c->proto == &chap) + astype = AuthChap; + else if(c->proto == &mschap) + astype = AuthMSchap; + else{ + werrstr("bad proto"); + goto out; + } + + c->state = "find key"; + if((s.k = plan9authkey(c->attr)) == nil) + goto out; + + a = copyattr(s.k->attr); + a = delattr(a, "proto"); + c->attr = addattrs(c->attr, a); + freeattr(a); + + c->state = "authdial"; + s.hostid = strfindattr(s.k->attr, "user"); + s.dom = strfindattr(s.k->attr, "dom"); + if((s.asfd = xioauthdial(nil, s.dom)) < 0){ + werrstr("authdial %s: %r", s.dom); + goto out; + } + + c->state = "authchal"; + if(chapchal(&s, astype, chal) < 0) + goto out; + + c->state = "write challenge"; + if(convprint(c, "%s", chal) < 0) + goto out; + + c->state = "read user"; + if(convreadm(c, &user) < 0) + goto out; + + c->state = "read response"; + if(convreadm(c, &resp) < 0) + goto out; + + c->state = "authwrite"; + switch(chapresp(&s, user, resp)){ + default: + fprint(2, "factotum: bad result from chapresp\n"); + goto out; + case -1: + goto out; + case 0: + c->state = "write status"; + if(convprint(c, "bad authentication failed") < 0) + goto out; + goto out; + + case 1: + c->state = "write status"; + if(convprint(c, "ok") < 0) + goto out; + goto ok; + } + +ok: + ret = 0; + c->attr = addcap(c->attr, c->sysuser, &s.t); + +out: + keyclose(s.k); + free(user); + free(resp); +// xioclose(s.asfd); + return ret; +} + +static int +chapchal(ServerState *s, int astype, char chal[ChapChallen]) +{ + char trbuf[TICKREQLEN]; + Ticketreq tr; + + memset(&tr, 0, sizeof tr); + + tr.type = astype; + + if(strlen(s->hostid) >= sizeof tr.hostid){ + werrstr("hostid too long"); + return -1; + } + strcpy(tr.hostid, s->hostid); + + if(strlen(s->dom) >= sizeof tr.authdom){ + werrstr("domain too long"); + return -1; + } + strcpy(tr.authdom, s->dom); + + convTR2M(&tr, trbuf); + if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + return -1; + + if(xioasrdresp(s->asfd, chal, ChapChallen) <= 5) + return -1; + + s->tr = tr; + return 0; +} + +static int +chapresp(ServerState *s, char *user, char *resp) +{ + char tabuf[TICKETLEN+AUTHENTLEN]; + char trbuf[TICKREQLEN]; + int len; + Authenticator a; + Ticket t; + Ticketreq tr; + + tr = s->tr; + if(memrandom(tr.chal, CHALLEN) < 0) + return -1; + + if(strlen(user) >= sizeof tr.uid){ + werrstr("uid too long"); + return -1; + } + strcpy(tr.uid, user); + + convTR2M(&tr, trbuf); + if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + return -1; + + len = strlen(resp); + if(xiowrite(s->asfd, resp, len) != len) + return -1; + + if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN) + return 0; + + convM2T(tabuf, &t, s->k->priv); + if(t.num != AuthTs + || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){ + werrstr("key mismatch with auth server"); + return -1; + } + + convM2A(tabuf+TICKETLEN, &a, t.key); + if(a.num != AuthAc + || memcmp(a.chal, tr.chal, sizeof a.chal) != 0 + || a.id != 0){ + werrstr("key2 mismatch with auth server"); + return -1; + } + + s->t = t; + return 1; +} + +static Role +chaproles[] = +{ + "client", chapclient, + "server", chapserver, + 0 +}; + +Proto chap = { +.name= "chap", +.roles= chaproles, +.checkkey= chapcheck, +.keyprompt= "user? !password?", +}; + +Proto mschap = { +.name= "mschap", +.roles= chaproles, +.checkkey= chapcheck, +.keyprompt= "user? !password?", +}; + blob - /dev/null blob + 8f49245043b90a6fed49bd1f90336db5d1ac1b16 (mode 644) --- /dev/null +++ src/cmd/factotum/confirm.c @@ -0,0 +1,139 @@ +#include "std.h" +#include "dat.h" + +Logbuf confbuf; + +void +confirmread(Req *r) +{ + lbread(&confbuf, r); +} + +void +confirmflush(Req *r) +{ + lbflush(&confbuf, r); +} + +int +confirmwrite(char *s) +{ + char *t, *ans; + int allow; + ulong tag; + Attr *a; + Conv *c; + + a = _parseattr(s); + if(a == nil){ + werrstr("bad attr"); + return -1; + } + if((t = _strfindattr(a, "tag")) == nil){ + werrstr("no tag"); + return -1; + } + tag = strtoul(t, 0, 0); + if((ans = _strfindattr(a, "answer")) == nil){ + werrstr("no answer"); + return -1; + } + if(strcmp(ans, "yes") == 0) + allow = 1; + else if(strcmp(ans, "no") == 0) + allow = 0; + else{ + werrstr("bad answer"); + return -1; + } + for(c=conv; c; c=c->next){ + if(tag == c->tag){ + nbsendul(c->keywait, allow); + break; + } + } + if(c == nil){ + werrstr("tag not found"); + return -1; + } + return 0; +} + +int +confirmkey(Conv *c, Key *k) +{ + if(*confirminuse == 0) + return -1; + + lbappend(&confbuf, "confirm tag=%lud %A %N", c->tag, k->attr, k->privattr); + c->state = "keyconfirm"; + return recvul(c->keywait); +} + +Logbuf needkeybuf; + +void +needkeyread(Req *r) +{ + lbread(&needkeybuf, r); +} + +void +needkeyflush(Req *r) +{ + lbflush(&needkeybuf, r); +} + +int +needkeywrite(char *s) +{ + char *t; + ulong tag; + Attr *a; + Conv *c; + + a = _parseattr(s); + if(a == nil){ + werrstr("empty write"); + return -1; + } + if((t = _strfindattr(a, "tag")) == nil){ + werrstr("no tag"); + freeattr(a); + return -1; + } + tag = strtoul(t, 0, 0); + for(c=conv; c; c=c->next) + if(c->tag == tag){ + nbsendul(c->keywait, 0); + break; + } + if(c == nil){ + werrstr("tag not found"); + freeattr(a); + return -1; + } + freeattr(a); + return 0; +} + +int +needkey(Conv *c, Attr *a) +{ + if(c == nil || *needkeyinuse == 0) + return -1; + + lbappend(&needkeybuf, "needkey tag=%lud %A", c->tag, a); + return nbrecvul(c->keywait); +} + +int +badkey(Conv *c, Key *k, char *msg, Attr *a) +{ + if(c == nil || *needkeyinuse == 0) + return -1; + + lbappend(&needkeybuf, "badkey tag=%lud %A %N\n%s\n%A", + c->tag, k->attr, k->privattr, msg, a); + return nbrecvul(c->keywait); +} blob - /dev/null blob + 862993f9a5d0ba879789de766123e5c1bda715e9 (mode 644) --- /dev/null +++ src/cmd/factotum/conv.c @@ -0,0 +1,254 @@ +#include "std.h" +#include "dat.h" + +Conv *conv; + +ulong taggen = 1; + +Conv* +convalloc(char *sysuser) +{ + Conv *c; + + c = mallocz(sizeof(Conv), 1); + if(c == nil) + return nil; + c->ref = 1; + c->tag = taggen++; + c->next = conv; + c->sysuser = estrdup(sysuser); + c->state = "nascent"; + c->rpcwait = chancreate(sizeof(void*), 0); + c->keywait = chancreate(sizeof(void*), 0); + strcpy(c->err, "protocol has not started"); + conv = c; + convreset(c); + return c; +} + +void +convreset(Conv *c) +{ + if(c->ref != 1){ + c->hangup = 1; + nbsendp(c->rpcwait, 0); + while(c->ref > 1) + yield(); + c->hangup = 0; + } + c->state = "nascent"; + c->err[0] = '\0'; + freeattr(c->attr); + c->attr = nil; + c->proto = nil; + c->rpc.op = 0; + c->active = 0; + c->done = 0; + c->hangup = 0; +} + +void +convhangup(Conv *c) +{ + c->hangup = 1; + c->rpc.op = 0; + (*c->kickreply)(c); + nbsendp(c->rpcwait, 0); +} + +void +convclose(Conv *c) +{ + Conv *p; + + if(c == nil) + return; + + if(--c->ref > 0) + return; + + if(c == conv){ + conv = c->next; + goto free; + } + for(p=conv; p && p->next!=c; p=p->next) + ; + if(p == nil){ + print("cannot find conv in list\n"); + return; + } + p->next = c->next; + +free: + c->next = nil; + free(c); +} + +static Rpc* +convgetrpc(Conv *c, int want) +{ + for(;;){ + if(c->hangup){ + werrstr("hangup"); + return nil; + } + if(c->rpc.op == RpcUnknown){ + recvp(c->rpcwait); + if(c->hangup){ + werrstr("hangup"); + return nil; + } + if(c->rpc.op == RpcUnknown) + continue; + } + if(want < 0 || c->rpc.op == want) + return &c->rpc; + rpcrespond(c, "phase in state '%s' want '%s'", c->state, rpcname[want]); + } + return nil; /* not reached */ +} + +/* read until the done function tells us that's enough */ +int +convreadfn(Conv *c, int (*done)(void*, int), char **ps) +{ + int n; + Rpc *r; + char *s; + + for(;;){ + r = convgetrpc(c, RpcWrite); + if(r == nil) + return -1; + n = (*done)(r->data, r->count); + if(n == r->count) + break; + rpcrespond(c, "toosmall %d", n); + } + + s = emalloc(r->count+1); + memmove(s, r->data, r->count); + s[r->count] = 0; + *ps = s; + rpcrespond(c, "ok"); + return r->count; +} + +/* + * read until we get a non-zero write. assumes remote side + * knows something about the protocol (is not auth_proxy). + * the remote side typically won't bother with the zero-length + * write to find out the length -- the loop is there only so the + * test program can call auth_proxy on both sides of a pipe + * to play a conversation. + */ +int +convreadm(Conv *c, char **ps) +{ + char *s; + Rpc *r; + + for(;;){ + r = convgetrpc(c, RpcWrite); + if(r == nil) + return -1; + if(r->count > 0) + break; + rpcrespond(c, "toosmall %d", AuthRpcMax); + } + s = emalloc(r->count+1); + memmove(s, r->data, r->count); + s[r->count] = 0; + *ps = s; + rpcrespond(c, "ok"); + return r->count; +} + +/* read exactly count bytes */ +int +convread(Conv *c, void *data, int count) +{ + Rpc *r; + + for(;;){ + r = convgetrpc(c, RpcWrite); + if(r == nil) + return -1; + if(r->count == count) + break; + if(r->count < count) + rpcrespond(c, "toosmall %d", count); + else + rpcrespond(c, "error too much data; want %d got %d", count, r->count); + } + memmove(data, r->data, count); + rpcrespond(c, "ok"); + return 0; +} + +/* write exactly count bytes */ +int +convwrite(Conv *c, void *data, int count) +{ + Rpc *r; + + for(;;){ + r = convgetrpc(c, RpcRead); + if(r == nil) + return -1; + break; + } + rpcrespondn(c, "ok", data, count); + return 0; +} + +/* print to the conversation */ +int +convprint(Conv *c, char *fmt, ...) +{ + char *s; + va_list arg; + int ret; + + va_start(arg, fmt); + s = vsmprint(fmt, arg); + va_end(arg); + if(s == nil) + return -1; + ret = convwrite(c, s, strlen(s)); + free(s); + return ret; +} + +/* ask for a key */ +int +convneedkey(Conv *c, Attr *a) +{ + /* + * Piggyback key requests in the usual RPC channel. + * Wait for the next RPC and then send a key request + * in response. The keys get added out-of-band (via the + * ctl file), so assume the key has been added when the + * next request comes in. + */ + if(convgetrpc(c, -1) == nil) + return -1; + rpcrespond(c, "needkey %A", a); + if(convgetrpc(c, -1) == nil) + return -1; + return 0; +} + +/* ask for a replacement for a bad key*/ +int +convbadkey(Conv *c, Key *k, char *msg, Attr *a) +{ + if(convgetrpc(c, -1) == nil) + return -1; + rpcrespond(c, "badkey %A %N\n%s\n%A", + k->attr, k->privattr, msg, a); + if(convgetrpc(c, -1) == nil) + return -1; + return 0; +} + blob - /dev/null blob + da8280ada929d6305234c49dc6c57fadea6876e7 (mode 644) --- /dev/null +++ src/cmd/factotum/cpu.c @@ -0,0 +1,1117 @@ +/* + * cpu.c - Make a connection to a cpu server + * + * Invoked by listen as 'cpu -R | -N service net netdir' + * by users as 'cpu [-h system] [-c cmd args ...]' + */ + +#include +#include +#include +#include +#include +#include + +#define Maxfdata 8192 + +void remoteside(int); +void fatal(int, char*, ...); +void lclnoteproc(int); +void rmtnoteproc(void); +void catcher(void*, char*); +void usage(void); +void writestr(int, char*, char*, int); +int readstr(int, char*, int); +char *rexcall(int*, char*, char*); +int setamalg(char*); + +int notechan; +char system[32]; +int cflag; +int hflag; +int dbg; +char *user; + +char *srvname = "ncpu"; +char *exportfs = "/bin/exportfs"; +char *ealgs = "rc4_256 sha1"; + +/* message size for exportfs; may be larger so we can do big graphics in CPU window */ +int msgsize = 8192+IOHDRSZ; + +/* authentication mechanisms */ +static int netkeyauth(int); +static int netkeysrvauth(int, char*); +static int p9auth(int); +static int srvp9auth(int, char*); +static int noauth(int); +static int srvnoauth(int, char*); + +typedef struct AuthMethod AuthMethod; +struct AuthMethod { + char *name; /* name of method */ + int (*cf)(int); /* client side authentication */ + int (*sf)(int, char*); /* server side authentication */ +} authmethod[] = +{ + { "p9", p9auth, srvp9auth,}, + { "netkey", netkeyauth, netkeysrvauth,}, +// { "none", noauth, srvnoauth,}, + { nil, nil} +}; +AuthMethod *am = authmethod; /* default is p9 */ + +char *p9authproto = "p9any"; + +int setam(char*); + +void +usage(void) +{ + fprint(2, "usage: cpu [-h system] [-a authmethod] [-e 'crypt hash'] [-c cmd args ...]\n"); + exits("usage"); +} +int fdd; + +void +main(int argc, char **argv) +{ + char dat[128], buf[128], cmd[128], *p, *err; + int fd, ms, kms, data; + + /* see if we should use a larger message size */ + fd = open("/dev/draw", OREAD); + if(fd > 0){ + ms = iounit(fd); + if(ms != 0 && ms < ms+IOHDRSZ) + msgsize = ms+IOHDRSZ; + close(fd); + } + kms = kiounit(); + if(msgsize > kms-IOHDRSZ-100) /* 100 for network packets, etc. */ + msgsize = kms-IOHDRSZ-100; + + user = getuser(); + if(user == nil) + fatal(1, "can't read user name"); + ARGBEGIN{ + case 'a': + p = EARGF(usage()); + if(setam(p) < 0) + fatal(0, "unknown auth method %s", p); + break; + case 'e': + ealgs = EARGF(usage()); + if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) + ealgs = nil; + break; + case 'd': + dbg++; + break; + case 'f': + /* ignored but accepted for compatibility */ + break; + case 'O': + p9authproto = "p9sk2"; + remoteside(1); /* From listen */ + break; + case 'R': /* From listen */ + remoteside(0); + break; + case 'h': + hflag++; + p = EARGF(usage()); + strcpy(system, p); + break; + case 'c': + cflag++; + cmd[0] = '!'; + cmd[1] = '\0'; + while(p = ARGF()) { + strcat(cmd, " "); + strcat(cmd, p); + } + break; + case 'o': + p9authproto = "p9sk2"; + srvname = "cpu"; + break; + case 'u': + user = EARGF(usage()); + break; + default: + usage(); + }ARGEND; + + + if(argc != 0) + usage(); + + if(hflag == 0) { + p = getenv("cpu"); + if(p == 0) + fatal(0, "set $cpu"); + strcpy(system, p); + } + + if(err = rexcall(&data, system, srvname)) + fatal(1, "%s: %s", err, system); + + /* Tell the remote side the command to execute and where our working directory is */ + if(cflag) + writestr(data, cmd, "command", 0); + if(getwd(dat, sizeof(dat)) == 0) + writestr(data, "NO", "dir", 0); + else + writestr(data, dat, "dir", 0); + + /* start up a process to pass along notes */ + lclnoteproc(data); + + /* + * Wait for the other end to execute and start our file service + * of /mnt/term + */ + if(readstr(data, buf, sizeof(buf)) < 0) + fatal(1, "waiting for FS"); + if(strncmp("FS", buf, 2) != 0) { + print("remote cpu: %s", buf); + exits(buf); + } + + /* Begin serving the gnot namespace */ + close(0); + dup(data, 0); + close(data); + sprint(buf, "%d", msgsize); + if(dbg) + execl(exportfs, exportfs, "-dm", buf, 0); + else + execl(exportfs, exportfs, "-m", buf, 0); + fatal(1, "starting exportfs"); +} + +void +fatal(int syserr, char *fmt, ...) +{ + char buf[ERRMAX]; + va_list arg; + + va_start(arg, fmt); + doprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + if(syserr) + fprint(2, "cpu: %s: %r\n", buf); + else + fprint(2, "cpu: %s\n", buf); + exits(buf); +} + +char *negstr = "negotiating authentication method"; + +char bug[256]; + +int +old9p(int fd) +{ + int p[2]; + + if(pipe(p) < 0) + fatal(1, "pipe"); + + switch(rfork(RFPROC|RFFDG|RFNAMEG)) { + case -1: + fatal(1, "rfork srvold9p"); + case 0: + if(fd != 1){ + dup(fd, 1); + close(fd); + } + if(p[0] != 0){ + dup(p[0], 0); + close(p[0]); + } + close(p[1]); + if(0){ + fd = open("/sys/log/cpu", OWRITE); + if(fd != 2){ + dup(fd, 2); + close(fd); + } + execl("/bin/srvold9p", "srvold9p", "-ds", 0); + } else + execl("/bin/srvold9p", "srvold9p", "-s", 0); + fatal(1, "exec srvold9p"); + default: + close(fd); + close(p[0]); + } + return p[1]; +} + +/* Invoked with stdin, stdout and stderr connected to the network connection */ +void +remoteside(int old) +{ + char user[128], home[128], buf[128], xdir[128], cmd[128]; + int i, n, fd, badchdir, gotcmd; + + fd = 0; + + /* negotiate authentication mechanism */ + n = readstr(fd, cmd, sizeof(cmd)); + if(n < 0) + fatal(1, "authenticating"); + if(setamalg(cmd) < 0){ + writestr(fd, "unsupported auth method", nil, 0); + fatal(1, "bad auth method %s", cmd); + } else + writestr(fd, "", "", 1); + + fd = (*am->sf)(fd, user); + if(fd < 0) + fatal(1, "srvauth"); + + /* Set environment values for the user */ + putenv("user", user); + sprint(home, "/usr/%s", user); + putenv("home", home); + + /* Now collect invoking cpu's current directory or possibly a command */ + gotcmd = 0; + if(readstr(fd, xdir, sizeof(xdir)) < 0) + fatal(1, "dir/cmd"); + if(xdir[0] == '!') { + strcpy(cmd, &xdir[1]); + gotcmd = 1; + if(readstr(fd, xdir, sizeof(xdir)) < 0) + fatal(1, "dir"); + } + + /* Establish the new process at the current working directory of the + * gnot */ + badchdir = 0; + if(strcmp(xdir, "NO") == 0) + chdir(home); + else if(chdir(xdir) < 0) { + badchdir = 1; + chdir(home); + } + + /* Start the gnot serving its namespace */ + writestr(fd, "FS", "FS", 0); + writestr(fd, "/", "exportfs dir", 0); + + n = read(fd, buf, sizeof(buf)); + if(n != 2 || buf[0] != 'O' || buf[1] != 'K') + exits("remote tree"); + + if(old) + fd = old9p(fd); + + /* make sure buffers are big by doing fversion explicitly; pick a huge number; other side will trim */ + strcpy(buf, VERSION9P); + if(fversion(fd, 64*1024, buf, sizeof buf) < 0) + exits("fversion failed"); + if(mount(fd, -1, "/mnt/term", MCREATE|MREPL, "") < 0) + exits("mount failed"); + + close(fd); + + /* the remote noteproc uses the mount so it must follow it */ + rmtnoteproc(); + + for(i = 0; i < 3; i++) + close(i); + + if(open("/mnt/term/dev/cons", OREAD) != 0) + exits("open stdin"); + if(open("/mnt/term/dev/cons", OWRITE) != 1) + exits("open stdout"); + dup(1, 2); + + if(badchdir) + print("cpu: failed to chdir to '%s'\n", xdir); + + if(gotcmd) + execl("/bin/rc", "rc", "-lc", cmd, 0); + else + execl("/bin/rc", "rc", "-li", 0); + fatal(1, "exec shell"); +} + +char* +rexcall(int *fd, char *host, char *service) +{ + char *na; + char dir[128]; + char err[ERRMAX]; + char msg[128]; + int n; + + na = netmkaddr(host, 0, service); + if((*fd = dial(na, 0, dir, 0)) < 0) + return "can't dial"; + + /* negotiate authentication mechanism */ + if(ealgs != nil) + snprint(msg, sizeof(msg), "%s %s", am->name, ealgs); + else + snprint(msg, sizeof(msg), "%s", am->name); + writestr(*fd, msg, negstr, 0); + n = readstr(*fd, err, sizeof err); + if(n < 0) + return negstr; + if(*err){ + werrstr(err); + return negstr; + } + + /* authenticate */ + *fd = (*am->cf)(*fd); + if(*fd < 0) + return "can't authenticate"; + return 0; +} + +void +writestr(int fd, char *str, char *thing, int ignore) +{ + int l, n; + + l = strlen(str); + n = write(fd, str, l+1); + if(!ignore && n < 0) + fatal(1, "writing network: %s", thing); +} + +int +readstr(int fd, char *str, int len) +{ + int n; + + while(len) { + n = read(fd, str, 1); + if(n < 0) + return -1; + if(*str == '\0') + return 0; + str++; + len--; + } + return -1; +} + +static int +readln(char *buf, int n) +{ + char *p = buf; + + n--; + while(n > 0){ + if(read(0, p, 1) != 1) + break; + if(*p == '\n' || *p == '\r'){ + *p = 0; + return p-buf; + } + p++; + } + *p = 0; + return p-buf; +} + +/* + * user level challenge/response + */ +static int +netkeyauth(int fd) +{ + char chall[32]; + char resp[32]; + + strcpy(chall, getuser()); + print("user[%s]: ", chall); + if(readln(resp, sizeof(resp)) < 0) + return -1; + if(*resp != 0) + strcpy(chall, resp); + writestr(fd, chall, "challenge/response", 1); + + for(;;){ + if(readstr(fd, chall, sizeof chall) < 0) + break; + if(*chall == 0) + return fd; + print("challenge: %s\nresponse: ", chall); + if(readln(resp, sizeof(resp)) < 0) + break; + writestr(fd, resp, "challenge/response", 1); + } + return -1; +} + +static int +netkeysrvauth(int fd, char *user) +{ + char response[32]; + Chalstate *ch; + int tries; + AuthInfo *ai; + + if(readstr(fd, user, 32) < 0) + return -1; + + ai = nil; + ch = nil; + for(tries = 0; tries < 10; tries++){ + if((ch = auth_challenge("p9cr", user, nil)) == nil) + return -1; + writestr(fd, ch->chal, "challenge", 1); + if(readstr(fd, response, sizeof response) < 0) + return -1; + ch->resp = response; + ch->nresp = strlen(response); + if((ai = auth_response(ch)) != nil) + break; + } + auth_freechal(ch); + if(ai == nil) + return -1; + writestr(fd, "", "challenge", 1); + if(auth_chuid(ai, 0) < 0) + fatal(1, "newns"); + auth_freeAI(ai); + return fd; +} + +static void +mksecret(char *t, uchar *f) +{ + sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", + f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]); +} + +/* + * plan9 authentication followed by rc4 encryption + */ +static int +p9auth(int fd) +{ + uchar key[16]; + uchar digest[SHA1dlen]; + char fromclientsecret[21]; + char fromserversecret[21]; + int i; + AuthInfo *ai; + + ai = auth_proxy(fd, auth_getkey, "proto=%q user=%q role=client", p9authproto, user); + if(ai == nil) + return -1; + memmove(key+4, ai->secret, ai->nsecret); + if(ealgs == nil) + return fd; + + /* exchange random numbers */ + srand(truerand()); + for(i = 0; i < 4; i++) + key[i] = rand(); + if(write(fd, key, 4) != 4) + return -1; + if(readn(fd, key+12, 4) != 4) + return -1; + + /* scramble into two secrets */ + sha1(key, sizeof(key), digest, nil); + mksecret(fromclientsecret, digest); + mksecret(fromserversecret, digest+10); + + /* set up encryption */ + i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil); + if(i < 0) + werrstr("can't establish ssl connection: %r"); + return i; +} + +static int +noauth(int fd) +{ + ealgs = nil; + return fd; +} + +static int +srvnoauth(int fd, char *user) +{ + strcpy(user, getuser()); + ealgs = nil; + return fd; +} + +void +loghex(uchar *p, int n) +{ + char buf[100]; + int i; + + for(i = 0; i < n; i++) + sprint(buf+2*i, "%2.2ux", p[i]); + syslog(0, "cpu", buf); +} + +static int +srvp9auth(int fd, char *user) +{ + uchar key[16]; + uchar digest[SHA1dlen]; + char fromclientsecret[21]; + char fromserversecret[21]; + int i; + AuthInfo *ai; + + ai = auth_proxy(0, nil, "proto=%q role=server", p9authproto); + if(ai == nil) + return -1; + if(auth_chuid(ai, nil) < 0) + return -1; + strcpy(user, ai->cuid); + memmove(key+4, ai->secret, ai->nsecret); + + if(ealgs == nil) + return fd; + + /* exchange random numbers */ + srand(truerand()); + for(i = 0; i < 4; i++) + key[i+12] = rand(); + if(readn(fd, key, 4) != 4) + return -1; + if(write(fd, key+12, 4) != 4) + return -1; + + /* scramble into two secrets */ + sha1(key, sizeof(key), digest, nil); + mksecret(fromclientsecret, digest); + mksecret(fromserversecret, digest+10); + + /* set up encryption */ + i = pushssl(fd, ealgs, fromserversecret, fromclientsecret, nil); + if(i < 0) + werrstr("can't establish ssl connection: %r"); + return i; +} + +/* + * set authentication mechanism + */ +int +setam(char *name) +{ + for(am = authmethod; am->name != nil; am++) + if(strcmp(am->name, name) == 0) + return 0; + am = authmethod; + return -1; +} + +/* + * set authentication mechanism and encryption/hash algs + */ +int +setamalg(char *s) +{ + ealgs = strchr(s, ' '); + if(ealgs != nil) + *ealgs++ = 0; + return setam(s); +} + +char *rmtnotefile = "/mnt/term/dev/cpunote"; + +/* + * loop reading /mnt/term/dev/note looking for notes. + * The child returns to start the shell. + */ +void +rmtnoteproc(void) +{ + int n, fd, pid, notepid; + char buf[256]; + + /* new proc returns to start shell */ + pid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM); + switch(pid){ + case -1: + syslog(0, "cpu", "cpu -R: can't start noteproc: %r"); + return; + case 0: + return; + } + + /* new proc reads notes from other side and posts them to shell */ + switch(notepid = rfork(RFPROC|RFFDG|RFMEM)){ + case -1: + syslog(0, "cpu", "cpu -R: can't start wait proc: %r"); + _exits(0); + case 0: + fd = open(rmtnotefile, OREAD); + if(fd < 0){ + syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile); + _exits(0); + } + + for(;;){ + n = read(fd, buf, sizeof(buf)-1); + if(n <= 0){ + postnote(PNGROUP, pid, "hangup"); + _exits(0); + } + buf[n] = 0; + postnote(PNGROUP, pid, buf); + } + break; + } + + /* original proc waits for shell proc to die and kills note proc */ + for(;;){ + n = waitpid(); + if(n < 0 || n == pid) + break; + } + postnote(PNPROC, notepid, "kill"); + _exits(0); +} + +enum +{ + Qdir, + Qcpunote, + + Nfid = 32, +}; + +struct { + char *name; + Qid qid; + ulong perm; +} fstab[] = +{ + [Qdir] { ".", {Qdir, 0, QTDIR}, DMDIR|0555 }, + [Qcpunote] { "cpunote", {Qcpunote, 0}, 0444 }, +}; + +typedef struct Note Note; +struct Note +{ + Note *next; + char msg[ERRMAX]; +}; + +typedef struct Request Request; +struct Request +{ + Request *next; + Fcall f; +}; + +typedef struct Fid Fid; +struct Fid +{ + int fid; + int file; +}; +Fid fids[Nfid]; + +struct { + Lock; + Note *nfirst, *nlast; + Request *rfirst, *rlast; +} nfs; + +int +fsreply(int fd, Fcall *f) +{ + uchar buf[IOHDRSZ+Maxfdata]; + int n; + + if(dbg) + fprint(2, "<-%F\n", f); + n = convS2M(f, buf, sizeof buf); + if(n > 0){ + if(write(fd, buf, n) != n){ + close(fd); + return -1; + } + } + return 0; +} + +/* match a note read request with a note, reply to the request */ +int +kick(int fd) +{ + Request *rp; + Note *np; + int rv; + + for(;;){ + lock(&nfs); + rp = nfs.rfirst; + np = nfs.nfirst; + if(rp == nil || np == nil){ + unlock(&nfs); + break; + } + nfs.rfirst = rp->next; + nfs.nfirst = np->next; + unlock(&nfs); + + rp->f.type = Rread; + rp->f.count = strlen(np->msg); + rp->f.data = np->msg; + rv = fsreply(fd, &rp->f); + free(rp); + free(np); + if(rv < 0) + return -1; + } + return 0; +} + +void +flushreq(int tag) +{ + Request **l, *rp; + + lock(&nfs); + for(l = &nfs.rfirst; *l != nil; l = &(*l)->next){ + rp = *l; + if(rp->f.tag == tag){ + *l = rp->next; + unlock(&nfs); + free(rp); + return; + } + } + unlock(&nfs); +} + +Fid* +getfid(int fid) +{ + int i, freefid; + + freefid = -1; + for(i = 0; i < Nfid; i++){ + if(freefid < 0 && fids[i].file < 0) + freefid = i; + if(fids[i].fid == fid) + return &fids[i]; + } + if(freefid >= 0){ + fids[freefid].fid = fid; + return &fids[freefid]; + } + return nil; +} + +int +fsstat(int fd, Fid *fid, Fcall *f) +{ + Dir d; + uchar statbuf[256]; + + memset(&d, 0, sizeof(d)); + d.name = fstab[fid->file].name; + d.uid = user; + d.gid = user; + d.muid = user; + d.qid = fstab[fid->file].qid; + d.mode = fstab[fid->file].perm; + d.atime = d.mtime = time(0); + f->stat = statbuf; + f->nstat = convD2M(&d, statbuf, sizeof statbuf); + return fsreply(fd, f); +} + +int +fsread(int fd, Fid *fid, Fcall *f) +{ + Dir d; + uchar buf[256]; + Request *rp; + + switch(fid->file){ + default: + return -1; + case Qdir: + if(f->offset == 0 && f->count >0){ + memset(&d, 0, sizeof(d)); + d.name = fstab[Qcpunote].name; + d.uid = user; + d.gid = user; + d.muid = user; + d.qid = fstab[Qcpunote].qid; + d.mode = fstab[Qcpunote].perm; + d.atime = d.mtime = time(0); + f->count = convD2M(&d, buf, sizeof buf); + f->data = (char*)buf; + } else + f->count = 0; + return fsreply(fd, f); + case Qcpunote: + rp = mallocz(sizeof(*rp), 1); + if(rp == nil) + return -1; + rp->f = *f; + lock(&nfs); + if(nfs.rfirst == nil) + nfs.rfirst = rp; + else + nfs.rlast->next = rp; + nfs.rlast = rp; + unlock(&nfs); + return kick(fd);; + } +} + +char Eperm[] = "permission denied"; +char Enofile[] = "out of files"; +char Enotdir[] = "not a directory"; + +void +notefs(int fd) +{ + uchar buf[IOHDRSZ+Maxfdata]; + int i, j, n; + char err[ERRMAX]; + Fcall f; + Fid *fid, *nfid; + int doreply; + + rfork(RFNOTEG); + fmtinstall('F', fcallconv); + + for(n = 0; n < Nfid; n++) + fids[n].file = -1; + + for(;;){ + n = read9pmsg(fd, buf, sizeof(buf)); + if(n <= 0){ + if(dbg) + fprint(2, "read9pmsg(%d) returns %d: %r\n", fd, n); + break; + } + if(convM2S(buf, n, &f) < 0) + break; + if(dbg) + fprint(2, "->%F\n", &f); + doreply = 1; + fid = getfid(f.fid); + if(fid == nil){ +nofids: + f.type = Rerror; + f.ename = Enofile; + fsreply(fd, &f); + continue; + } + switch(f.type++){ + default: + f.type = Rerror; + f.ename = "unknown type"; + break; + case Tflush: + flushreq(f.oldtag); + break; + case Tversion: + if(f.msize > IOHDRSZ+Maxfdata) + f.msize = IOHDRSZ+Maxfdata; + break; + case Tauth: + f.type = Rerror; + f.ename = "cpu: authentication not required"; + break; + case Tattach: + f.qid = fstab[Qdir].qid; + fid->file = Qdir; + break; + case Twalk: + nfid = nil; + if(f.newfid != f.fid){ + nfid = getfid(f.newfid); + if(nfid == nil) + goto nofids; + nfid->file = fid->file; + fid = nfid; + } + + f.ename = nil; + for(i=0; i MAXWELEM){ + f.type = Rerror; + f.ename = "too many name elements"; + break; + } + if(fid->file != Qdir){ + f.type = Rerror; + f.ename = Enotdir; + break; + } + if(strcmp(f.wname[i], "cpunote") == 0){ + fid->file = Qcpunote; + f.wqid[i] = fstab[Qcpunote].qid; + continue; + } + f.type = Rerror; + f.ename = err; + strcpy(err, "cpu: file \""); + for(j=0; j<=i; j++){ + if(strlen(err)+1+strlen(f.wname[j])+32 > sizeof err) + break; + if(j != 0) + strcat(err, "/"); + strcat(err, f.wname[j]); + } + strcat(err, "\" does not exist"); + break; + } + if(nfid != nil && (f.ename != nil || i < f.nwname)) + nfid ->file = -1; + if(f.type != Rerror) + f.nwqid = i; + break; + case Topen: + if(f.mode != OREAD){ + f.type = Rerror; + f.ename = Eperm; + } + f.qid = fstab[fid->file].qid; + break; + case Tcreate: + f.type = Rerror; + f.ename = Eperm; + break; + case Tread: + if(fsread(fd, fid, &f) < 0) + goto err; + doreply = 0; + break; + case Twrite: + f.type = Rerror; + f.ename = Eperm; + break; + case Tclunk: + fid->file = -1; + break; + case Tremove: + f.type = Rerror; + f.ename = Eperm; + break; + case Tstat: + if(fsstat(fd, fid, &f) < 0) + goto err; + doreply = 0; + break; + case Twstat: + f.type = Rerror; + f.ename = Eperm; + break; + } + if(doreply) + if(fsreply(fd, &f) < 0) + break; + } +err: + if(dbg) + fprint(2, "notefs exiting: %r\n"); + close(fd); +} + +char notebuf[ERRMAX]; + +void +catcher(void*, char *text) +{ + int n; + + n = strlen(text); + if(n >= sizeof(notebuf)) + n = sizeof(notebuf)-1; + memmove(notebuf, text, n); + notebuf[n] = '\0'; + noted(NCONT); +} + +/* + * mount in /dev a note file for the remote side to read. + */ +void +lclnoteproc(int netfd) +{ + int exportfspid; + Waitmsg *w; + Note *np; + int pfd[2]; + + if(pipe(pfd) < 0){ + fprint(2, "cpu: can't start note proc: pipe: %r\n"); + return; + } + + /* new proc mounts and returns to start exportfs */ + switch(exportfspid = rfork(RFPROC|RFNAMEG|RFFDG|RFMEM)){ + case -1: + fprint(2, "cpu: can't start note proc: rfork: %r\n"); + return; + case 0: + close(pfd[0]); + if(mount(pfd[1], -1, "/dev", MBEFORE, "") < 0) + fprint(2, "cpu: can't mount note proc: %r\n"); + close(pfd[1]); + return; + } + + close(netfd); + close(pfd[1]); + + /* new proc listens for note file system rpc's */ + switch(rfork(RFPROC|RFNAMEG|RFMEM)){ + case -1: + fprint(2, "cpu: can't start note proc: rfork1: %r\n"); + _exits(0); + case 0: + notefs(pfd[0]); + _exits(0); + } + + /* original proc waits for notes */ + notify(catcher); + w = nil; + for(;;) { + *notebuf = 0; + free(w); + w = wait(); + if(w == nil) { + if(*notebuf == 0) + break; + np = mallocz(sizeof(Note), 1); + if(np != nil){ + strcpy(np->msg, notebuf); + lock(&nfs); + if(nfs.nfirst == nil) + nfs.nfirst = np; + else + nfs.nlast->next = np; + nfs.nlast = np; + unlock(&nfs); + kick(pfd[0]); + } + unlock(&nfs); + } else if(w->pid == exportfspid) + break; + } + + if(w == nil) + exits(nil); + exits(w->msg); +} blob - /dev/null blob + df44b97da1a66add949dfc0478761c60fa4a2895 (mode 644) --- /dev/null +++ src/cmd/factotum/ctl.c @@ -0,0 +1,158 @@ +#include "std.h" +#include "dat.h" + +/* + * key attr=val... - add a key + * the attr=val pairs are protocol-specific. + * for example, both of these are valid: + * key p9sk1 gre cs.bell-labs.com mysecret + * key p9sk1 gre cs.bell-labs.com 11223344556677 fmt=des7hex + * delkey ... - delete a key + * if given, the attr=val pairs are used to narrow the search + * [maybe should require a password?] + * + * debug - toggle debugging + */ + +static char *msg[] = { + "key", + "delkey", + "debug", +}; + +static int +classify(char *s) +{ + int i; + + for(i=0; i/mnt/factotum/ctl" + * and writes that (incorrectly) contain multiple key lines. + */ + if(p = strchr(a, '\n')){ + if(p[1] != '\0'){ + werrstr("multiline write not allowed"); + return -1; + } + *p = '\0'; + } + + if((p = strchr(a, ' ')) == nil) + p = ""; + else + *p++ = '\0'; + switch(classify(a)){ + default: + werrstr("unknown verb"); + return -1; + case 0: /* key */ + attr = parseattr(p); + /* separate out proto= attributes */ + lprotos = &protos; + for(l=&attr; (*l); ){ + if(strcmp((*l)->name, "proto") == 0){ + *lprotos = *l; + lprotos = &(*l)->next; + *l = (*l)->next; + }else + l = &(*l)->next; + } + *lprotos = nil; + if(protos == nil){ + werrstr("key without protos"); + freeattr(attr); + return -1; + } + + /* separate out private attributes */ + lpriv = &priv; + for(l=&attr; (*l); ){ + if((*l)->name[0] == '!'){ + *lpriv = *l; + lpriv = &(*l)->next; + *l = (*l)->next; + }else + l = &(*l)->next; + } + *lpriv = nil; + + /* add keys */ + ret = 0; + for(pa=protos; pa; pa=pa->next){ + if((proto = protolookup(pa->val)) == nil){ + werrstr("unknown proto %s", pa->val); + ret = -1; + continue; + } + if(proto->checkkey == nil){ + werrstr("proto %s does not accept keys", proto->name); + ret = -1; + continue; + } + k = emalloc(sizeof(Key)); + k->attr = mkattr(AttrNameval, "proto", proto->name, copyattr(attr)); + k->privattr = copyattr(priv); + k->ref = 1; + k->proto = proto; + if((*proto->checkkey)(k) < 0){ + ret = -1; + keyclose(k); + continue; + } + keyadd(k); + keyclose(k); + } + freeattr(attr); + freeattr(priv); + freeattr(protos); + return ret; + case 1: /* delkey */ + nmatch = 0; + attr = parseattr(p); + for(pa=attr; pa; pa=pa->next){ + if(pa->type != AttrQuery && pa->name[0]=='!'){ + werrstr("only !private? patterns are allowed for private fields"); + freeattr(attr); + return -1; + } + } + for(i=0; iattr, ring.key[i]->privattr)){ + nmatch++; + keyclose(ring.key[i]); + ring.nkey--; + memmove(&ring.key[i], &ring.key[i+1], (ring.nkey-i)*sizeof(ring.key[0])); + }else + i++; + } + freeattr(attr); + if(nmatch == 0){ + werrstr("found no keys to delete"); + return -1; + } + return 0; + case 2: /* debug */ + debug ^= 1; + return 0; + } +} blob - /dev/null blob + e0c5b4fb528ef058c637b0c9c4c3230289dd2d75 (mode 644) --- /dev/null +++ src/cmd/factotum/dat.h @@ -0,0 +1,224 @@ +enum +{ + MaxRpc = 2048, /* max size of any protocol message */ + + /* keep in sync with rpc.c:/rpcname */ + RpcUnknown = 0, /* Rpc.op */ + RpcAuthinfo, + RpcAttr, + RpcRead, + RpcStart, + RpcWrite, + + /* thread stack size */ + STACK = 8192, +}; + +typedef struct Conv Conv; +typedef struct Key Key; +typedef struct Logbuf Logbuf; +typedef struct Proto Proto; +typedef struct Ring Ring; +typedef struct Role Role; +typedef struct Rpc Rpc; + +struct Rpc +{ + int op; + void *data; + int count; +}; + +struct Conv +{ + int ref; /* ref count */ + int hangup; /* flag: please hang up */ + int active; /* flag: there is an active thread */ + int done; /* flag: conversation finished successfully */ + ulong tag; /* identifying tag */ + Conv *next; /* in linked list */ + char *sysuser; /* system name for user speaking to us */ + char *state; /* for debugging */ + char statebuf[128]; /* for formatted states */ + char err[ERRMAX]; /* last error */ + + Attr *attr; /* current attributes */ + Proto *proto; /* protocol */ + + Channel *rpcwait; /* wait here for an rpc */ + Rpc rpc; /* current rpc. op==RpcUnknown means none */ + char rpcbuf[MaxRpc]; /* buffer for rpc */ + char reply[MaxRpc]; /* buffer for response */ + int nreply; /* count of response */ + void (*kickreply)(Conv*); /* call to send response */ + Req *req; /* 9P call to read response */ + + Channel *keywait; /* wait here for key confirmation */ + +}; + +struct Key +{ + int ref; /* ref count */ + ulong tag; /* identifying tag: sequence number */ + Attr *attr; /* public attributes */ + Attr *privattr; /* private attributes, like !password */ + Proto *proto; /* protocol owner of key */ + void *priv; /* protocol-specific storage */ +}; + +struct Logbuf +{ + Req *wait; + Req **waitlast; + int rp; + int wp; + char *msg[128]; +}; + +struct Ring +{ + Key **key; + int nkey; +}; + +struct Proto +{ + char *name; /* name of protocol */ + Role *roles; /* list of roles and service functions */ + char *keyprompt; /* required attributes for key proto=name */ + int (*checkkey)(Key*); /* initialize k->priv or reject key */ + void (*closekey)(Key*); /* free k->priv */ +}; + +struct Role +{ + char *name; /* name of role */ + int (*fn)(Conv*); /* service function */ +}; + +extern char *authaddr; /* plan9.c */ +extern int *confirminuse; /* fs.c */ +extern Conv* conv; /* conv.c */ +extern int debug; /* main.c */ +extern char *factname; /* main.c */ +extern Srv fs; /* fs.c */ +extern int *needkeyinuse; /* fs.c */ +extern char *owner; /* main.c */ +extern Proto *prototab[]; /* main.c */ +extern Ring ring; /* key.c */ +extern char *rpcname[]; /* rpc.c */ + +extern char Easproto[]; /* err.c */ + +extern Proto apop; /* apop.c */ +extern Proto chap; /* chap.c */ +extern Proto cram; /* cram.c */ +extern Proto mschap; /* mschap.c */ +extern Proto p9any; /* p9any.c */ +extern Proto p9sk1; /* p9sk1.c */ +extern Proto p9sk2; /* p9sk2.c */ + +/* provided by lib9p */ +#define emalloc emalloc9p +#define erealloc erealloc9p +#define estrdup estrdup9p + +/* hidden in libauth */ +#define attrfmt _attrfmt +#define copyattr _copyattr +#define delattr _delattr +#define findattr _findattr +#define freeattr _freeattr +#define mkattr _mkattr +#define parseattr _parseattr +#define strfindattr _strfindattr + +extern Attr* addattr(Attr*, char*, ...); +/* #pragma varargck argpos addattr 2 */ +extern Attr* addattrs(Attr*, Attr*); +extern Attr* sortattr(Attr*); +extern int attrnamefmt(Fmt*); +/* #pragma varargck type "N" Attr* */ +extern int matchattr(Attr*, Attr*, Attr*); +extern Attr* parseattrfmt(char*, ...); +/* #pragma varargck argpos parseattrfmt 1 */ +extern Attr* parseattrfmtv(char*, va_list); + +extern void confirmflush(Req*); +extern void confirmread(Req*); +extern int confirmwrite(char*); +extern int needkey(Conv*, Attr*); +extern int badkey(Conv*, Key*, char*, Attr*); +extern int confirmkey(Conv*, Key*); + +extern Conv* convalloc(char*); +extern void convclose(Conv*); +extern void convhangup(Conv*); +extern int convneedkey(Conv*, Attr*); +extern int convbadkey(Conv*, Key*, char*, Attr*); +extern int convread(Conv*, void*, int); +extern int convreadm(Conv*, char**); +extern int convprint(Conv*, char*, ...); +/* #pragma varargck argpos convprint 2 */ +extern int convreadfn(Conv*, int(*)(void*, int), char**); +extern void convreset(Conv*); +extern int convwrite(Conv*, void*, int); + +extern int ctlwrite(char*); + +extern char* estrappend(char*, char*, ...); +/* #pragma varargck argpos estrappend 2 */ +extern int hexparse(char*, uchar*, int); + +extern void keyadd(Key*); +extern Key* keylookup(char*, ...); +/* #pragma varargck argpos keylookup 1 */ +extern Key* keyfetch(Conv*, char*, ...); +/* #pragma varargck argpos keyfetch 2 */ +extern void keyclose(Key*); +extern void keyevict(Conv*, Key*, char*, ...); +/* #pragma varargck argpos keyevict 3 */ +extern Key* keyreplace(Conv*, Key*, char*, ...); +/* #pragma varargck argpos keyreplace 3 */ + +extern void lbkick(Logbuf*); +extern void lbappend(Logbuf*, char*, ...); +extern void lbvappend(Logbuf*, char*, va_list); +/* #pragma varargck argpos lbappend 2 */ +extern void lbread(Logbuf*, Req*); +extern void lbflush(Logbuf*, Req*); +extern void flog(char*, ...); +/* #pragma varargck argpos flog 1 */ + +extern void logflush(Req*); +extern void logread(Req*); +extern void logwrite(Req*); + +extern void needkeyread(Req*); +extern void needkeyflush(Req*); +extern int needkeywrite(char*); +extern int needkeyqueue(void); + +extern Attr* addcap(Attr*, char*, Ticket*); +extern Key* plan9authkey(Attr*); +extern int _authdial(char*, char*); + +extern int memrandom(void*, int); + +extern Proto* protolookup(char*); + +extern char* readcons(char *prompt, char *def, int raw); + +extern int rpcwrite(Conv*, void*, int); +extern void rpcrespond(Conv*, char*, ...); +/* #pragma varargck argpos rpcrespond 2 */ +extern void rpcrespondn(Conv*, char*, void*, int); +extern void rpcexec(Conv*); + +extern int xioauthdial(char*, char*); +extern void xioclose(int); +extern int xiodial(char*, char*, char*, int*); +extern int xiowrite(int, void*, int); +extern int xioasrdresp(int, void*, int); +extern int xioasgetticket(int, char*, char*); blob - /dev/null blob + 47d2a4e17aeb1754c31a134722adbf4f85e074f6 (mode 644) --- /dev/null +++ src/cmd/factotum/fs.acid @@ -0,0 +1,1686 @@ +sizeof_1_ = 8; +aggr _1_ +{ + 'D' 0 lo; + 'D' 4 hi; +}; + +defn +_1_(addr) { + complex _1_ addr; + print(" lo ", addr.lo, "\n"); + print(" hi ", addr.hi, "\n"); +}; + +sizeofFPdbleword = 8; +aggr FPdbleword +{ + 'F' 0 x; + { + 'D' 0 lo; + 'D' 4 hi; + }; +}; + +defn +FPdbleword(addr) { + complex FPdbleword addr; + print(" x ", addr.x, "\n"); + print("_1_ {\n"); + _1_(addr+0); + print("}\n"); +}; + +UTFmax = 3; +Runesync = 128; +Runeself = 128; +Runeerror = 128; +sizeofFconv = 24; +aggr Fconv +{ + 'X' 0 out; + 'X' 4 eout; + 'D' 8 f1; + 'D' 12 f2; + 'D' 16 f3; + 'D' 20 chr; +}; + +defn +Fconv(addr) { + complex Fconv addr; + print(" out ", addr.out\X, "\n"); + print(" eout ", addr.eout\X, "\n"); + print(" f1 ", addr.f1, "\n"); + print(" f2 ", addr.f2, "\n"); + print(" f3 ", addr.f3, "\n"); + print(" chr ", addr.chr, "\n"); +}; + +sizeofTm = 40; +aggr Tm +{ + 'D' 0 sec; + 'D' 4 min; + 'D' 8 hour; + 'D' 12 mday; + 'D' 16 mon; + 'D' 20 year; + 'D' 24 wday; + 'D' 28 yday; + 'a' 32 zone; + 'D' 36 tzoff; +}; + +defn +Tm(addr) { + complex Tm addr; + print(" sec ", addr.sec, "\n"); + print(" min ", addr.min, "\n"); + print(" hour ", addr.hour, "\n"); + print(" mday ", addr.mday, "\n"); + print(" mon ", addr.mon, "\n"); + print(" year ", addr.year, "\n"); + print(" wday ", addr.wday, "\n"); + print(" yday ", addr.yday, "\n"); + print(" zone ", addr.zone, "\n"); + print(" tzoff ", addr.tzoff, "\n"); +}; + +PNPROC = 1; +PNGROUP = 2; +sizeofLock = 4; +aggr Lock +{ + 'D' 0 val; +}; + +defn +Lock(addr) { + complex Lock addr; + print(" val ", addr.val, "\n"); +}; + +sizeofQLp = 12; +aggr QLp +{ + 'D' 0 inuse; + 'A' QLp 4 next; + 'C' 8 state; +}; + +defn +QLp(addr) { + complex QLp addr; + print(" inuse ", addr.inuse, "\n"); + print(" next ", addr.next\X, "\n"); + print(" state ", addr.state, "\n"); +}; + +sizeofQLock = 16; +aggr QLock +{ + Lock 0 lock; + 'D' 4 locked; + 'A' QLp 8 $head; + 'A' QLp 12 $tail; +}; + +defn +QLock(addr) { + complex QLock addr; + print("Lock lock {\n"); + Lock(addr.lock); + print("}\n"); + print(" locked ", addr.locked, "\n"); + print(" $head ", addr.$head\X, "\n"); + print(" $tail ", addr.$tail\X, "\n"); +}; + +sizeofRWLock = 20; +aggr RWLock +{ + Lock 0 lock; + 'D' 4 readers; + 'D' 8 writer; + 'A' QLp 12 $head; + 'A' QLp 16 $tail; +}; + +defn +RWLock(addr) { + complex RWLock addr; + print("Lock lock {\n"); + Lock(addr.lock); + print("}\n"); + print(" readers ", addr.readers, "\n"); + print(" writer ", addr.writer, "\n"); + print(" $head ", addr.$head\X, "\n"); + print(" $tail ", addr.$tail\X, "\n"); +}; + +RFNAMEG = 1; +RFENVG = 2; +RFFDG = 4; +RFNOTEG = 8; +RFPROC = 16; +RFMEM = 32; +RFNOWAIT = 64; +RFCNAMEG = 1024; +RFCENVG = 2048; +RFCFDG = 4096; +RFREND = 8192; +RFNOMNT = 16384; +sizeofQid = 16; +aggr Qid +{ + 'W' 0 path; + 'U' 8 vers; + 'b' 12 type; +}; + +defn +Qid(addr) { + complex Qid addr; + print(" path ", addr.path, "\n"); + print(" vers ", addr.vers, "\n"); + print(" type ", addr.type, "\n"); +}; + +sizeofDir = 60; +aggr Dir +{ + 'u' 0 type; + 'U' 4 dev; + Qid 8 qid; + 'U' 24 mode; + 'U' 28 atime; + 'U' 32 mtime; + 'V' 36 length; + 'X' 44 name; + 'X' 48 uid; + 'X' 52 gid; + 'X' 56 muid; +}; + +defn +Dir(addr) { + complex Dir addr; + print(" type ", addr.type, "\n"); + print(" dev ", addr.dev, "\n"); + print("Qid qid {\n"); + Qid(addr.qid); + print("}\n"); + print(" mode ", addr.mode, "\n"); + print(" atime ", addr.atime, "\n"); + print(" mtime ", addr.mtime, "\n"); + print(" length ", addr.length, "\n"); + print(" name ", addr.name\X, "\n"); + print(" uid ", addr.uid\X, "\n"); + print(" gid ", addr.gid\X, "\n"); + print(" muid ", addr.muid\X, "\n"); +}; + +sizeofWaitmsg = 20; +aggr Waitmsg +{ + 'D' 0 pid; + 'a' 4 time; + 'X' 16 msg; +}; + +defn +Waitmsg(addr) { + complex Waitmsg addr; + print(" pid ", addr.pid, "\n"); + print(" time ", addr.time, "\n"); + print(" msg ", addr.msg\X, "\n"); +}; + +sizeofIOchunk = 8; +aggr IOchunk +{ + 'X' 0 addr; + 'U' 4 len; +}; + +defn +IOchunk(addr) { + complex IOchunk addr; + print(" addr ", addr.addr\X, "\n"); + print(" len ", addr.len, "\n"); +}; + +MAXCHLEN = 256; +MAXNAMELEN = 256; +MD5LEN = 16; +ARok = 0; +ARdone = 1; +ARerror = 2; +ARneedkey = 3; +ARbadkey = 4; +ARwritenext = 5; +ARtoosmall = 6; +ARtoobig = 7; +ARrpcfailure = 8; +ARphase = 9; +AuthRpcMax = 4096; +sizeofAuthRpc = 8208; +aggr AuthRpc +{ + 'D' 0 afd; + 'X' 4 verb; + 'a' 8 ibuf; + 'a' 4104 obuf; + 'X' 8200 arg; + 'U' 8204 narg; +}; + +defn +AuthRpc(addr) { + complex AuthRpc addr; + print(" afd ", addr.afd, "\n"); + print(" verb ", addr.verb\X, "\n"); + print(" ibuf ", addr.ibuf, "\n"); + print(" obuf ", addr.obuf, "\n"); + print(" arg ", addr.arg\X, "\n"); + print(" narg ", addr.narg, "\n"); +}; + +sizeofAuthInfo = 20; +aggr AuthInfo +{ + 'X' 0 cuid; + 'X' 4 suid; + 'X' 8 cap; + 'D' 12 nsecret; + 'X' 16 secret; +}; + +defn +AuthInfo(addr) { + complex AuthInfo addr; + print(" cuid ", addr.cuid\X, "\n"); + print(" suid ", addr.suid\X, "\n"); + print(" cap ", addr.cap\X, "\n"); + print(" nsecret ", addr.nsecret, "\n"); + print(" secret ", addr.secret\X, "\n"); +}; + +sizeofChalstate = 540; +aggr Chalstate +{ + 'X' 0 user; + 'a' 4 chal; + 'D' 260 nchal; + 'X' 264 resp; + 'D' 268 nresp; + 'D' 272 afd; + 'A' AuthRpc 276 rpc; + 'a' 280 userbuf; + 'D' 536 userinchal; +}; + +defn +Chalstate(addr) { + complex Chalstate addr; + print(" user ", addr.user\X, "\n"); + print(" chal ", addr.chal, "\n"); + print(" nchal ", addr.nchal, "\n"); + print(" resp ", addr.resp\X, "\n"); + print(" nresp ", addr.nresp, "\n"); + print(" afd ", addr.afd, "\n"); + print(" rpc ", addr.rpc\X, "\n"); + print(" userbuf ", addr.userbuf, "\n"); + print(" userinchal ", addr.userinchal, "\n"); +}; + +sizeofChapreply = 20; +aggr Chapreply +{ + 'b' 0 id; + 'a' 1 resp; +}; + +defn +Chapreply(addr) { + complex Chapreply addr; + print(" id ", addr.id, "\n"); + print(" resp ", addr.resp, "\n"); +}; + +sizeofMSchapreply = 48; +aggr MSchapreply +{ + 'a' 0 LMresp; + 'a' 24 NTresp; +}; + +defn +MSchapreply(addr) { + complex MSchapreply addr; + print(" LMresp ", addr.LMresp, "\n"); + print(" NTresp ", addr.NTresp, "\n"); +}; + +sizeofUserPasswd = 8; +aggr UserPasswd +{ + 'X' 0 user; + 'X' 4 passwd; +}; + +defn +UserPasswd(addr) { + complex UserPasswd addr; + print(" user ", addr.user\X, "\n"); + print(" passwd ", addr.passwd\X, "\n"); +}; + +ANAMELEN = 28; +AERRLEN = 64; +DOMLEN = 48; +DESKEYLEN = 7; +CHALLEN = 8; +NETCHLEN = 16; +CONFIGLEN = 14; +SECRETLEN = 32; +KEYDBOFF = 8; +OKEYDBLEN = 41; +KEYDBLEN = 73; +OMD5LEN = 16; +AuthTreq = 1; +AuthChal = 2; +AuthPass = 3; +AuthOK = 4; +AuthErr = 5; +AuthMod = 6; +AuthApop = 7; +AuthOKvar = 9; +AuthChap = 10; +AuthMSchap = 11; +AuthCram = 12; +AuthHttp = 13; +AuthVNC = 14; +AuthTs = 64; +AuthTc = 65; +AuthAs = 66; +AuthAc = 67; +AuthTp = 68; +AuthHr = 69; +sizeofTicketreq = 144; +aggr Ticketreq +{ + 'C' 0 type; + 'a' 1 authid; + 'a' 29 authdom; + 'a' 77 chal; + 'a' 85 hostid; + 'a' 113 uid; +}; + +defn +Ticketreq(addr) { + complex Ticketreq addr; + print(" type ", addr.type, "\n"); + print(" authid ", addr.authid, "\n"); + print(" authdom ", addr.authdom, "\n"); + print(" chal ", addr.chal, "\n"); + print(" hostid ", addr.hostid, "\n"); + print(" uid ", addr.uid, "\n"); +}; + +sizeofTicket = 72; +aggr Ticket +{ + 'C' 0 num; + 'a' 1 chal; + 'a' 9 cuid; + 'a' 37 suid; + 'a' 65 key; +}; + +defn +Ticket(addr) { + complex Ticket addr; + print(" num ", addr.num, "\n"); + print(" chal ", addr.chal, "\n"); + print(" cuid ", addr.cuid, "\n"); + print(" suid ", addr.suid, "\n"); + print(" key ", addr.key, "\n"); +}; + +sizeofAuthenticator = 16; +aggr Authenticator +{ + 'C' 0 num; + 'a' 1 chal; + 'U' 12 id; +}; + +defn +Authenticator(addr) { + complex Authenticator addr; + print(" num ", addr.num, "\n"); + print(" chal ", addr.chal, "\n"); + print(" id ", addr.id, "\n"); +}; + +sizeofPasswordreq = 92; +aggr Passwordreq +{ + 'C' 0 num; + 'a' 1 old; + 'a' 29 new; + 'C' 57 changesecret; + 'a' 58 secret; +}; + +defn +Passwordreq(addr) { + complex Passwordreq addr; + print(" num ", addr.num, "\n"); + print(" old ", addr.old, "\n"); + print(" new ", addr.new, "\n"); + print(" changesecret ", addr.changesecret, "\n"); + print(" secret ", addr.secret, "\n"); +}; + +sizeofOChapreply = 48; +aggr OChapreply +{ + 'b' 0 id; + 'a' 1 uid; + 'a' 29 resp; +}; + +defn +OChapreply(addr) { + complex OChapreply addr; + print(" id ", addr.id, "\n"); + print(" uid ", addr.uid, "\n"); + print(" resp ", addr.resp, "\n"); +}; + +sizeofOMSchapreply = 76; +aggr OMSchapreply +{ + 'a' 0 uid; + 'a' 28 LMresp; + 'a' 52 NTresp; +}; + +defn +OMSchapreply(addr) { + complex OMSchapreply addr; + print(" uid ", addr.uid, "\n"); + print(" LMresp ", addr.LMresp, "\n"); + print(" NTresp ", addr.NTresp, "\n"); +}; + +NVwrite = 1; +NVwriteonerr = 2; +sizeofNvrsafe = 112; +aggr Nvrsafe +{ + 'a' 0 machkey; + 'b' 7 machsum; + 'a' 8 authkey; + 'b' 15 authsum; + 'a' 16 config; + 'b' 30 configsum; + 'a' 31 authid; + 'b' 59 authidsum; + 'a' 60 authdom; + 'b' 108 authdomsum; +}; + +defn +Nvrsafe(addr) { + complex Nvrsafe addr; + print(" machkey ", addr.machkey, "\n"); + print(" machsum ", addr.machsum, "\n"); + print(" authkey ", addr.authkey, "\n"); + print(" authsum ", addr.authsum, "\n"); + print(" config ", addr.config, "\n"); + print(" configsum ", addr.configsum, "\n"); + print(" authid ", addr.authid, "\n"); + print(" authidsum ", addr.authidsum, "\n"); + print(" authdom ", addr.authdom, "\n"); + print(" authdomsum ", addr.authdomsum, "\n"); +}; + +AESbsize = 16; +AESmaxkey = 32; +AESmaxrounds = 14; +sizeofAESstate = 540; +aggr AESstate +{ + 'U' 0 setup; + 'D' 4 rounds; + 'D' 8 keybytes; + 'a' 12 key; + 'a' 44 ekey; + 'a' 284 dkey; + 'a' 524 ivec; +}; + +defn +AESstate(addr) { + complex AESstate addr; + print(" setup ", addr.setup, "\n"); + print(" rounds ", addr.rounds, "\n"); + print(" keybytes ", addr.keybytes, "\n"); + print(" key ", addr.key, "\n"); + print(" ekey ", addr.ekey, "\n"); + print(" dkey ", addr.dkey, "\n"); + print(" ivec ", addr.ivec, "\n"); +}; + +BFbsize = 8; +BFrounds = 16; +sizeofBFstate = 4236; +aggr BFstate +{ + 'U' 0 setup; + 'a' 4 key; + 'a' 60 ivec; + 'a' 68 pbox; + 'a' 140 sbox; +}; + +defn +BFstate(addr) { + complex BFstate addr; + print(" setup ", addr.setup, "\n"); + print(" key ", addr.key, "\n"); + print(" ivec ", addr.ivec, "\n"); + print(" pbox ", addr.pbox, "\n"); + print(" sbox ", addr.sbox, "\n"); +}; + +DESbsize = 8; +sizeofDESstate = 148; +aggr DESstate +{ + 'U' 0 setup; + 'a' 4 key; + 'a' 12 expanded; + 'a' 140 ivec; +}; + +defn +DESstate(addr) { + complex DESstate addr; + print(" setup ", addr.setup, "\n"); + print(" key ", addr.key, "\n"); + print(" expanded ", addr.expanded, "\n"); + print(" ivec ", addr.ivec, "\n"); +}; + +DES3E = 0; +DES3D = 1; +DES3EEE = 0; +DES3EDE = 2; +DES3DED = 5; +DES3DDD = 7; +sizeofDES3state = 420; +aggr DES3state +{ + 'U' 0 setup; + 'a' 4 key; + 'a' 28 expanded; + 'a' 412 ivec; +}; + +defn +DES3state(addr) { + complex DES3state addr; + print(" setup ", addr.setup, "\n"); + print(" key ", addr.key, "\n"); + print(" expanded ", addr.expanded, "\n"); + print(" ivec ", addr.ivec, "\n"); +}; + +SHA1dlen = 20; +MD4dlen = 16; +MD5dlen = 16; +sizeofDigestState = 160; +aggr DigestState +{ + 'U' 0 len; + 'a' 4 state; + 'a' 24 buf; + 'D' 152 blen; + 'C' 156 malloced; + 'C' 157 seeded; +}; + +defn +DigestState(addr) { + complex DigestState addr; + print(" len ", addr.len, "\n"); + print(" state ", addr.state, "\n"); + print(" buf ", addr.buf, "\n"); + print(" blen ", addr.blen, "\n"); + print(" malloced ", addr.malloced, "\n"); + print(" seeded ", addr.seeded, "\n"); +}; + +sizeofRC4state = 260; +aggr RC4state +{ + 'a' 0 state; + 'b' 256 x; + 'b' 257 y; +}; + +defn +RC4state(addr) { + complex RC4state addr; + print(" state ", addr.state, "\n"); + print(" x ", addr.x, "\n"); + print(" y ", addr.y, "\n"); +}; + +sizeofRSApub = 8; +aggr RSApub +{ + 'X' 0 n; + 'X' 4 ek; +}; + +defn +RSApub(addr) { + complex RSApub addr; + print(" n ", addr.n\X, "\n"); + print(" ek ", addr.ek\X, "\n"); +}; + +sizeofRSApriv = 32; +aggr RSApriv +{ + RSApub 0 pub; + 'X' 8 dk; + 'X' 12 p; + 'X' 16 q; + 'X' 20 kp; + 'X' 24 kq; + 'X' 28 c2; +}; + +defn +RSApriv(addr) { + complex RSApriv addr; + print("RSApub pub {\n"); + RSApub(addr.pub); + print("}\n"); + print(" dk ", addr.dk\X, "\n"); + print(" p ", addr.p\X, "\n"); + print(" q ", addr.q\X, "\n"); + print(" kp ", addr.kp\X, "\n"); + print(" kq ", addr.kq\X, "\n"); + print(" c2 ", addr.c2\X, "\n"); +}; + +sizeofEGpub = 12; +aggr EGpub +{ + 'X' 0 p; + 'X' 4 alpha; + 'X' 8 key; +}; + +defn +EGpub(addr) { + complex EGpub addr; + print(" p ", addr.p\X, "\n"); + print(" alpha ", addr.alpha\X, "\n"); + print(" key ", addr.key\X, "\n"); +}; + +sizeofEGpriv = 16; +aggr EGpriv +{ + EGpub 0 pub; + 'X' 12 secret; +}; + +defn +EGpriv(addr) { + complex EGpriv addr; + print("EGpub pub {\n"); + EGpub(addr.pub); + print("}\n"); + print(" secret ", addr.secret\X, "\n"); +}; + +sizeofEGsig = 8; +aggr EGsig +{ + 'X' 0 r; + 'X' 4 s; +}; + +defn +EGsig(addr) { + complex EGsig addr; + print(" r ", addr.r\X, "\n"); + print(" s ", addr.s\X, "\n"); +}; + +sizeofString = 20; +aggr String +{ + { + 'D' 0 val; + }; + 'X' 4 base; + 'X' 8 end; + 'X' 12 ptr; + 'd' 16 ref; + 'b' 18 fixed; +}; + +defn +String(addr) { + complex String addr; + print("Lock {\n"); + Lock(addr+0); + print("}\n"); + print(" base ", addr.base\X, "\n"); + print(" end ", addr.end\X, "\n"); + print(" ptr ", addr.ptr\X, "\n"); + print(" ref ", addr.ref, "\n"); + print(" fixed ", addr.fixed, "\n"); +}; + +sizeofChannel = 156; +aggr Channel +{ + 'D' 0 s; + 'U' 4 f; + 'U' 8 n; + 'D' 12 e; + 'D' 16 freed; + 'U' 20 qused; + 'a' 24 qentry; + 'a' 152 v; +}; + +defn +Channel(addr) { + complex Channel addr; + print(" s ", addr.s, "\n"); + print(" f ", addr.f, "\n"); + print(" n ", addr.n, "\n"); + print(" e ", addr.e, "\n"); + print(" freed ", addr.freed, "\n"); + print(" qused ", addr.qused, "\n"); + print(" qentry ", addr.qentry, "\n"); + print(" v ", addr.v, "\n"); +}; + +sizeofAlt = 20; +aggr Alt +{ + 'A' Channel 0 c; + 'X' 4 v; + 'D' 8 op; + 'A' Channel 12 tag; + 'U' 16 q; +}; + +defn +Alt(addr) { + complex Alt addr; + print(" c ", addr.c\X, "\n"); + print(" v ", addr.v\X, "\n"); + print(" op ", addr.op, "\n"); + print(" tag ", addr.tag\X, "\n"); + print(" q ", addr.q, "\n"); +}; + +sizeofRef = 4; +aggr Ref +{ + 'D' 0 ref; +}; + +defn +Ref(addr) { + complex Ref addr; + print(" ref ", addr.ref, "\n"); +}; + +sizeof_2_ = 8; +aggr _2_ +{ + 'U' 0 msize; + 'X' 4 version; +}; + +defn +_2_(addr) { + complex _2_ addr; + print(" msize ", addr.msize, "\n"); + print(" version ", addr.version\X, "\n"); +}; + +sizeof_3_ = 4; +aggr _3_ +{ + 'u' 0 oldtag; +}; + +defn +_3_(addr) { + complex _3_ addr; + print(" oldtag ", addr.oldtag, "\n"); +}; + +sizeof_4_ = 4; +aggr _4_ +{ + 'X' 0 ename; +}; + +defn +_4_(addr) { + complex _4_ addr; + print(" ename ", addr.ename\X, "\n"); +}; + +sizeof_5_ = 20; +aggr _5_ +{ + Qid 0 qid; + 'U' 16 iounit; +}; + +defn +_5_(addr) { + complex _5_ addr; + print("Qid qid {\n"); + Qid(addr.qid); + print("}\n"); + print(" iounit ", addr.iounit, "\n"); +}; + +sizeof_6_ = 16; +aggr _6_ +{ + Qid 0 aqid; +}; + +defn +_6_(addr) { + complex _6_ addr; + print("Qid aqid {\n"); + Qid(addr.aqid); + print("}\n"); +}; + +sizeof_7_ = 12; +aggr _7_ +{ + 'U' 0 afid; + 'X' 4 uname; + 'X' 8 aname; +}; + +defn +_7_(addr) { + complex _7_ addr; + print(" afid ", addr.afid, "\n"); + print(" uname ", addr.uname\X, "\n"); + print(" aname ", addr.aname\X, "\n"); +}; + +sizeof_8_ = 12; +aggr _8_ +{ + 'U' 0 perm; + 'X' 4 name; + 'b' 8 mode; +}; + +defn +_8_(addr) { + complex _8_ addr; + print(" perm ", addr.perm, "\n"); + print(" name ", addr.name\X, "\n"); + print(" mode ", addr.mode, "\n"); +}; + +sizeof_9_ = 72; +aggr _9_ +{ + 'U' 0 newfid; + 'u' 4 nwname; + 'a' 8 wname; +}; + +defn +_9_(addr) { + complex _9_ addr; + print(" newfid ", addr.newfid, "\n"); + print(" nwname ", addr.nwname, "\n"); + print(" wname ", addr.wname, "\n"); +}; + +sizeof_10_ = 260; +aggr _10_ +{ + 'u' 0 nwqid; + 'a' 4 wqid; +}; + +defn +_10_(addr) { + complex _10_ addr; + print(" nwqid ", addr.nwqid, "\n"); + print(" wqid ", addr.wqid, "\n"); +}; + +sizeof_11_ = 16; +aggr _11_ +{ + 'V' 0 offset; + 'U' 8 count; + 'X' 12 data; +}; + +defn +_11_(addr) { + complex _11_ addr; + print(" offset ", addr.offset, "\n"); + print(" count ", addr.count, "\n"); + print(" data ", addr.data\X, "\n"); +}; + +sizeof_12_ = 8; +aggr _12_ +{ + 'u' 0 nstat; + 'X' 4 stat; +}; + +defn +_12_(addr) { + complex _12_ addr; + print(" nstat ", addr.nstat, "\n"); + print(" stat ", addr.stat\X, "\n"); +}; + +sizeof_13_ = 260; +aggr _13_ +{ + { + 'U' 0 msize; + 'X' 4 version; + }; + { + 'u' 0 oldtag; + }; + { + 'X' 0 ename; + }; + { + Qid 0 qid; + 'U' 16 iounit; + }; + { + Qid 0 aqid; + }; + { + 'U' 0 afid; + 'X' 4 uname; + 'X' 8 aname; + }; + { + 'U' 0 perm; + 'X' 4 name; + 'b' 8 mode; + }; + { + 'U' 0 newfid; + 'u' 4 nwname; + 'a' 8 wname; + }; + { + 'u' 0 nwqid; + 'a' 4 wqid; + }; + { + 'V' 0 offset; + 'U' 8 count; + 'X' 12 data; + }; + { + 'u' 0 nstat; + 'X' 4 stat; + }; +}; + +defn +_13_(addr) { + complex _13_ addr; + print("_2_ {\n"); + _2_(addr+0); + print("}\n"); + print("_3_ {\n"); + _3_(addr+0); + print("}\n"); + print("_4_ {\n"); + _4_(addr+0); + print("}\n"); + print("_5_ {\n"); + _5_(addr+0); + print("}\n"); + print("_6_ {\n"); + _6_(addr+0); + print("}\n"); + print("_7_ {\n"); + _7_(addr+0); + print("}\n"); + print("_8_ {\n"); + _8_(addr+0); + print("}\n"); + print("_9_ {\n"); + _9_(addr+0); + print("}\n"); + print("_10_ {\n"); + _10_(addr+0); + print("}\n"); + print("_11_ {\n"); + _11_(addr+0); + print("}\n"); + print("_12_ {\n"); + _12_(addr+0); + print("}\n"); +}; + +sizeofFcall = 272; +aggr Fcall +{ + 'b' 0 type; + 'U' 4 fid; + 'u' 8 tag; + { + { + 'U' 12 msize; + 'X' 16 version; + }; + { + 'u' 12 oldtag; + }; + { + 'X' 12 ename; + }; + { + Qid 12 qid; + 'U' 28 iounit; + }; + { + Qid 12 aqid; + }; + { + 'U' 12 afid; + 'X' 16 uname; + 'X' 20 aname; + }; + { + 'U' 12 perm; + 'X' 16 name; + 'b' 20 mode; + }; + { + 'U' 12 newfid; + 'u' 16 nwname; + 'a' 20 wname; + }; + { + 'u' 12 nwqid; + 'a' 16 wqid; + }; + { + 'V' 12 offset; + 'U' 20 count; + 'X' 24 data; + }; + { + 'u' 12 nstat; + 'X' 16 stat; + }; + }; +}; + +defn +Fcall(addr) { + complex Fcall addr; + print(" type ", addr.type, "\n"); + print(" fid ", addr.fid, "\n"); + print(" tag ", addr.tag, "\n"); + print("_13_ {\n"); + _13_(addr+12); + print("}\n"); +}; + +Tversion = 100; +Rversion = 101; +Tauth = 102; +Rauth = 103; +Tattach = 104; +Rattach = 105; +Terror = 106; +Rerror = 107; +Tflush = 108; +Rflush = 109; +Twalk = 110; +Rwalk = 111; +Topen = 112; +Ropen = 113; +Tcreate = 114; +Rcreate = 115; +Tread = 116; +Rread = 117; +Twrite = 118; +Rwrite = 119; +Tclunk = 120; +Rclunk = 121; +Tremove = 122; +Rremove = 123; +Tstat = 124; +Rstat = 125; +Twstat = 126; +Rwstat = 127; +Tmax = 128; +sizeofFid = 60; +aggr Fid +{ + 'U' 0 fid; + 'C' 4 omode; + 'X' 8 file; + 'X' 12 uid; + Qid 16 qid; + 'X' 32 aux; + 'X' 36 rdir; + Ref 40 ref; + 'X' 44 pool; + 'V' 48 diroffset; + 'D' 56 dirindex; +}; + +defn +Fid(addr) { + complex Fid addr; + print(" fid ", addr.fid, "\n"); + print(" omode ", addr.omode, "\n"); + print(" file ", addr.file\X, "\n"); + print(" uid ", addr.uid\X, "\n"); + print("Qid qid {\n"); + Qid(addr.qid); + print("}\n"); + print(" aux ", addr.aux\X, "\n"); + print(" rdir ", addr.rdir\X, "\n"); + print("Ref ref {\n"); + Ref(addr.ref); + print("}\n"); + print(" pool ", addr.pool\X, "\n"); + print(" diroffset ", addr.diroffset, "\n"); + print(" dirindex ", addr.dirindex, "\n"); +}; + +sizeofReq = 656; +aggr Req +{ + 'U' 0 tag; + 'X' 4 aux; + Fcall 8 ifcall; + Fcall 280 ofcall; + Dir 552 d; + 'A' Req 612 oldreq; + 'A' Fid 616 fid; + 'A' Fid 620 afid; + 'A' Fid 624 newfid; + 'X' 628 srv; + Ref 632 ref; + 'X' 636 pool; + 'X' 640 buf; + 'b' 644 type; + 'b' 645 responded; + 'X' 648 error; + 'X' 652 rbuf; +}; + +defn +Req(addr) { + complex Req addr; + print(" tag ", addr.tag, "\n"); + print(" aux ", addr.aux\X, "\n"); + print("Fcall ifcall {\n"); + Fcall(addr.ifcall); + print("}\n"); + print("Fcall ofcall {\n"); + Fcall(addr.ofcall); + print("}\n"); + print("Dir d {\n"); + Dir(addr.d); + print("}\n"); + print(" oldreq ", addr.oldreq\X, "\n"); + print(" fid ", addr.fid\X, "\n"); + print(" afid ", addr.afid\X, "\n"); + print(" newfid ", addr.newfid\X, "\n"); + print(" srv ", addr.srv\X, "\n"); + print("Ref ref {\n"); + Ref(addr.ref); + print("}\n"); + print(" pool ", addr.pool\X, "\n"); + print(" buf ", addr.buf\X, "\n"); + print(" type ", addr.type, "\n"); + print(" responded ", addr.responded, "\n"); + print(" error ", addr.error\X, "\n"); + print(" rbuf ", addr.rbuf\X, "\n"); +}; + +sizeofFidpool = 12; +aggr Fidpool +{ + 'X' 0 map; + 'X' 4 destroy; + 'X' 8 srv; +}; + +defn +Fidpool(addr) { + complex Fidpool addr; + print(" map ", addr.map\X, "\n"); + print(" destroy ", addr.destroy\X, "\n"); + print(" srv ", addr.srv\X, "\n"); +}; + +sizeofReqpool = 12; +aggr Reqpool +{ + 'X' 0 map; + 'X' 4 destroy; + 'X' 8 srv; +}; + +defn +Reqpool(addr) { + complex Reqpool addr; + print(" map ", addr.map\X, "\n"); + print(" destroy ", addr.destroy\X, "\n"); + print(" srv ", addr.srv\X, "\n"); +}; + +sizeofFile = 108; +aggr File +{ + { + 'D' 0 ref; + }; + { + 'u' 4 type; + 'U' 8 dev; + Qid 12 qid; + 'U' 28 mode; + 'U' 32 atime; + 'U' 36 mtime; + 'V' 40 length; + 'X' 48 name; + 'X' 52 uid; + 'X' 56 gid; + 'X' 60 muid; + }; + 'A' File 64 parent; + 'X' 68 aux; + { + Lock 72 lock; + 'D' 76 readers; + 'D' 80 writer; + 'A' QLp 84 $head; + 'A' QLp 88 $tail; + }; + 'X' 92 filelist; + 'X' 96 tree; + 'D' 100 nchild; + 'D' 104 allocd; +}; + +defn +File(addr) { + complex File addr; + print("Ref {\n"); + Ref(addr+0); + print("}\n"); + print("Dir {\n"); + Dir(addr+4); + print("}\n"); + print(" parent ", addr.parent\X, "\n"); + print(" aux ", addr.aux\X, "\n"); + print("RWLock {\n"); + RWLock(addr+72); + print("}\n"); + print(" filelist ", addr.filelist\X, "\n"); + print(" tree ", addr.tree\X, "\n"); + print(" nchild ", addr.nchild, "\n"); + print(" allocd ", addr.allocd, "\n"); +}; + +sizeofTree = 20; +aggr Tree +{ + 'A' File 0 root; + 'X' 4 destroy; + Lock 8 genlock; + 'U' 12 qidgen; + 'U' 16 dirqidgen; +}; + +defn +Tree(addr) { + complex Tree addr; + print(" root ", addr.root\X, "\n"); + print(" destroy ", addr.destroy\X, "\n"); + print("Lock genlock {\n"); + Lock(addr.genlock); + print("}\n"); + print(" qidgen ", addr.qidgen, "\n"); + print(" dirqidgen ", addr.dirqidgen, "\n"); +}; + +sizeofSrv = 136; +aggr Srv +{ + 'A' Tree 0 tree; + 'X' 4 destroyfid; + 'X' 8 destroyreq; + 'X' 12 end; + 'X' 16 aux; + 'X' 20 attach; + 'X' 24 auth; + 'X' 28 open; + 'X' 32 create; + 'X' 36 read; + 'X' 40 write; + 'X' 44 remove; + 'X' 48 flush; + 'X' 52 stat; + 'X' 56 wstat; + 'X' 60 walk; + 'X' 64 clone; + 'X' 68 walk1; + 'D' 72 infd; + 'D' 76 outfd; + 'D' 80 nopipe; + 'A' Fidpool 84 fpool; + 'A' Reqpool 88 rpool; + 'U' 92 msize; + 'X' 96 rbuf; + QLock 100 rlock; + 'X' 116 wbuf; + QLock 120 wlock; +}; + +defn +Srv(addr) { + complex Srv addr; + print(" tree ", addr.tree\X, "\n"); + print(" destroyfid ", addr.destroyfid\X, "\n"); + print(" destroyreq ", addr.destroyreq\X, "\n"); + print(" end ", addr.end\X, "\n"); + print(" aux ", addr.aux\X, "\n"); + print(" attach ", addr.attach\X, "\n"); + print(" auth ", addr.auth\X, "\n"); + print(" open ", addr.open\X, "\n"); + print(" create ", addr.create\X, "\n"); + print(" read ", addr.read\X, "\n"); + print(" write ", addr.write\X, "\n"); + print(" remove ", addr.remove\X, "\n"); + print(" flush ", addr.flush\X, "\n"); + print(" stat ", addr.stat\X, "\n"); + print(" wstat ", addr.wstat\X, "\n"); + print(" walk ", addr.walk\X, "\n"); + print(" clone ", addr.clone\X, "\n"); + print(" walk1 ", addr.walk1\X, "\n"); + print(" infd ", addr.infd, "\n"); + print(" outfd ", addr.outfd, "\n"); + print(" nopipe ", addr.nopipe, "\n"); + print(" fpool ", addr.fpool\X, "\n"); + print(" rpool ", addr.rpool\X, "\n"); + print(" msize ", addr.msize, "\n"); + print(" rbuf ", addr.rbuf\X, "\n"); + print("QLock rlock {\n"); + QLock(addr.rlock); + print("}\n"); + print(" wbuf ", addr.wbuf\X, "\n"); + print("QLock wlock {\n"); + QLock(addr.wlock); + print("}\n"); +}; + +OMASK = 3; +Maxname = 128; +Maxrpc = 4096; +Notstarted = -3; +Broken = -2; +Established = -1; +RpcFailure = 0; +RpcNeedkey = 1; +RpcOk = 2; +RpcErrstr = 3; +RpcToosmall = 4; +RpcPhase = 5; +sizeofAttr = 12; +aggr Attr +{ + 'A' Attr 0 next; + 'A' String 4 name; + 'A' String 8 val; +}; + +defn +Attr(addr) { + complex Attr addr; + print(" next ", addr.next\X, "\n"); + print(" name ", addr.name\X, "\n"); + print(" val ", addr.val\X, "\n"); +}; + +sizeof_14_ = 4120; +aggr _14_ +{ + 'X' 0 arg; + 'a' 4 buf; + 'X' 4100 verb; + 'D' 4104 iverb; + 'D' 4108 narg; + 'D' 4112 nbuf; + 'D' 4116 nwant; +}; + +defn +_14_(addr) { + complex _14_ addr; + print(" arg ", addr.arg\X, "\n"); + print(" buf ", addr.buf, "\n"); + print(" verb ", addr.verb\X, "\n"); + print(" iverb ", addr.iverb, "\n"); + print(" narg ", addr.narg, "\n"); + print(" nbuf ", addr.nbuf, "\n"); + print(" nwant ", addr.nwant, "\n"); +}; + +sizeofFsstate = 4700; +aggr Fsstate +{ + 'X' 0 sysuser; + 'D' 4 listoff; + _14_ 8 rpc; + 'a' 4128 err; + 'a' 4256 keyinfo; + 'X' 4640 phasename; + 'D' 4644 isclient; + 'D' 4648 haveai; + 'D' 4652 maxphase; + 'D' 4656 phase; + 'D' 4660 started; + 'A' Attr 4664 attr; + AuthInfo 4668 ai; + 'X' 4688 proto; + 'X' 4692 ps; + 'X' 4696 ring; +}; + +defn +Fsstate(addr) { + complex Fsstate addr; + print(" sysuser ", addr.sysuser\X, "\n"); + print(" listoff ", addr.listoff, "\n"); + print("_14_ rpc {\n"); + _14_(addr.rpc); + print("}\n"); + print(" err ", addr.err, "\n"); + print(" keyinfo ", addr.keyinfo, "\n"); + print(" phasename ", addr.phasename\X, "\n"); + print(" isclient ", addr.isclient, "\n"); + print(" haveai ", addr.haveai, "\n"); + print(" maxphase ", addr.maxphase, "\n"); + print(" phase ", addr.phase, "\n"); + print(" started ", addr.started, "\n"); + print(" attr ", addr.attr\X, "\n"); + print("AuthInfo ai {\n"); + AuthInfo(addr.ai); + print("}\n"); + print(" proto ", addr.proto\X, "\n"); + print(" ps ", addr.ps\X, "\n"); + print(" ring ", addr.ring\X, "\n"); +}; + +sizeofKey = 20; +aggr Key +{ + 'D' 0 ref; + 'A' Attr 4 attr; + 'A' Attr 8 privattr; + 'X' 12 proto; + 'X' 16 priv; +}; + +defn +Key(addr) { + complex Key addr; + print(" ref ", addr.ref, "\n"); + print(" attr ", addr.attr\X, "\n"); + print(" privattr ", addr.privattr\X, "\n"); + print(" proto ", addr.proto\X, "\n"); + print(" priv ", addr.priv\X, "\n"); +}; + +sizeofKeyring = 8; +aggr Keyring +{ + 'A' Key 0 key; + 'D' 4 nkey; +}; + +defn +Keyring(addr) { + complex Keyring addr; + print(" key ", addr.key\X, "\n"); + print(" nkey ", addr.nkey, "\n"); +}; + +sizeofLogbuf = 520; +aggr Logbuf +{ + 'D' 0 rp; + 'D' 4 wp; + 'a' 8 msg; +}; + +defn +Logbuf(addr) { + complex Logbuf addr; + print(" rp ", addr.rp, "\n"); + print(" wp ", addr.wp, "\n"); + print(" msg ", addr.msg, "\n"); +}; + +sizeofProto = 28; +aggr Proto +{ + 'X' 0 name; + 'X' 4 init; + 'X' 8 addkey; + 'X' 12 closekey; + 'X' 16 write; + 'X' 20 read; + 'X' 24 close; +}; + +defn +Proto(addr) { + complex Proto addr; + print(" name ", addr.name\X, "\n"); + print(" init ", addr.init\X, "\n"); + print(" addkey ", addr.addkey\X, "\n"); + print(" closekey ", addr.closekey\X, "\n"); + print(" write ", addr.write\X, "\n"); + print(" read ", addr.read\X, "\n"); + print(" close ", addr.close\X, "\n"); +}; + +complex Keyring ring; +complex Logbuf logbuf; +complex Proto apop; +complex Proto cram; +complex Proto p9any; +complex Proto p9sk1; +complex Proto p9sk2; +complex Keyring ring; +complex Srv fs; +complex Proto main:p; +Qroot = 0; +Qfactotum = 1; +Qrpc = 2; +Qkeylist = 3; +Qprotolist = 4; +Qconfirm = 5; +Qlog = 6; +Qctl = 7; +complex Qid mkqid:q; +complex Req fsattach:r; +sizeof_15_ = 12; +aggr _15_ +{ + 'X' 0 name; + 'D' 4 qidpath; + 'U' 8 perm; +}; + +defn +_15_(addr) { + complex _15_ addr; + print(" name ", addr.name\X, "\n"); + print(" qidpath ", addr.qidpath, "\n"); + print(" perm ", addr.perm, "\n"); +}; + +complex Dir fillstat:dir; +complex Dir fsdirgen:dir; +complex Fid fswalk1:fid; +complex Qid fswalk1:qid; +complex Req fsstat:r; +complex Req fsopen:r; +complex Fsstate fsopen:fss; +complex Fid fsdestroyfid:fid; +complex Req readlist:r; +complex Key keylist:k; +complex Req fsread:r; +complex Fsstate fsread:s; +complex Req fswrite:r; +complex Srv fs; blob - /dev/null blob + 0e4f2fd6e3e1ab44699445367d961581d27d1af9 (mode 644) --- /dev/null +++ src/cmd/factotum/fs.c @@ -0,0 +1,524 @@ +#include "std.h" +#include "dat.h" + +enum +{ + Qroot, + Qfactotum, + Qrpc, + Qkeylist, + Qprotolist, + Qconfirm, + Qlog, + Qctl, + Qneedkey, + Qconv, +}; + +Qid +mkqid(int type, int path) +{ + Qid q; + + q.type = type; + q.path = path; + q.vers = 0; + return q; +} + +static struct +{ + char *name; + int qidpath; + ulong perm; +} dirtab[] = { + /* positions of confirm and needkey known below */ + "confirm", Qconfirm, 0600|DMEXCL, + "needkey", Qneedkey, 0600|DMEXCL, + "ctl", Qctl, 0600, + "rpc", Qrpc, 0666, + "proto", Qprotolist, 0444, + "log", Qlog, 0600|DMEXCL, + "conv", Qconv, 0400, +}; + +static void +fillstat(Dir *dir, char *name, int type, int path, ulong perm) +{ + dir->name = estrdup(name); + dir->uid = estrdup(owner); + dir->gid = estrdup(owner); + dir->mode = perm; + dir->length = 0; + dir->qid = mkqid(type, path); + dir->atime = time(0); + dir->mtime = time(0); + dir->muid = estrdup(""); +} + +static int +rootdirgen(int n, Dir *dir, void *v) +{ + USED(v); + + if(n > 0) + return -1; + + fillstat(dir, factname, QTDIR, Qfactotum, DMDIR|0555); + return 0; +} + +static int +fsdirgen(int n, Dir *dir, void *v) +{ + USED(v); + + if(n >= nelem(dirtab)) + return -1; + fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm); + return 0; +} + +static char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + int i; + + switch((int)fid->qid.path){ + default: + return "fswalk1: cannot happen"; + case Qroot: + if(strcmp(name, factname) == 0){ + *qid = mkqid(QTDIR, Qfactotum); + fid->qid = *qid; + return nil; + } + if(strcmp(name, "..") == 0){ + *qid = fid->qid; + return nil; + } + return "not found"; + case Qfactotum: + for(i=0; iqid = *qid; + return nil; + } + if(strcmp(name, "..") == 0){ + *qid = mkqid(QTDIR, Qroot); + fid->qid = *qid; + return nil; + } + return "not found"; + } +} + +static void +fsstat(Req *r) +{ + int i, path; + + path = r->fid->qid.path; + switch(path){ + case Qroot: + fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR); + break; + case Qfactotum: + fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR); + break; + default: + for(i=0; id, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm); + goto Break2; + } + respond(r, "file not found"); + break; + } + Break2: + respond(r, nil); +} + +static int +readlist(int off, int (*gen)(int, char*, uint), Req *r) +{ + char *a, *ea; + int n; + + a = r->ofcall.data; + ea = a+r->ifcall.count; + for(;;){ + n = (*gen)(off, a, ea-a); + if(n == 0){ + r->ofcall.count = a - (char*)r->ofcall.data; + return off; + } + a += n; + off++; + } + return -1; /* not reached */ +} + +static int +keylist(int i, char *a, uint nn) +{ + int n; + char buf[512]; + Key *k; + + if(i >= ring.nkey) + return 0; + + k = ring.key[i]; + k->attr = sortattr(k->attr); + n = snprint(buf, sizeof buf, "key %A %N\n", k->attr, k->privattr); + if(n >= sizeof(buf)-5) + strcpy(buf+sizeof(buf)-5, "...\n"); + n = strlen(buf); + if(n > nn) + return 0; + memmove(a, buf, n); + return n; +} + +static int +protolist(int i, char *a, uint n) +{ + if(prototab[i] == nil) + return 0; + if(strlen(prototab[i]->name)+1 > n) + return 0; + n = strlen(prototab[i]->name)+1; + memmove(a, prototab[i]->name, n-1); + a[n-1] = '\n'; + return n; +} + +/* BUG this is O(n^2) to fill in the list */ +static int +convlist(int i, char *a, uint nn) +{ + Conv *c; + char buf[512]; + int n; + + for(c=conv; c && i-- > 0; c=c->next) + ; + + if(c == nil) + return 0; + + if(c->state) + n = snprint(buf, sizeof buf, "conv state=%q %A\n", c->state, c->attr); + else + n = snprint(buf, sizeof buf, "conv state=closed err=%q\n", c->err); + + if(n >= sizeof(buf)-5) + strcpy(buf+sizeof(buf)-5, "...\n"); + n = strlen(buf); + if(n > nn) + return 0; + memmove(a, buf, n); + return n; +} + +static void +fskickreply(Conv *c) +{ + Req *r; + + if(c->hangup){ + if(c->req){ + respond(c->req, "hangup"); + c->req = nil; + } + return; + } + + if(!c->req || !c->nreply) + return; + + r = c->req; + r->ofcall.count = c->nreply; + r->ofcall.data = c->reply; + if(r->ofcall.count > r->ifcall.count) + r->ofcall.count = r->ifcall.count; + respond(r, nil); + c->req = nil; + c->nreply = 0; +} + +/* + * Some of the file system work happens in the fs proc, but + * fsopen, fsread, fswrite, fsdestroyfid, and fsflush happen in + * the main proc so that they can access the various shared + * data structures without worrying about locking. + */ +static int inuse[nelem(dirtab)]; +int *confirminuse = &inuse[0]; +int *needkeyinuse = &inuse[1]; +static void +fsopen(Req *r) +{ + int i, *inusep, perm; + static int need[4] = { 4, 2, 6, 1 }; + Conv *c; + + inusep = nil; + perm = 5; /* directory */ + for(i=0; ifid->qid.path){ + if(dirtab[i].perm & DMEXCL) + inusep = &inuse[i]; + if(strcmp(r->fid->uid, owner) == 0) + perm = dirtab[i].perm>>6; + else + perm = dirtab[i].perm; + break; + } + + if((r->ifcall.mode&~(OMASK|OTRUNC)) + || (need[r->ifcall.mode&3] & ~perm)){ + respond(r, "permission denied"); + return; + } + + if(inusep){ + if(*inusep){ + respond(r, "file in use"); + return; + } + *inusep = 1; + } + + if(r->fid->qid.path == Qrpc){ + if((c = convalloc(r->fid->uid)) == nil){ + char e[ERRMAX]; + + rerrstr(e, sizeof e); + respond(r, e); + return; + } + c->kickreply = fskickreply; + r->fid->aux = c; + } + + respond(r, nil); +} + +static void +fsread(Req *r) +{ + Conv *c; + + switch((int)r->fid->qid.path){ + default: + respond(r, "fsread: cannot happen"); + break; + case Qroot: + dirread9p(r, rootdirgen, nil); + respond(r, nil); + break; + case Qfactotum: + dirread9p(r, fsdirgen, nil); + respond(r, nil); + break; + case Qrpc: + c = r->fid->aux; + if(c->rpc.op == RpcUnknown){ + respond(r, "no rpc pending"); + break; + } + if(c->req){ + respond(r, "read already pending"); + break; + } + c->req = r; + if(c->nreply) + (*c->kickreply)(c); + else + rpcexec(c); + break; + case Qconfirm: + confirmread(r); + break; + case Qlog: + logread(r); + break; + case Qctl: + r->fid->aux = (void*)readlist((int)r->fid->aux, keylist, r); + respond(r, nil); + break; + case Qneedkey: + needkeyread(r); + break; + case Qprotolist: + r->fid->aux = (void*)readlist((int)r->fid->aux, protolist, r); + respond(r, nil); + break; + case Qconv: + r->fid->aux = (void*)readlist((int)r->fid->aux, convlist, r); + respond(r, nil); + break; + } +} + +static void +fswrite(Req *r) +{ + int ret; + char err[ERRMAX], *s; + int (*strfn)(char*); + + switch((int)r->fid->qid.path){ + default: + respond(r, "fswrite: cannot happen"); + break; + case Qrpc: + if(rpcwrite(r->fid->aux, r->ifcall.data, r->ifcall.count) < 0){ + rerrstr(err, sizeof err); + respond(r, err); + }else{ + r->ofcall.count = r->ifcall.count; + respond(r, nil); + } + break; + case Qneedkey: + strfn = needkeywrite; + goto string; + case Qctl: + strfn = ctlwrite; + goto string; + case Qconfirm: + strfn = confirmwrite; + string: + s = emalloc(r->ifcall.count+1); + memmove(s, r->ifcall.data, r->ifcall.count); + s[r->ifcall.count] = '\0'; + ret = (*strfn)(s); + free(s); + if(ret < 0){ + rerrstr(err, sizeof err); + respond(r, err); + }else{ + r->ofcall.count = r->ifcall.count; + respond(r, nil); + } + break; + } +} + +static void +fsflush(Req *r) +{ + confirmflush(r); + logflush(r); +} + +static void +fsdestroyfid(Fid *fid) +{ + if(fid->qid.path == Qrpc){ + convhangup(fid->aux); + convclose(fid->aux); + } +} + +static Channel *creq; +static Channel *cfid, *cfidr; + +static void +fsreqthread(void *v) +{ + Req *r; + + USED(v); + + creq = chancreate(sizeof(Req*), 0); + + while((r = recvp(creq)) != nil){ + switch(r->ifcall.type){ + default: + respond(r, "bug in fsreqthread"); + break; + case Topen: + fsopen(r); + break; + case Tread: + fsread(r); + break; + case Twrite: + fswrite(r); + break; + case Tflush: + fsflush(r); + break; + } + } +} + +static void +fsclunkthread(void *v) +{ + Fid *f; + + USED(v); + cfid = chancreate(sizeof(Fid*), 0); + cfidr = chancreate(sizeof(Fid*), 0); + + while((f = recvp(cfid)) != nil){ + fsdestroyfid(f); + sendp(cfidr, 0); + } +} + +static void +fsproc(void *v) +{ + USED(v); + + threadcreate(fsreqthread, nil, STACK); + threadcreate(fsclunkthread, nil, STACK); + threadexits(nil); +} + +static void +fsattach(Req *r) +{ + static int first = 1; + + if(first){ + proccreate(fsproc, nil, STACK); + first = 0; + } + + r->fid->qid = mkqid(QTDIR, Qroot); + r->ofcall.qid = r->fid->qid; + respond(r, nil); +} + +static void +fssend(Req *r) +{ + sendp(creq, r); +} + +static void +fssendclunk(Fid *f) +{ + sendp(cfid, f); + recvp(cfidr); +} + +Srv fs = { +.attach= fsattach, +.walk1= fswalk1, +.open= fssend, +.read= fssend, +.write= fssend, +.stat= fsstat, +.flush= fssend, +.destroyfid= fssendclunk, +}; + blob - /dev/null blob + 7293e54b63e77381e187e4e86d8db5cd28f9d9bd (mode 755) --- /dev/null +++ src/cmd/factotum/guide @@ -0,0 +1,3 @@ +kill 8.out|rc +unmount /srv/factotum /mnt +8.out blob - /dev/null blob + 72f245d0e3d52b86bc84284a3eb4b088057173fb (mode 644) --- /dev/null +++ src/cmd/factotum/guide2 @@ -0,0 +1,6 @@ +kill 8.out|rc +unmount /mnt/factotum +8.out -m /mnt/factotum +cat /mnt/factotum/log & +unmount /factotum +bind 8.out /factotum blob - /dev/null blob + 9023fed509e1bcd4e560efb778f077c322f35cdd (mode 644) --- /dev/null +++ src/cmd/factotum/key.c @@ -0,0 +1,190 @@ +#include "std.h" +#include "dat.h" + +Ring ring; + +Key* +keylookup(char *fmt, ...) +{ + int i; + Attr *a; + Key *k; + va_list arg; + + va_start(arg, fmt); + a = parseattrfmtv(fmt, arg); + va_end(arg); + + for(i=0; iattr, k->privattr)){ + k->ref++; + freeattr(a); + return k; + } + } + freeattr(a); + werrstr("no key found"); + return nil; +} + +Key* +keyfetch(Conv *c, char *fmt, ...) +{ + int i, tag; + Attr *a; + Key *k; + va_list arg; + + va_start(arg, fmt); + a = parseattrfmtv(fmt, arg); + va_end(arg); + + tag = 0; + + for(i=0; itag) + tag = k->tag; + if(matchattr(a, k->attr, k->privattr)){ + k->ref++; + if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){ + k->ref--; + continue; + } + freeattr(a); + return k; + } + } + + if(needkey(c, a) < 0) + convneedkey(c, a); + + for(i=0; itag <= tag) + continue; + if(matchattr(a, k->attr, k->privattr)){ + k->ref++; + if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){ + k->ref--; + continue; + } + freeattr(a); + return k; + } + } + freeattr(a); + werrstr("no key found"); + return nil; +} + +static int taggen; + +void +keyadd(Key *k) +{ + int i; + + k->ref++; + k->tag = ++taggen; + for(i=0; iattr, ring.key[i]->attr, nil) + && matchattr(ring.key[i]->attr, k->attr, nil)){ + keyclose(ring.key[i]); + ring.key[i] = k; + return; + } + } + + ring.key = erealloc(ring.key, (ring.nkey+1)*sizeof(ring.key[0])); + ring.key[ring.nkey++] = k; +} + +void +keyclose(Key *k) +{ + if(k == nil) + return; + + if(--k->ref > 0) + return; + + if(k->proto->closekey) + (*k->proto->closekey)(k); + + freeattr(k->attr); + freeattr(k->privattr); + free(k); +} + +Key* +keyreplace(Conv *c, Key *k, char *fmt, ...) +{ + Key *kk; + char *msg; + Attr *a, *b, *bp; + va_list arg; + + va_start(arg, fmt); + msg = vsmprint(fmt, arg); + if(msg == nil) + sysfatal("out of memory"); + va_end(arg); + + /* replace prompted values with prompts */ + a = copyattr(k->attr); + bp = parseattr(k->proto->keyprompt); + for(b=bp; b; b=b->next){ + a = delattr(a, b->name); + a = addattr(a, "%q?", b->name); + } + freeattr(bp); + + if(badkey(c, k, msg, a) < 0) + convbadkey(c, k, msg, a); + kk = keylookup("%A", a); + freeattr(a); + keyclose(k); + if(kk == k){ + keyclose(kk); + werrstr("%s", msg); + return nil; + } + + if(strfindattr(kk->attr, "confirm")){ + if(confirmkey(c, kk) != 1){ + werrstr("key use not confirmed"); + keyclose(kk); + return nil; + } + } + return kk; +} + +void +keyevict(Conv *c, Key *k, char *fmt, ...) +{ + char *msg; + Attr *a, *b, *bp; + va_list arg; + + va_start(arg, fmt); + msg = vsmprint(fmt, arg); + if(msg == nil) + sysfatal("out of memory"); + va_end(arg); + + /* replace prompted values with prompts */ + a = copyattr(k->attr); + bp = parseattr(k->proto->keyprompt); + for(b=bp; b; b=b->next){ + a = delattr(a, b->name); + a = addattr(a, "%q?", b->name); + } + freeattr(bp); + + if(badkey(c, k, msg, nil) < 0) + convbadkey(c, k, msg, nil); + keyclose(k); +} blob - /dev/null blob + 6c2d69ddbf6a5d152737ebbb1ef680f8a42eab2f (mode 644) --- /dev/null +++ src/cmd/factotum/log.c @@ -0,0 +1,121 @@ +#include "std.h" +#include "dat.h" + +void +lbkick(Logbuf *lb) +{ + char *s; + int n; + Req *r; + + while(lb->wait && lb->rp != lb->wp){ + r = lb->wait; + lb->wait = r->aux; + if(lb->wait == nil) + lb->waitlast = &lb->wait; + r->aux = nil; + if(r->ifcall.count < 5){ + respond(r, "factotum: read request count too short"); + continue; + } + s = lb->msg[lb->rp]; + lb->msg[lb->rp] = nil; + if(++lb->rp == nelem(lb->msg)) + lb->rp = 0; + n = r->ifcall.count; + if(n < strlen(s)+1+1){ + memmove(r->ofcall.data, s, n-5); + n -= 5; + r->ofcall.data[n] = '\0'; + /* look for first byte of UTF-8 sequence by skipping continuation bytes */ + while(n>0 && (r->ofcall.data[--n]&0xC0)==0x80) + ; + strcpy(r->ofcall.data+n, "...\n"); + }else{ + strcpy(r->ofcall.data, s); + strcat(r->ofcall.data, "\n"); + } + r->ofcall.count = strlen(r->ofcall.data); + free(s); + respond(r, nil); + } +} + +void +lbread(Logbuf *lb, Req *r) +{ + if(lb->waitlast == nil) + lb->waitlast = &lb->wait; + *(lb->waitlast) = r; + lb->waitlast = (Req**)&r->aux; + r->aux = nil; + lbkick(lb); +} + +void +lbflush(Logbuf *lb, Req *r) +{ + Req **l; + + for(l=&lb->wait; *l; l=(Req**)&(*l)->aux){ + if(*l == r){ + *l = r->aux; + r->aux = nil; + if(*l == nil) + lb->waitlast = l; + closereq(r); + break; + } + } +} + +void +lbappend(Logbuf *lb, char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + lbvappend(lb, fmt, arg); + va_end(arg); +} + +void +lbvappend(Logbuf *lb, char *fmt, va_list arg) +{ + char *s; + + s = smprint(fmt, arg); + if(s == nil) + sysfatal("out of memory"); + if(lb->msg[lb->wp]) + free(lb->msg[lb->wp]); + lb->msg[lb->wp] = s; + if(++lb->wp == nelem(lb->msg)) + lb->wp = 0; + lbkick(lb); +} + +Logbuf logbuf; + +void +logread(Req *r) +{ + lbread(&logbuf, r); +} + +void +logflush(Req *r) +{ + lbflush(&logbuf, r); +} + +void +flog(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + lbvappend(&logbuf, fmt, arg); + va_end(arg); +} + blob - /dev/null blob + 3e9f89f8ef467a02483d67ddb639f4a4838aed04 (mode 644) --- /dev/null +++ src/cmd/factotum/main.c @@ -0,0 +1,163 @@ +#include "std.h" +#include "dat.h" + +int debug; +char *factname = "factotum"; +char *service = nil; +char *owner; +char *authaddr; +void gflag(char*); + +void +usage(void) +{ + fprint(2, "usage: factotum [-Dd] [-a authaddr] [-m mtpt]\n"); + fprint(2, " or factotum -g keypattern\n"); + fprint(2, " or factotum -g 'badkeyattr\nmsg\nkeypattern'"); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *mtpt; + + mtpt = "/mnt"; + owner = getuser(); + quotefmtinstall(); + fmtinstall('A', attrfmt); + fmtinstall('H', encodefmt); + fmtinstall('N', attrnamefmt); + + if(argc == 3 && strcmp(argv[1], "-g") == 0){ + gflag(argv[2]); + exits(nil); + } + + ARGBEGIN{ + default: + usage(); + case 'D': + chatty9p++; + break; + case 'a': + authaddr = EARGF(usage()); + break; + case 'g': + usage(); + case 'm': + mtpt = EARGF(usage()); + break; + case 's': + service = EARGF(usage()); + break; + }ARGEND + + if(argc != 0) + usage(); + + threadpostmountsrv(&fs, service, mtpt, MBEFORE); + threadexits(nil); +} + +/* + * prompt user for a key. don't care about memory leaks, runs standalone + */ +static Attr* +promptforkey(int fd, char *params) +{ + char *v; + Attr *a, *attr; + char *def; + + attr = _parseattr(params); + fprint(fd, "!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; +} + +static void +askuser(int fd, char *params) +{ + Attr *attr; + + attr = promptforkey(fd, params); + if(attr == nil) + sysfatal("no key supplied"); + if(sendkey(attr) < 0) + sysfatal("sending key to factotum: %r"); +} + +void +gflag(char *s) +{ + char *f[4]; + int nf; + int fd; + + if((fd = open("/dev/cons", ORDWR)) < 0) + sysfatal("open /dev/cons: %r"); + + nf = getfields(s, f, nelem(f), 0, "\n"); + if(nf == 1){ /* needkey or old badkey */ + fprint(fd, "\n"); + askuser(fd, s); + exits(nil); + } + if(nf == 3){ /* new badkey */ + fprint(fd, "\n"); + fprint(fd, "!replace: %s\n", f[0]); + fprint(fd, "!because: %s\n", f[1]); + askuser(fd, f[2]); + exits(nil); + } + usage(); +} blob - /dev/null blob + 3aa9bb7071bc4062bb60867081e0d1c64a027319 (mode 644) --- /dev/null +++ src/cmd/factotum/mkfile @@ -0,0 +1,34 @@ +PLAN9=../../.. +<$PLAN9/src/mkhdr + +TARG=factotum +PROTO=\ + apop.$O\ + chap.$O\ + p9any.$O\ + p9sk1.$O\ + +OFILES=\ + $PROTO\ + attr.$O\ + confirm.$O\ + conv.$O\ + ctl.$O\ + fs.$O\ + key.$O\ + log.$O\ + main.$O\ + plan9.$O\ + proto.$O\ + rpc.$O\ + util.$O\ + xio.$O\ + +HFILES=dat.h + +SHORTLIB=auth authsrv sec mp 9p thread 9 +<$PLAN9/src/mkone + +$O.test: test.$O + $LD -o $target $prereq + blob - /dev/null blob + 971c7e50c243fb0d678a88d938d6cebc542bd27f (mode 644) --- /dev/null +++ src/cmd/factotum/p9any.c @@ -0,0 +1,266 @@ +#include "std.h" +#include "dat.h" + +/* + * p9any - protocol negotiator + * + * Protocol: + * S->C: v.2 proto@dom proto@dom proto@dom... NUL + * C->S: proto dom NUL + * [negotiated proto continues] + */ + +static Proto* okproto[] = +{ + &p9sk1, + nil, +}; + +static int +rolecall(Role *r, char *name, Conv *c) +{ + for(; r->name; r++) + if(strcmp(r->name, name) == 0) + return (*r->fn)(c); + werrstr("unknown role"); + return -1; +} + +static int +hasnul(void *v, int n) +{ + char *c; + + c = v; + if(n > 0 && c[n-1] == '\0') + return n; + else + return n+1; +} + +static int +p9anyserver(Conv *c) +{ + char *s, *dom; + int i, j, n, m, ret; + char *tok[3]; + Attr *attr; + Key *k; + + ret = -1; + s = estrdup("v.2"); + n = 0; + attr = delattr(copyattr(c->attr), "proto"); + + for(i=0; iproto == okproto[j] + && (dom = strfindattr(k->attr, "dom")) != nil + && matchattr(attr, k->attr, k->privattr)){ + s = estrappend(s, " %s@%s", k->proto->name, dom); + n++; + } + } + + if(n == 0){ + werrstr("no valid keys"); + goto out; + } + + c->state = "write offer"; + if(convwrite(c, s, strlen(s)+1) < 0) + goto out; + free(s); + s = nil; + + c->state = "read choice"; + if(convreadfn(c, hasnul, &s) < 0) + goto out; + + m = tokenize(s, tok, nelem(tok)); + if(m != 2){ + werrstr("bad protocol message"); + goto out; + } + + for(i=0; okproto[i]; i++) + if(strcmp(okproto[i]->name, tok[0]) == 0) + break; + if(!okproto[i]){ + werrstr("bad chosen protocol %q", tok[0]); + goto out; + } + + c->state = "write ok"; + if(convwrite(c, "OK\0", 3) < 0) + goto out; + + c->state = "start choice"; + attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]); + free(c->attr); + c->attr = attr; + attr = nil; + c->proto = okproto[i]; + + if(rolecall(c->proto->roles, "server", c) < 0){ + werrstr("%s: %r", tok[0]); + goto out; + } + + ret = 0; + +out: + free(s); + freeattr(attr); + return ret; +} + +static int +p9anyclient(Conv *c) +{ + char *s, **f, *tok[20], ok[3], *q, *user, *dom; + int i, n, ret, version; + Key *k; + Attr *attr; + Proto *p; + + ret = -1; + s = nil; + k = nil; + + user = strfindattr(c->attr, "user"); + dom = strfindattr(c->attr, "dom"); + + /* + * if the user is the factotum owner, any key will do. + * if not, then if we have a speakfor key, + * we will only vouch for the user's local identity. + * + * this logic is duplicated in p9sk1.c + */ + attr = delattr(copyattr(c->attr), "role"); + attr = delattr(attr, "proto"); + if(strcmp(c->sysuser, owner) == 0) + attr = addattr(attr, "role=client"); + else if(user==nil || strcmp(c->sysuser, user)==0){ + attr = delattr(attr, "user"); + attr = addattr(attr, "role=speakfor"); + }else{ + werrstr("will not authenticate for %q as %q", c->sysuser, user); + goto out; + } + + c->state = "read offer"; + if(convreadfn(c, hasnul, &s) < 0) + goto out; + + c->state = "look for keys"; + n = tokenize(s, tok, nelem(tok)); + f = tok; + version = 1; + if(n > 0 && memcmp(f[0], "v.", 2) == 0){ + version = atoi(f[0]+2); + if(version != 2){ + werrstr("unknown p9any version: %s", f[0]); + goto out; + } + f++; + n--; + } + + /* look for keys that don't need confirmation */ + for(i=0; iattr, "confirm") == nil) + goto found; + *--q = '@'; + } + + /* look for any keys at all */ + for(i=0; istate = "ask for keys"; + for(i=0; ikeyprompt == nil){ + *--q = '@'; + continue; + } + if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt)) + goto found; + *--q = '@'; + } + + /* nothing worked */ + werrstr("unable to find common key"); + goto out; + +found: + /* f[i] is the chosen protocol, q the chosen domain */ + attr = addattr(attr, "proto=%q dom=%q", f[i], q); + c->state = "write choice"; + /* have a key: go for it */ + if(convprint(c, "%q %q", f[i], q) < 0 + || convwrite(c, "\0", 1) < 0) + goto out; + + if(version == 2){ + c->state = "read ok"; + if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0) + goto out; + } + + c->state = "start choice"; + c->proto = protolookup(f[i]); + freeattr(c->attr); + c->attr = attr; + attr = nil; + + if(rolecall(c->proto->roles, "client", c) < 0){ + werrstr("%s: %r", c->proto->name); + goto out; + } + + ret = 0; + +out: + keyclose(k); + freeattr(attr); + free(s); + return ret; +} + +static Role +p9anyroles[] = +{ + "client", p9anyclient, + "server", p9anyserver, + 0 +}; + +Proto p9any = { +.name= "p9any", +.roles= p9anyroles, +}; + blob - /dev/null blob + 7f53e44782057f8f4aa67ba923ab8530b8490e38 (mode 644) --- /dev/null +++ src/cmd/factotum/p9cr.c @@ -0,0 +1,545 @@ +/* + * p9cr, vnc - one-sided challenge/response authentication + * + * Protocol: + * + * C -> S: user + * S -> C: challenge + * C -> S: response + * S -> C: ok or bad + * + * Note that this is the protocol between factotum and the local + * program, not between the two factotums. The information + * exchanged here is wrapped in other protocols by the local + * programs. + */ + +#include "std.h" +#include "dat.h" + +static int +p9crcheck(Key *k) +{ + if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){ + werrstr("need user and !password attributes"); + return -1; + } + return 0; +} + +static int +p9crclient(Conv *c) +{ + char *chal, *pw, *res, *user; + int astype, nchal, npw, ntry, ret; + uchar resp[MD5dlen]; + Attr *attr; + DigestState *ds; + Key *k; + + chal = nil; + k = nil; + res = nil; + ret = -1; + attr = c->attr; + + if(c->proto == &p9cr){ + astype = AuthChal; + challen = NETCHLEN; + }else if(c->proto == &vnc){ + astype = AuthVnc; + challen = MAXCHAL; + }else{ + werrstr("bad proto"); + goto out; + } + + c->state = "find key"; + k = keyfetch(c, "%A %s", attr, c->proto->keyprompt); + if(k == nil) + goto out; + + for(ntry=1;; ntry++){ + if(c->attr != attr) + freeattr(c->attr); + c->attr = addattrs(copyattr(attr), k->attr); + if((pw = strfindattr(k->privattr, "!password")) == nil){ + werrstr("key has no !password (cannot happen)"); + goto out; + } + npw = strlen(pw); + + if((user = strfindattr(k->attr, "user")) == nil){ + werrstr("key has no user (cannot happen)"); + goto out; + } + + if(convprint(c, "%s", user) < 0) + goto out; + + if(convreadm(c, &chal) < 0) + goto out; + + if((nresp = (*response)(chal, resp)) < 0) + goto out; + + if(convwrite(c, resp, nresp) < 0) + goto out; + + if(convreadm(c, &res) < 0) + goto out; + + if(strcmp(res, "ok") == 0) + break; + + if((k = keyreplace(c, k, "%s", res)) == nil){ + c->state = "auth failed"; + werrstr("%s", res); + goto out; + } + } + + werrstr("succeeded"); + ret = 0; + +out: + keyclose(k); + free(chal); + if(c->attr != attr) + freeattr(attr); + return ret; +} + +static int +p9crserver(Conv *c) +{ + char chal[APOPCHALLEN], *user, *resp; + ServerState s; + int astype, ret; + Attr *a; + + ret = -1; + user = nil; + resp = nil; + memset(&s, 0, sizeof s); + s.asfd = -1; + + if(c->proto == &apop) + astype = AuthApop; + else if(c->proto == &cram) + astype = AuthCram; + else{ + werrstr("bad proto"); + goto out; + } + + c->state = "find key"; + if((s.k = plan9authkey(c->attr)) == nil) + goto out; + + a = copyattr(s.k->attr); + a = delattr(a, "proto"); + c->attr = addattrs(c->attr, a); + freeattr(a); + + c->state = "authdial"; + s.hostid = strfindattr(s.k->attr, "user"); + s.dom = strfindattr(s.k->attr, "dom"); + if((s.asfd = xioauthdial(nil, s.dom)) < 0){ + werrstr("authdial %s: %r", s.dom); + goto out; + } + + c->state = "authchal"; + if(p9crchal(&s, astype, chal) < 0) + goto out; + + c->state = "write challenge"; + if(convprint(c, "%s", chal) < 0) + goto out; + + for(;;){ + c->state = "read user"; + if(convreadm(c, &user) < 0) + goto out; + + c->state = "read response"; + if(convreadm(c, &resp) < 0) + goto out; + + c->state = "authwrite"; + switch(apopresp(&s, user, resp)){ + case -1: + goto out; + case 0: + c->state = "write status"; + if(convprint(c, "bad authentication failed") < 0) + goto out; + break; + case 1: + c->state = "write status"; + if(convprint(c, "ok") < 0) + goto out; + goto ok; + } + free(user); + free(resp); + user = nil; + resp = nil; + } + +ok: + ret = 0; + c->attr = addcap(c->attr, c->sysuser, &s.t); + +out: + keyclose(s.k); + free(user); + free(resp); +// xioclose(s.asfd); + return ret; +} + +enum +{ + MAXCHAL = 64, +}; + +typedef struct State State; +struct State +{ + Key *key; + int astype; + int asfd; + Ticket t; + Ticketreq tr; + char chal[MAXCHAL]; + int challen; + char resp[MAXCHAL]; + int resplen; +}; + +enum +{ + CNeedChal, + CHaveResp, + + SHaveChal, + SNeedResp, + + Maxphase, +}; + +static char *phasenames[Maxphase] = +{ +[CNeedChal] "CNeedChal", +[CHaveResp] "CHaveResp", + +[SHaveChal] "SHaveChal", +[SNeedResp] "SNeedResp", +}; + +static void +p9crclose(Fsstate *fss) +{ + State *s; + + s = fss->ps; + if(s->asfd >= 0){ + close(s->asfd); + s->asfd = -1; + } + free(s); +} + +static int getchal(State*, Fsstate*); + +static int +p9crinit(Proto *p, Fsstate *fss) +{ + int iscli, ret; + char *user; + State *s; + Attr *attr; + + if((iscli = isclient(_str_findattr(fss->attr, "role"))) < 0) + return failure(fss, nil); + + s = emalloc(sizeof(*s)); + s->asfd = -1; + if(p == &p9cr){ + s->astype = AuthChal; + s->challen = NETCHLEN; + }else if(p == &vnc){ + s->astype = AuthVNC; + s->challen = Maxchal; + }else + abort(); + + if(iscli){ + fss->phase = CNeedChal; + if(p == &p9cr) + attr = setattr(_copyattr(fss->attr), "proto=p9sk1"); + else + attr = nil; + ret = findkey(&s->key, fss, Kuser, 0, attr ? attr : fss->attr, + "role=client %s", p->keyprompt); + _freeattr(attr); + if(ret != RpcOk){ + free(s); + return ret; + } + fss->ps = s; + }else{ + if((ret = findp9authkey(&s->key, fss)) != RpcOk){ + free(s); + return ret; + } + if((user = _str_findattr(fss->attr, "user")) == nil){ + free(s); + return failure(fss, "no user name specified in start msg"); + } + if(strlen(user) >= sizeof s->tr.uid){ + free(s); + return failure(fss, "user name too long"); + } + fss->ps = s; + strcpy(s->tr.uid, user); + ret = getchal(s, fss); + if(ret != RpcOk){ + p9crclose(fss); /* frees s */ + fss->ps = nil; + } + } + fss->phasename = phasenames; + fss->maxphase = Maxphase; + return ret; +} + +static int +p9crread(Fsstate *fss, void *va, uint *n) +{ + int m; + State *s; + + s = fss->ps; + switch(fss->phase){ + default: + return phaseerror(fss, "read"); + + case CHaveResp: + if(s->resplen < *n) + *n = s->resplen; + memmove(va, s->resp, *n); + fss->phase = Established; + return RpcOk; + + case SHaveChal: + if(s->astype == AuthChal) + m = strlen(s->chal); /* ascii string */ + else + m = s->challen; /* fixed length binary */ + if(m > *n) + return toosmall(fss, m); + *n = m; + memmove(va, s->chal, m); + fss->phase = SNeedResp; + return RpcOk; + } +} + +static int +p9response(Fsstate *fss, State *s) +{ + char key[DESKEYLEN]; + uchar buf[8]; + ulong chal; + char *pw; + + pw = _str_findattr(s->key->privattr, "!password"); + if(pw == nil) + return failure(fss, "vncresponse cannot happen"); + passtokey(key, pw); + memset(buf, 0, 8); + sprint((char*)buf, "%d", atoi(s->chal)); + if(encrypt(key, buf, 8) < 0) + return failure(fss, "can't encrypt response"); + chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3]; + s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal); + return RpcOk; +} + +static uchar tab[256]; + +/* VNC reverses the bits of each byte before using as a des key */ +static void +mktab(void) +{ + int i, j, k; + static int once; + + if(once) + return; + once = 1; + + for(i=0; i<256; i++) { + j=i; + tab[i] = 0; + for(k=0; k<8; k++) { + tab[i] = (tab[i]<<1) | (j&1); + j >>= 1; + } + } +} + +static int +vncaddkey(Key *k) +{ + uchar *p; + char *s; + + k->priv = emalloc(8+1); + if(s = _str_findattr(k->privattr, "!password")){ + mktab(); + memset(k->priv, 0, 8+1); + strncpy((char*)k->priv, s, 8); + for(p=k->priv; *p; p++) + *p = tab[*p]; + }else{ + werrstr("no key data"); + return -1; + } + return replacekey(k); +} + +static void +vncclosekey(Key *k) +{ + free(k->priv); +} + +static int +vncresponse(Fsstate*, State *s) +{ + DESstate des; + + memmove(s->resp, s->chal, sizeof s->chal); + setupDESstate(&des, s->key->priv, nil); + desECBencrypt((uchar*)s->resp, s->challen, &des); + s->resplen = s->challen; + return RpcOk; +} + +static int +p9crwrite(Fsstate *fss, void *va, uint n) +{ + char tbuf[TICKETLEN+AUTHENTLEN]; + State *s; + char *data = va; + Authenticator a; + char resp[Maxchal]; + int ret; + + s = fss->ps; + switch(fss->phase){ + default: + return phaseerror(fss, "write"); + + case CNeedChal: + if(n >= sizeof(s->chal)) + return failure(fss, Ebadarg); + memset(s->chal, 0, sizeof s->chal); + memmove(s->chal, data, n); + s->challen = n; + + if(s->astype == AuthChal) + ret = p9response(fss, s); + else + ret = vncresponse(fss, s); + if(ret != RpcOk) + return ret; + fss->phase = CHaveResp; + return RpcOk; + + case SNeedResp: + /* send response to auth server and get ticket */ + if(n > sizeof(resp)) + return failure(fss, Ebadarg); + memset(resp, 0, sizeof resp); + memmove(resp, data, n); + if(write(s->asfd, resp, s->challen) != s->challen) + return failure(fss, Easproto); + + /* get ticket plus authenticator from auth server */ + if(_asrdresp(s->asfd, tbuf, TICKETLEN+AUTHENTLEN) < 0) + return failure(fss, nil); + + /* check ticket */ + convM2T(tbuf, &s->t, s->key->priv); + if(s->t.num != AuthTs + || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0) + return failure(fss, Easproto); + convM2A(tbuf+TICKETLEN, &a, s->t.key); + if(a.num != AuthAc + || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0 + || a.id != 0) + return failure(fss, Easproto); + + fss->haveai = 1; + fss->ai.cuid = s->t.cuid; + fss->ai.suid = s->t.suid; + fss->ai.nsecret = 0; + fss->ai.secret = nil; + fss->phase = Established; + return RpcOk; + } +} + +static int +getchal(State *s, Fsstate *fss) +{ + char trbuf[TICKREQLEN]; + int n; + + safecpy(s->tr.hostid, _str_findattr(s->key->attr, "user"), sizeof(s->tr.hostid)); + safecpy(s->tr.authdom, _str_findattr(s->key->attr, "dom"), sizeof(s->tr.authdom)); + s->tr.type = s->astype; + convTR2M(&s->tr, trbuf); + + /* get challenge from auth server */ + s->asfd = _authdial(nil, _str_findattr(s->key->attr, "dom")); + if(s->asfd < 0) + return failure(fss, Easproto); + if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN) + return failure(fss, Easproto); + n = _asrdresp(s->asfd, s->chal, s->challen); + if(n <= 0){ + if(n == 0) + werrstr("_asrdresp short read"); + return failure(fss, nil); + } + s->challen = n; + fss->phase = SHaveChal; + return RpcOk; +} + +Proto p9cr = +{ +.name= "p9cr", +.init= p9crinit, +.write= p9crwrite, +.read= p9crread, +.close= p9crclose, +.keyprompt= "user? !password?", +}; + +Proto vnc = +{ +.name= "vnc", +.init= p9crinit, +.write= p9crwrite, +.read= p9crread, +.close= p9crclose, +.keyprompt= "!password?", +.addkey= vncaddkey, +}; blob - /dev/null blob + 2828e708c879be1d6abcec0c76cc1e047606ef95 (mode 644) --- /dev/null +++ src/cmd/factotum/p9sk1.c @@ -0,0 +1,352 @@ +/* + * p9sk1, p9sk2 - Plan 9 secret (private) key authentication. + * p9sk2 is an incomplete flawed variant of p9sk1. + * + * Client protocol: + * write challenge[challen] (p9sk1 only) + * read tickreq[tickreqlen] + * write ticket[ticketlen] + * read authenticator[authentlen] + * + * Server protocol: + * read challenge[challen] (p9sk1 only) + * write tickreq[tickreqlen] + * read ticket[ticketlen] + * write authenticator[authentlen] + */ + +#include "std.h" +#include "dat.h" + +static int gettickets(Ticketreq*, char*, Key*); + +#define max(a, b) ((a) > (b) ? (a) : (b)) +enum +{ + MAXAUTH = max(TICKREQLEN, TICKETLEN+max(TICKETLEN, AUTHENTLEN)) +}; + +static int +p9skclient(Conv *c) +{ + char *user; + char cchal[CHALLEN]; + uchar secret[8]; + char buf[MAXAUTH]; + int speakfor, ret; + Attr *a; + Authenticator au; + Key *k; + Ticket t; + Ticketreq tr; + + ret = -1; + a = nil; + k = nil; + + /* p9sk1: send client challenge */ + if(c->proto == &p9sk1){ + c->state = "write challenge"; + memrandom(cchal, CHALLEN); + if(convwrite(c, cchal, CHALLEN) < 0) + goto out; + } + + /* read ticket request */ + c->state = "read tickreq"; + if(convread(c, buf, TICKREQLEN) < 0) + goto out; + convM2TR(buf, &tr); + + /* p9sk2: use server challenge as client challenge */ + if(c->proto == &p9sk2) + memmove(cchal, tr.chal, CHALLEN); + + /* + * find a key. + * + * if the user is the factotum owner, any key will do. + * if not, then if we have a speakfor key, + * we will only vouch for the user's local identity. + * + * this logic is duplicated in p9any.c + */ + user = strfindattr(c->attr, "user"); + a = delattr(copyattr(c->attr), "role"); + a = addattr(a, "proto=p9sk1"); + + if(strcmp(c->sysuser, owner) == 0){ + speakfor = 0; + a = addattr(a, "proto=p9sk1 user? dom=%q", tr.authdom); + }else if(user==nil || strcmp(c->sysuser, user)==0){ + speakfor = 1; + a = delattr(a, "user"); + a = addattr(a, "proto=p9sk1 user? dom=%q role=speakfor", tr.authdom); + }else{ + werrstr("will not authenticate for %q as %q", c->sysuser, user); + goto out; + } + + for(;;){ + c->state = "find key"; + k = keyfetch(c, "%A", a); + if(k == nil) + goto out; + + /* relay ticket request to auth server, get tickets */ + strcpy(tr.hostid, strfindattr(k->attr, "user")); + if(speakfor) + strcpy(tr.uid, c->sysuser); + else + strcpy(tr.uid, tr.hostid); + + c->state = "get tickets"; + if(gettickets(&tr, buf, k) < 0) + goto out; + + convM2T(buf, &t, k->priv); + if(t.num == AuthTc) + break; + + /* we don't agree with the auth server about the key; try again */ + c->state = "replace key"; + if((k = keyreplace(c, k, "key mismatch with auth server")) == nil){ + werrstr("key mismatch with auth server"); + goto out; + } + } + + /* send second ticket and authenticator to server */ + c->state = "write ticket+auth"; + memmove(buf, buf+TICKETLEN, TICKETLEN); + au.num = AuthAc; + memmove(au.chal, tr.chal, CHALLEN); + au.id = 0; + convA2M(&au, buf+TICKETLEN, t.key); + if(convwrite(c, buf, TICKETLEN+AUTHENTLEN) < 0) + goto out; + + /* read authenticator from server */ + c->state = "read auth"; + if(convread(c, buf, AUTHENTLEN) < 0) + goto out; + convM2A(buf, &au, t.key); + if(au.num != AuthAs || memcmp(au.chal, cchal, CHALLEN) != 0 || au.id != 0){ + werrstr("server lies through his teeth"); + goto out; + } + + /* success */ + c->attr = addcap(c->attr, c->sysuser, &t); + des56to64((uchar*)t.key, secret); + c->attr = addattr(c->attr, "secret=%.8H", secret); + ret = 0; + +out: + freeattr(a); + keyclose(k); + return ret; +} + +static int +p9skserver(Conv *c) +{ + char cchal[CHALLEN], buf[MAXAUTH]; + uchar secret[8]; + int ret; + Attr *a; + Authenticator au; + Key *k; + Ticketreq tr; + Ticket t; + + ret = -1; + + a = addattr(copyattr(c->attr), "user? dom?"); + a = addattr(a, "user? dom? proto=p9sk1"); + if((k = keyfetch(c, "%A", a)) == nil) + goto out; + + /* p9sk1: read client challenge */ + if(c->proto == &p9sk1){ + if(convread(c, cchal, CHALLEN) < 0) + goto out; + } + + /* send ticket request */ + memset(&tr, 0, sizeof tr); + tr.type = AuthTreq; + strcpy(tr.authid, strfindattr(k->attr, "user")); + strcpy(tr.authdom, strfindattr(k->attr, "dom")); + memrandom(tr.chal, sizeof tr.chal); + convTR2M(&tr, buf); + if(convwrite(c, buf, TICKREQLEN) < 0) + goto out; + + /* p9sk2: use server challenge as client challenge */ + if(c->proto == &p9sk2) + memmove(cchal, tr.chal, sizeof tr.chal); + + /* read ticket+authenticator */ + if(convread(c, buf, TICKETLEN+AUTHENTLEN) < 0) + goto out; + + convM2T(buf, &t, k->priv); + if(t.num != AuthTs || memcmp(t.chal, tr.chal, CHALLEN) != 0){ + /* BUG badkey */ + werrstr("key mismatch with auth server"); + goto out; + } + + convM2A(buf+TICKETLEN, &au, t.key); + if(au.num != AuthAc || memcmp(au.chal, tr.chal, CHALLEN) != 0 || au.id != 0){ + werrstr("client lies through his teeth"); + goto out; + } + + /* send authenticator */ + au.num = AuthAs; + memmove(au.chal, cchal, CHALLEN); + convA2M(&au, buf, t.key); + if(convwrite(c, buf, AUTHENTLEN) < 0) + goto out; + + /* success */ + c->attr = addcap(c->attr, c->sysuser, &t); + des56to64((uchar*)t.key, secret); + c->attr = addattr(c->attr, "secret=%.8H", secret); + ret = 0; + +out: + freeattr(a); + keyclose(k); + return ret; +} + +int +_asgetticket(int fd, char *trbuf, char *tbuf) +{ + if(write(fd, trbuf, TICKREQLEN) < 0){ + close(fd); + return -1; + } + return _asrdresp(fd, tbuf, 2*TICKETLEN); +} +static int +getastickets(Ticketreq *tr, char *buf) +{ + int asfd; + int ret; + + if((asfd = xioauthdial(nil, tr->authdom)) < 0) + return -1; + convTR2M(tr, buf); + ret = xioasgetticket(asfd, buf, buf); + xioclose(asfd); + return ret; +} + +static int +mktickets(Ticketreq *tr, char *buf, Key *k) +{ + Ticket t; + + if(strcmp(tr->authid, tr->hostid) != 0) + return -1; + + memset(&t, 0, sizeof t); + memmove(t.chal, tr->chal, CHALLEN); + strcpy(t.cuid, tr->uid); + strcpy(t.suid, tr->uid); + memrandom(t.key, DESKEYLEN); + t.num = AuthTc; + convT2M(&t, buf, k->priv); + t.num = AuthTs; + convT2M(&t, buf+TICKETLEN, k->priv); + return 0; +} + +static int +gettickets(Ticketreq *tr, char *buf, Key *k) +{ + if(getastickets(tr, buf) == 0) + return 0; + if(mktickets(tr, buf, k) == 0) + return 0; + werrstr("gettickets: %r"); + return -1; +} + +static int +p9sk1check(Key *k) +{ + char *user, *dom, *pass; + Ticketreq tr; + + user = strfindattr(k->attr, "user"); + dom = strfindattr(k->attr, "dom"); + if(user==nil || dom==nil){ + werrstr("need user and dom attributes"); + return -1; + } + if(strlen(user) >= sizeof tr.authid){ + werrstr("user name too long"); + return -1; + } + if(strlen(dom) >= sizeof tr.authdom){ + werrstr("auth dom name too long"); + return -1; + } + + k->priv = emalloc(DESKEYLEN); + if(pass = strfindattr(k->privattr, "!password")) + passtokey(k->priv, pass); + else if(pass = strfindattr(k->privattr, "!hex")){ + if(hexparse(pass, k->priv, 7) < 0){ + werrstr("malformed !hex key data"); + return -1; + } + }else{ + werrstr("need !password or !hex attribute"); + return -1; + } + + return 0; +} + +static void +p9sk1close(Key *k) +{ + free(k->priv); + k->priv = nil; +} + +static Role +p9sk1roles[] = +{ + "client", p9skclient, + "server", p9skserver, + 0 +}; + +static Role +p9sk2roles[] = +{ + "client", p9skclient, + "server", p9skserver, + 0 +}; + +Proto p9sk1 = { +.name= "p9sk1", +.roles= p9sk1roles, +.checkkey= p9sk1check, +.closekey= p9sk1close, +.keyprompt= "user? dom? !password?", +}; + +Proto p9sk2 = { +.name= "p9sk2", +.roles= p9sk2roles, +}; + blob - /dev/null blob + b3d4cb6ad2c87b9b7755d314e63f41bebde4c72a (mode 644) --- /dev/null +++ src/cmd/factotum/pass.c @@ -0,0 +1,100 @@ +/* + * This is just a repository for a password. + * We don't want to encourage this, there's + * no server side. + */ + +#include "dat.h" + +typedef struct State State; +struct State +{ + Key *key; +}; + +enum +{ + HavePass, + Maxphase, +}; + +static char *phasenames[Maxphase] = +{ +[HavePass] "HavePass", +}; + +static int +passinit(Proto *p, Fsstate *fss) +{ + int ask; + Key *k; + State *s; + + k = findkey(fss, Kuser, &ask, 0, fss->attr, "%s", p->keyprompt); + if(k == nil){ + if(ask) + return RpcNeedkey; + return failure(fss, nil); + } + setattrs(fss->attr, k->attr); + s = emalloc(sizeof(*s)); + s->key = k; + fss->ps = s; + return RpcOk; +} + +static void +passclose(Fsstate *fss) +{ + State *s; + + s = fss->ps; + if(s->key) + closekey(s->key); + free(s); +} + +static int +passread(Fsstate *fss, void *va, uint *n) +{ + int m; + char buf[500]; + char *pass, *user; + State *s; + + s = fss->ps; + switch(fss->phase){ + default: + return phaseerror(fss, "read"); + + case HavePass: + user = strfindattr(s->key->attr, "user"); + pass = strfindattr(s->key->privattr, "!password"); + if(user==nil || pass==nil) + return failure(fss, "passread cannot happen"); + snprint(buf, sizeof buf, "%q %q", user, pass); + m = strlen(buf); + if(m > *n) + return toosmall(fss, m); + *n = m; + memmove(va, buf, m); + return RpcOk; + } +} + +static int +passwrite(Fsstate *fss, void*, uint) +{ + return phaseerror(fss, "write"); +} + +Proto pass = +{ +.name= "pass", +.init= passinit, +.write= passwrite, +.read= passread, +.close= passclose, +.addkey= replacekey, +.keyprompt= "user? !password?", +}; blob - /dev/null blob + 048c190663c4b0779df9e00ddfe99deda97b2540 (mode 644) --- /dev/null +++ src/cmd/factotum/plan9.c @@ -0,0 +1,189 @@ +#include "std.h" +#include "dat.h" +#include + +int +memrandom(void *p, int n) +{ + uchar *cp; + + for(cp = (uchar*)p; n > 0; n--) + *cp++ = fastrand(); + return 0; +} + +/* + * create a change uid capability + */ +static int caphashfd; + +static char* +mkcap(char *from, char *to) +{ + uchar rand[20]; + char *cap; + char *key; + int nfrom, nto; + uchar hash[SHA1dlen]; + + if(caphashfd < 0) + return nil; + + /* create the capability */ + nto = strlen(to); + nfrom = strlen(from); + cap = emalloc(nfrom+1+nto+1+sizeof(rand)*3+1); + sprint(cap, "%s@%s", from, to); + memrandom(rand, sizeof(rand)); + key = cap+nfrom+1+nto+1; + enc64(key, sizeof(rand)*3, rand, sizeof(rand)); + + /* hash the capability */ + hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil); + + /* give the kernel the hash */ + key[-1] = '@'; + if(write(caphashfd, hash, SHA1dlen) < 0){ + free(cap); + return nil; + } + + return cap; +} + +Attr* +addcap(Attr *a, char *from, Ticket *t) +{ + char *cap; + + cap = mkcap(from, t->suid); + return addattr(a, "cuid=%q suid=%q cap=%q", t->cuid, t->suid, cap); +} + +/* bind in the default network and cs */ +static int +bindnetcs(void) +{ + int srvfd; + + if(access("/net/tcp", AEXIST) < 0) + bind("#I", "/net", MBEFORE); + + if(access("/net/cs", AEXIST) < 0){ + if((srvfd = open("#s/cs", ORDWR)) >= 0){ + /* mount closes srvfd on success */ + if(mount(srvfd, -1, "/net", MBEFORE, "") >= 0) + return 0; + close(srvfd); + } + return -1; + } + return 0; +} + +int +_authdial(char *net, char *authdom) +{ + int vanilla; + + vanilla = net==nil || strcmp(net, "/net")==0; + + if(!vanilla || bindnetcs()>=0) + return authdial(net, authdom); + + /* use the auth sever passed to us as an arg */ + if(authaddr == nil) + return -1; + return dial(netmkaddr(authaddr, "tcp", "567"), 0, 0, 0); +} + +Key* +plan9authkey(Attr *a) +{ + char *dom; + Key *k; + + /* + * The only important part of a is dom. + * We don't care, for example, about user name. + */ + dom = strfindattr(a, "dom"); + if(dom) + k = keylookup("proto=p9sk1 role=server user? dom=%q", dom); + else + k = keylookup("proto=p9sk1 role=server user? dom?"); + if(k == nil) + werrstr("could not find plan 9 auth key dom %q", dom); + return k; +} + +/* + * prompt for a string with a possible default response + */ +char* +readcons(char *prompt, char *def, int raw) +{ + int fdin, fdout, ctl, n; + char line[10]; + char *s; + + fdin = open("/dev/cons", OREAD); + if(fdin < 0) + fdin = 0; + fdout = open("/dev/cons", OWRITE); + if(fdout < 0) + fdout = 1; + if(def != nil) + fprint(fdout, "%s[%s]: ", prompt, def); + else + fprint(fdout, "%s: ", prompt); + if(raw){ + ctl = open("/dev/consctl", OWRITE); + if(ctl >= 0) + write(ctl, "rawon", 5); + } else + ctl = -1; + s = estrdup(""); + for(;;){ + n = read(fdin, line, 1); + if(n == 0){ + Error: + close(fdin); + close(fdout); + if(ctl >= 0) + close(ctl); + free(s); + return nil; + } + if(n < 0) + goto Error; + if(line[0] == 0x7f) + goto Error; + if(n == 0 || line[0] == '\n' || line[0] == '\r'){ + if(raw){ + write(ctl, "rawoff", 6); + write(fdout, "\n", 1); + } + close(ctl); + close(fdin); + close(fdout); + if(*s == 0 && def != nil) + s = estrappend(s, "%s", def); + return s; + } + if(line[0] == '\b'){ + if(strlen(s) > 0) + s[strlen(s)-1] = 0; + } else if(line[0] == 0x15) { /* ^U: line kill */ + if(def != nil) + fprint(fdout, "\n%s[%s]: ", prompt, def); + else + fprint(fdout, "\n%s: ", prompt); + + s[0] = 0; + } else { + s = estrappend(s, "%c", line[0]); + } + } + return nil; /* not reached */ +} blob - /dev/null blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644) blob - /dev/null blob + 64bb2e3411fe666d9858d2dd3e9cf2acccb4fe2a (mode 644) --- /dev/null +++ src/cmd/factotum/proto.c @@ -0,0 +1,22 @@ +#include "std.h" +#include "dat.h" + +Proto *prototab[] = { + &apop, + &cram, + &p9any, + &p9sk1, + &p9sk2, + nil, +}; + +Proto* +protolookup(char *name) +{ + int i; + + for(i=0; prototab[i]; i++) + if(strcmp(prototab[i]->name, name) == 0) + return prototab[i]; + return nil; +} blob - /dev/null blob + e9c163aa4eff3753961142f1b20fd319388158fc (mode 644) --- /dev/null +++ src/cmd/factotum/rpc.c @@ -0,0 +1,315 @@ +#include "std.h" +#include "dat.h" + +/* + * Factotum RPC + * + * Must be paired write/read cycles on /mnt/factotum/rpc. + * The format of a request is verb, single space, data. + * Data format is verb-dependent; in particular, it can be binary. + * The format of a response is the same. The write only sets up + * the RPC. The read tries to execute it. If the /mnt/factotum/key + * file is open, we ask for new keys using that instead of returning + * an error in the RPC. This means the read blocks. + * Textual arguments are parsed with tokenize, so rc-style quoting + * rules apply. + * + * Only authentication protocol messages go here. Configuration + * is still via ctl (below). + * + * Request RPCs are: + * start attrs - initializes protocol for authentication, can fail. + * returns "ok read" or "ok write" on success. + * read - execute protocol read + * write - execute protocol write + * authinfo - if the protocol is finished, return the AI if any + * attr - return protocol information + * Return values are: + * error message - an error happened. + * ok [data] - success, possible data is request dependent. + * needkey attrs - request aborted, get me this key and try again + * badkey attrs - request aborted, this key might be bad + * done [haveai] - authentication is done [haveai: you can get an ai with authinfo] + */ + +char *rpcname[] = +{ + "unknown", + "authinfo", + "attr", + "read", + "start", + "write", +}; + +static int +classify(char *s) +{ + int i; + + for(i=1; i= MaxRpc){ + werrstr("rpc too large"); + return -1; + } + + /* cancel any current rpc */ + c->rpc.op = RpcUnknown; + c->nreply = 0; + + /* parse new rpc */ + memmove(c->rpcbuf, data, count); + c->rpcbuf[count] = 0; + if(p = (uchar*)strchr((char*)c->rpcbuf, ' ')){ + *p++ = '\0'; + c->rpc.data = p; + c->rpc.count = count - (p - (uchar*)c->rpcbuf); + }else{ + c->rpc.data = ""; + c->rpc.count = 0; + } + op = classify(c->rpcbuf); + if(op == RpcUnknown){ + werrstr("bad rpc verb: %s", c->rpcbuf); + return -1; + } + + c->rpc.op = op; + return 0; +} + +void +convthread(void *v) +{ + Conv *c; + Attr *a; + char *role, *proto; + Proto *p; + Role *r; + + c = v; + a = parseattr(c->rpc.data); + if(a == nil){ + werrstr("empty attr"); + goto out; + } + c->attr = a; + proto = strfindattr(a, "proto"); + role = strfindattr(a, "role"); + + if(proto == nil){ + werrstr("no proto in attrs"); + goto out; + } + if(role == nil){ + werrstr("no role in attrs"); + goto out; + } + + p = protolookup(proto); + if(p == nil){ + werrstr("unknown proto %s", proto); + goto out; + } + + c->proto = p; + for(r=p->roles; r->name; r++){ + if(strcmp(r->name, role) != 0) + continue; + rpcrespond(c, "ok"); + c->active = 1; + if((*r->fn)(c) == 0){ + c->done = 1; + werrstr("protocol finished"); + }else + werrstr("%s %s %s: %r", p->name, r->name, c->state); + goto out; + } + werrstr("unknown role"); + +out: + c->active = 0; + c->state = 0; + rerrstr(c->err, sizeof c->err); + rpcrespond(c, "error %r"); + convclose(c); +} + +static uchar* convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex); + +void +rpcexec(Conv *c) +{ + uchar *p; + + switch(c->rpc.op){ + case RpcRead: + if(c->rpc.count > 0){ + rpcrespond(c, "error read takes no parameters"); + break; + } + /* fall through */ + default: + if(!c->active){ + if(c->done) + rpcrespond(c, "done"); + else + rpcrespond(c, "error %s", c->err); + break; + } + nbsendp(c->rpcwait, 0); + break; + case RpcUnknown: + break; + case RpcAuthinfo: + /* deprecated */ + if(c->active) + rpcrespond(c, "error conversation still active"); + else if(!c->done) + rpcrespond(c, "error conversation not successful"); + else{ + /* make up an auth info using the attr */ + p = convAI2M((uchar*)c->reply+3, sizeof c->reply-3, + strfindattr(c->attr, "cuid"), + strfindattr(c->attr, "suid"), + strfindattr(c->attr, "cap"), + strfindattr(c->attr, "secret")); + if(p == nil) + rpcrespond(c, "error %r"); + else + rpcrespondn(c, "ok", c->reply+3, p-(uchar*)(c->reply+3)); + } + break; + case RpcAttr: + rpcrespond(c, "ok %A", c->attr); + break; + case RpcStart: + convreset(c); + c->ref++; + threadcreate(convthread, c, STACK); + break; + } +} + +void +rpcrespond(Conv *c, char *fmt, ...) +{ + va_list arg; + + if(c->hangup) + return; + + if(fmt == nil) + fmt = ""; + + va_start(arg, fmt); + c->nreply = vsnprint(c->reply, sizeof c->reply, fmt, arg); + va_end(arg); + (*c->kickreply)(c); + c->rpc.op = RpcUnknown; +} + +void +rpcrespondn(Conv *c, char *verb, void *data, int count) +{ + char *p; + + if(c->hangup) + return; + + if(strlen(verb)+1+count > sizeof c->reply){ + print("RPC response too large; caller %#lux", getcallerpc(&c)); + return; + } + + strcpy(c->reply, verb); + p = c->reply + strlen(c->reply); + *p++ = ' '; + memmove(p, data, count); + c->nreply = count + (p - c->reply); + (*c->kickreply)(c); + c->rpc.op = RpcUnknown; +} + +/* deprecated */ +static uchar* +pstring(uchar *p, uchar *e, char *s) +{ + uint n; + + if(p == nil) + return nil; + if(s == nil) + s = ""; + n = strlen(s); + if(p+n+BIT16SZ >= e) + return nil; + PBIT16(p, n); + p += BIT16SZ; + memmove(p, s, n); + p += n; + return p; +} + +static uchar* +pcarray(uchar *p, uchar *e, uchar *s, uint n) +{ + if(p == nil) + return nil; + if(s == nil){ + if(n > 0) + sysfatal("pcarray"); + s = (uchar*)""; + } + if(p+n+BIT16SZ >= e) + return nil; + PBIT16(p, n); + p += BIT16SZ; + memmove(p, s, n); + p += n; + return p; +} + +static uchar* +convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex) +{ + uchar *e = p+n; + uchar *secret; + int nsecret; + + if(cuid == nil) + cuid = ""; + if(suid == nil) + suid = ""; + if(cap == nil) + cap = ""; + if(hex == nil) + hex = ""; + nsecret = strlen(hex)/2; + secret = emalloc(nsecret); + if(hexparse(hex, secret, nsecret) < 0){ + werrstr("hexparse %s failed", hex); /* can't happen */ + free(secret); + return nil; + } + p = pstring(p, e, cuid); + p = pstring(p, e, suid); + p = pstring(p, e, cap); + p = pcarray(p, e, secret, nsecret); + free(secret); + if(p == nil) + werrstr("authinfo too big"); + return p; +} + blob - /dev/null blob + 4c88bfe655f0f4a62326110184f1a3ff1a1c95a6 (mode 644) --- /dev/null +++ src/cmd/factotum/ssh.c @@ -0,0 +1,135 @@ +#include "dat.h" +#include +#include + +typedef struct Sshrsastate Sshrsastate; + +enum { + CReadpub, + CWritechal, + CReadresp, +}; +struct State +{ + RSApriv *priv; + Key *k; + mpint *resp; + int phase; +}; + +static RSApriv* +readrsapriv(char *s) +{ + RSApriv *priv; + + priv = rsaprivalloc(); + + strtoul(s, &s, 10); + if((priv->pub.ek=strtomp(s, &s, 16, nil)) == nil) + goto Error; + if((priv->dk=strtomp(s, &s, 16, nil)) == nil) + goto Error; + if((priv->pub.n=strtomp(s, &s, 16, nil)) == nil) + goto Error; + if((priv->p=strtomp(s, &s, 16, nil)) == nil) + goto Error; + if((priv->q=strtomp(s, &s, 16, nil)) == nil) + goto Error; + if((priv->kp=strtomp(s, &s, 16, nil)) == nil) + goto Error; + if((priv->kq=strtomp(s, &s, 16, nil)) == nil) + goto Error; + if((priv->c2=strtomp(s, &s, 16, nil)) == nil) + goto Error; + + return priv; + +Error: + rsaprivfree(priv); + return nil; +} + +int +sshinit(Fsstate *fss, +sshrsaopen(Key *k, char*, int client) +{ + Sshrsastate *s; + + fmtinstall('B', mpconv); + assert(client); + s = emalloc(sizeof *s); + s->priv = readrsapriv(s_to_c(k->data)); + s->k = k; + if(s->priv == nil){ + agentlog("error parsing ssh key %s", k->file); + free(s); + return nil; + } + return s; +} + +int +sshrsaread(void *va, void *buf, int n) +{ + Sshrsastate *s; + + s = va; + switch(s->phase){ + case Readpub: + s->phase = Done; + return snprint(buf, n, "%B", s->priv->pub.n); + case Readresp: + s->phase = Done; + return snprint(buf, n, "%B", s->resp); + default: + return 0; + } +} + +int +sshrsawrite(void *va, void *vbuf, int n) +{ + mpint *m; + char *buf; + Sshrsastate *s; + + s = va; + if((s->k->flags&Fconfirmuse) && confirm("ssh use") < 0) + return -1; + + buf = emalloc(n+1); + memmove(buf, vbuf, n); + buf[n] = '\0'; + m = strtomp(buf, nil, 16, nil); + free(buf); + if(m == nil){ + werrstr("bad bignum"); + return -1; + } + + agentlog("ssh use"); + m = rsadecrypt(s->priv, m, m); + s->resp = m; + s->phase = Readresp; + return n; +} + +void +sshrsaclose(void *v) +{ + Sshrsastate *s; + + s = v; + rsaprivfree(s->priv); + mpfree(s->resp); + free(s); +} + +Proto sshrsa = { +.name= "ssh-rsa", +.perm= 0666, +.open= sshrsaopen, +.read= sshrsaread, +.write= sshrsawrite, +.close= sshrsaclose, +}; blob - /dev/null blob + 7227c85566a54cece660b3900a723ec8ab3a92cc (mode 644) --- /dev/null +++ src/cmd/factotum/sshrsa.c @@ -0,0 +1,172 @@ +/* + * SSH RSA authentication. + * + * Client protocol: + * read public key + * if you don't like it, read another, repeat + * write challenge + * read response + * all numbers are hexadecimal biginits parsable with strtomp. + */ + +#include "dat.h" + +enum { + CHavePub, + CHaveResp, + + Maxphase, +}; + +static char *phasenames[] = { +[CHavePub] "CHavePub", +[CHaveResp] "CHaveResp", +}; + +struct State +{ + RSApriv *priv; + mpint *resp; + int off; + Key *key; +}; + +static RSApriv* +readrsapriv(Key *k) +{ + char *a; + RSApriv *priv; + + priv = rsaprivalloc(); + + if((a=strfindattr(k->attr, "ek"))==nil || (priv->pub.ek=strtomp(a, nil, 16, nil))==nil) + goto Error; + if((a=strfindattr(k->attr, "n"))==nil || (priv->pub.n=strtomp(a, nil, 16, nil))==nil) + goto Error; + if((a=strfindattr(k->privattr, "!p"))==nil || (priv->p=strtomp(a, nil, 16, nil))==nil) + goto Error; + if((a=strfindattr(k->privattr, "!q"))==nil || (priv->q=strtomp(a, nil, 16, nil))==nil) + goto Error; + if((a=strfindattr(k->privattr, "!kp"))==nil || (priv->kp=strtomp(a, nil, 16, nil))==nil) + goto Error; + if((a=strfindattr(k->privattr, "!kq"))==nil || (priv->kq=strtomp(a, nil, 16, nil))==nil) + goto Error; + if((a=strfindattr(k->privattr, "!c2"))==nil || (priv->c2=strtomp(a, nil, 16, nil))==nil) + goto Error; + if((a=strfindattr(k->privattr, "!dk"))==nil || (priv->dk=strtomp(a, nil, 16, nil))==nil) + goto Error; + return priv; + +Error: + rsaprivfree(priv); + return nil; +} + +static int +sshrsainit(Proto*, Fsstate *fss) +{ + int iscli; + State *s; + + if((iscli = isclient(strfindattr(fss->attr, "role"))) < 0) + return failure(fss, nil); + if(iscli==0) + return failure(fss, "sshrsa server unimplemented"); + + s = emalloc(sizeof *s); + fss->phasename = phasenames; + fss->maxphase = Maxphase; + fss->phase = CHavePub; + fss->ps = s; + return RpcOk; +} + +static int +sshrsaread(Fsstate *fss, void *va, uint *n) +{ + RSApriv *priv; + State *s; + + s = fss->ps; + switch(fss->phase){ + default: + return phaseerror(fss, "read"); + case CHavePub: + if(s->key){ + closekey(s->key); + s->key = nil; + } + if((s->key = findkey(fss, Kuser, nil, s->off, fss->attr, nil)) == nil) + return failure(fss, nil); + s->off++; + priv = s->key->priv; + *n = snprint(va, *n, "%B", priv->pub.n); + return RpcOk; + case CHaveResp: + *n = snprint(va, *n, "%B", s->resp); + fss->phase = Established; + return RpcOk; + } +} + +static int +sshrsawrite(Fsstate *fss, void *va, uint) +{ + mpint *m; + State *s; + + s = fss->ps; + switch(fss->phase){ + default: + return phaseerror(fss, "write"); + case CHavePub: + if(s->key == nil) + return failure(fss, "no current key"); + m = strtomp(va, nil, 16, nil); + m = rsadecrypt(s->key->priv, m, m); + s->resp = m; + fss->phase = CHaveResp; + return RpcOk; + } +} + +static void +sshrsaclose(Fsstate *fss) +{ + State *s; + + s = fss->ps; + if(s->key) + closekey(s->key); + if(s->resp) + mpfree(s->resp); + free(s); +} + +static int +sshrsaaddkey(Key *k) +{ + fmtinstall('B', mpconv); + + if((k->priv = readrsapriv(k)) == nil){ + werrstr("malformed key data"); + return -1; + } + return replacekey(k); +} + +static void +sshrsaclosekey(Key *k) +{ + rsaprivfree(k->priv); +} + +Proto sshrsa = { +.name= "sshrsa", +.init= sshrsainit, +.write= sshrsawrite, +.read= sshrsaread, +.close= sshrsaclose, +.addkey= sshrsaaddkey, +.closekey= sshrsaclosekey, +}; blob - /dev/null blob + 814664e0fb482cf11e5da201fb5f5c63d6b5b17a (mode 644) --- /dev/null +++ src/cmd/factotum/std.h @@ -0,0 +1,10 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include <9p.h> + blob - /dev/null blob + b41048980556fe7a81615ffbf1af9b703d5757b0 (mode 644) --- /dev/null +++ src/cmd/factotum/test.c @@ -0,0 +1,121 @@ +#include +#include +#include + +typedef struct Test Test; + +struct Test +{ + char *name; + int (*server)(Test*, AuthRpc*, int); + int (*client)(Test*, int); +}; + +int +ai2status(AuthInfo *ai) +{ + if(ai == nil) + return -1; + auth_freeAI(ai); + return 0; +} + +int +proxyserver(Test *t, AuthRpc *rpc, int fd) +{ + char buf[1024]; + + sprint(buf, "proto=%q role=server", t->name); + return ai2status(fauth_proxy(fd, rpc, nil, buf)); +} + +int +proxyclient(Test *t, int fd) +{ + return ai2status(auth_proxy(fd, auth_getkey, "proto=%q role=client", t->name)); +} + +Test test[] = +{ + "apop", proxyserver, proxyclient, + "cram", proxyserver, proxyclient, + "p9sk1", proxyserver, proxyclient, + "p9sk2", proxyserver, proxyclient, + "p9any", proxyserver, proxyclient, +}; + +void +usage(void) +{ + fprint(2, "usage: test [name]...\n"); + exits("usage"); +} + +void +runtest(AuthRpc *srpc, Test *t) +{ + int p[2], bad; + Waitmsg *w; + + if(pipe(p) < 0) + sysfatal("pipe: %r"); + + print("%s...", t->name); + + switch(fork()){ + case -1: + sysfatal("fork: %r"); + + case 0: + close(p[0]); + if((*t->server)(t, srpc, p[1]) < 0){ + print("\n\tserver: %r"); + _exits("oops"); + } + close(p[1]); + _exits(nil); + default: + close(p[1]); + if((*t->client)(t, p[0]) < 0){ + print("\n\tclient: %r"); + bad = 1; + } + close(p[0]); + break; + } + w = wait(); + if(w->msg[0]) + bad = 1; + print("\n"); +} + +void +main(int argc, char **argv) +{ + int i, j; + int afd; + AuthRpc *srpc; + + ARGBEGIN{ + default: + usage(); + }ARGEND + + quotefmtinstall(); + afd = open("/n/kremvax/factotum/rpc", ORDWR); + if(afd < 0) + sysfatal("open /n/kremvax/factotum/rpc: %r"); + srpc = auth_allocrpc(afd); + if(srpc == nil) + sysfatal("auth_allocrpc: %r"); + + if(argc == 0) + for(i=0; i[2]/dev/null +auth/aescbc -d < /usr/rsc/lib/factotum.aes >/n/sid/all +read -m /n/sid/all >/n/kremvax/factotum/ctl +read -m /n/sid/all >/mnt/factotum/ctl +unmount /n/sid + blob - /dev/null blob + 54b335190dc97925bd6c84a492a2341613e635a9 (mode 644) --- /dev/null +++ src/cmd/factotum/util.c @@ -0,0 +1,52 @@ +#include "std.h" +#include "dat.h" + +static int +unhex(char c) +{ + if('0' <= c && c <= '9') + return c-'0'; + if('a' <= c && c <= 'f') + return c-'a'+10; + if('A' <= c && c <= 'F') + return c-'A'+10; + abort(); + return -1; +} + +int +hexparse(char *hex, uchar *dat, int ndat) +{ + int i, n; + + n = strlen(hex); + if(n%2) + return -1; + n /= 2; + if(n > ndat) + return -1; + if(hex[strspn(hex, "0123456789abcdefABCDEF")] != '\0') + return -1; + for(i=0; i +#include +#include + +void +f(void*) +{ +} + +void +main(void) +{ + f(auth_challenge); + f(auth_response); +} blob - /dev/null blob + 508908476a0a4d8845e0f34c2f4a6de175c920e4 (mode 644) --- /dev/null +++ src/cmd/factotum/xio.c @@ -0,0 +1,165 @@ +#include "std.h" +#include "dat.h" + +static Ioproc *cache[5]; +static int ncache; + +static Ioproc* +xioproc(void) +{ + Ioproc *c; + int i; + + for(i=0; i #include #include +#define Tversion Tversion9p +#define Twrite Twrite9p +#include +#undef Tversion +#undef Twrite +#include #include #include "flayer.h" #include "samterm.h" @@ -212,27 +218,22 @@ plumbformat(Plumbmsg *m, int i) } void -plumbproc(void *argv) +plumbproc(void *arg) { - Channel *c; - int i, *fdp; - void **arg; + Fid *fid; + int i; Plumbmsg *m; - arg = argv; - c = arg[0]; - fdp = arg[1]; - - i = 0; - threadfdnoblock(*fdp); + fid = arg; + i = 0; for(;;){ - m = threadplumbrecv(*fdp); + m = plumbrecvfid(fid); if(m == nil){ fprint(2, "samterm: plumb read error: %r\n"); threadexits("plumb"); /* not a fatal error */ } if(plumbformat(m, i)){ - send(c, &i); + send(plumbc, &i); i = 1-i; /* toggle */ } } @@ -241,21 +242,18 @@ plumbproc(void *argv) int plumbstart(void) { - static int fd; - static void *arg[2]; + Fid *fid; plumbfd = plumbopen("send", OWRITE|OCEXEC); /* not open is ok */ - fd = plumbopen("edit", OREAD|OCEXEC); - if(fd < 0) + fid = plumbopenfid("edit", OREAD|OCEXEC); + if(fid == nil) return -1; plumbc = chancreate(sizeof(int), 0); if(plumbc == nil){ - close(fd); + fsclose(fid); return -1; } - arg[0] = plumbc; - arg[1] = &fd; - threadcreate(plumbproc, arg, STACK); + threadcreate(plumbproc, fid, STACK); return 1; } blob - f6efb917114a5736bdfed1cd41b029a823559a18 blob + a10ce3d0bd5f2a9c09e218568d54aa5868959f3a --- src/cmd/vac/vac.c +++ src/cmd/vac/vac.c @@ -1,8 +1,11 @@ +#include #include "stdinc.h" #include "vac.h" #include "dat.h" #include "fns.h" +int mainstacksize = 128*1024; + typedef struct Sink Sink; typedef struct MetaSink MetaSink; typedef struct DirSink DirSink; @@ -170,6 +173,9 @@ threadmain(int argc, char *argv[]) break; }ARGEND; + if(argc == 0) + usage(); + if(bsize < 512) bsize = 512; if(bsize > VtMaxLumpSize) @@ -215,8 +221,6 @@ vacwrite(VtConn *z, uchar score[VtScoreSize], int type sha1(buf, n, score, nil); return 0; } -sha1(buf, n, score, nil); -fprint(2, "write %V %d\n", score, type); return vtwrite(z, score, type, buf, n); } @@ -374,6 +378,18 @@ isexcluded(char *name) else /* x > 0 */ top = i; } + return 0; +} + +static int +islink(char *name) +{ + struct stat st; + + if(lstat(name, &st) < 0) + return 0; + if((st.st_mode&S_IFMT) == S_IFLNK) + return 1; return 0; } @@ -393,6 +409,9 @@ vacfile(DirSink *dsink, char *lname, char *sname, VacF if(merge && vacmerge(dsink, lname, sname) >= 0) return; + if(islink(sname)) + return; + fd = open(sname, OREAD); if(fd < 0) { warn("could not open file: %s: %r", lname); @@ -820,10 +839,8 @@ sinkclose(Sink *k) if(k->pbuf[n] > k->buf + kd->psize*n) break; -fprint(2, "type %d -> ", kd->type); base = kd->type&~VtTypeDepthMask; kd->type = base + sizetodepth(kd->size, kd->psize, kd->dsize); -fprint(2, "%d ", kd->type); /* skip full part of tree */ for(i=0; ipbuf[i] == k->buf + kd->psize*i; i++) @@ -831,7 +848,6 @@ fprint(2, "%d ", kd->type); /* is the tree completely full */ if(i == n && k->pbuf[n] == k->buf + kd->psize*n + VtScoreSize) { -fprint(2, "full\n"); memmove(kd->score, k->pbuf[n] - VtScoreSize, VtScoreSize); return; } @@ -846,7 +862,6 @@ fprint(2, "full\n"); k->pbuf[i+1] += VtScoreSize; } memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize); -fprint(2, "%V\n", kd->score); } void @@ -881,7 +896,6 @@ dirsinkwrite(DirSink *k, VtEntry *dir) sinkwrite(k->sink, k->buf, k->p - k->buf); k->p = k->buf; } -fprint(2, "write entry %V %d\n", dir->score, dir->type); vtentrypack(dir, k->p, 0); k->nentry++; k->p += VtEntrySize; blob - 2a2d1391338543560e7cb5209cfa0016dc0c26b2 blob + 384196cf4281d0bd9351fbc66b9713726f855801 --- src/lib9/getenv.c +++ src/lib9/getenv.c @@ -22,6 +22,5 @@ p9putenv(char *s, char *v) if(t == nil) return -1; putenv(t); - free(t); return 0; } blob - 7824ff18d26d8d74d03f3c45d196d522416275ac blob + d388d224e9787a3328043b613193caede3d1ab22 --- src/lib9/mkfile +++ src/lib9/mkfile @@ -107,6 +107,8 @@ LIB9OFILES=\ getuser.$O\ getwd.$O\ jmp.$O\ + lrand.$O\ + lnrand.$O\ lock.$O\ main.$O\ malloc.$O\ @@ -119,6 +121,7 @@ LIB9OFILES=\ nrand.$O\ nulldir.$O\ open.$O\ + opentemp.$O\ pipe.$O\ post9p.$O\ postnote.$O\ blob - f9fe2420e57a5ca68c6c61a38f3e54f4ac7879e7 blob + 01b244ef61434728657aa4b68ced75f51612b0da --- src/lib9/pipe.c +++ src/lib9/pipe.c @@ -3,6 +3,11 @@ #include #include +/* + * We use socketpair to get a two-way pipe. + * The pipe still doesn't preserve message boundaries. + * Worse, it cannot be reopened via /dev/fd/NNN on Linux. + */ int p9pipe(int fd[2]) { blob - /dev/null blob + e8313be1e4a67ba2b74b2e8b5900f26c19648628 (mode 644) --- /dev/null +++ src/lib9p/_post.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include <9p.h> +#include +#include "post.h" + +Postcrud* +_post1(Srv *s, char *name, char *mtpt, int flag) +{ + Postcrud *p; + + p = emalloc9p(sizeof *p); + if(!s->nopipe){ + if(pipe(p->fd) < 0) + sysfatal("pipe: %r"); + s->infd = s->outfd = p->fd[1]; + s->srvfd = p->fd[0]; + } + if(name) + if(postfd(name, s->srvfd) < 0) + sysfatal("postfd %s: %r", name); + p->s = s; + p->mtpt = mtpt; + p->flag = flag; + return p; +} + +void +_post2(void *v) +{ + Srv *s; + + s = v; + rfork(RFNOTEG); + if(!s->leavefdsopen){ + rendezvous((ulong)s, 0); + close(s->srvfd); + } + srv(s); +} + +void +_post3(Postcrud *p) +{ + /* + * Normally the server is posting as the last thing it does + * before exiting, so the correct thing to do is drop into + * a different fd space and close the 9P server half of the + * pipe before trying to mount the kernel half. This way, + * if the file server dies, we don't have a ref to the 9P server + * half of the pipe. Then killing the other procs will drop + * all the refs on the 9P server half, and the mount will fail. + * Otherwise the mount hangs forever. + * + * Libthread in general and acme win in particular make + * it hard to make this fd bookkeeping work out properly, + * so leaveinfdopen is a flag that win sets to opt out of this + * safety net. + */ + if(!p->s->leavefdsopen){ + rfork(RFFDG); + rendezvous((ulong)p->s, 0); + close(p->s->infd); + if(p->s->infd != p->s->outfd) + close(p->s->outfd); + } + + if(p->mtpt){ + if(amount(p->s->srvfd, p->mtpt, p->flag, "") == -1) + sysfatal("mount %s: %r", p->mtpt); + }else + close(p->s->srvfd); + free(p); +} + blob - /dev/null blob + 7fb88305058858b3701f4b83722d7d0e2fe7cf30 (mode 644) --- /dev/null +++ src/lib9p/dirread.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +void +dirread9p(Req *r, Dirgen *gen, void *aux) +{ + int start; + uchar *p, *ep; + uint rv; + Dir d; + + if(r->ifcall.offset == 0) + start = 0; + else + start = r->fid->dirindex; + + p = (uchar*)r->ofcall.data; + ep = p+r->ifcall.count; + + while(p < ep){ + memset(&d, 0, sizeof d); + if((*gen)(start, &d, aux) < 0) + break; + rv = convD2M(&d, p, ep-p); + free(d.name); + free(d.muid); + free(d.uid); + free(d.gid); + if(rv <= BIT16SZ) + break; + p += rv; + start++; + } + r->fid->dirindex = start; + r->ofcall.count = p - (uchar*)r->ofcall.data; +} blob - /dev/null blob + 2393f1c4c155fcdde1736ce99e66a0aee6fd6be8 (mode 644) --- /dev/null +++ src/lib9p/fid.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include "9p.h" + +static void +incfidref(void *v) +{ + Fid *f; + + f = v; + if(f) + incref(&f->ref); +} + +Fidpool* +allocfidpool(void (*destroy)(Fid*)) +{ + Fidpool *f; + + f = emalloc9p(sizeof *f); + f->map = allocmap(incfidref); + f->destroy = destroy; + return f; +} + +void +freefidpool(Fidpool *p) +{ + freemap(p->map, (void(*)(void*))p->destroy); + free(p); +} + +Fid* +allocfid(Fidpool *pool, ulong fid) +{ + Fid *f; + + f = emalloc9p(sizeof *f); + f->fid = fid; + f->omode = -1; + f->pool = pool; + + incfidref(f); + incfidref(f); + if(caninsertkey(pool->map, fid, f) == 0){ + closefid(f); + return nil; + } + + return f; +} + +Fid* +lookupfid(Fidpool *pool, ulong fid) +{ + return lookupkey(pool->map, fid); +} + +void +closefid(Fid *f) +{ + if(decref(&f->ref) == 0) { + if(f->rdir) + closedirfile(f->rdir); + if(f->pool->destroy) + f->pool->destroy(f); + if(f->file) + closefile(f->file); + free(f->uid); + free(f); + } +} + +Fid* +removefid(Fidpool *pool, ulong fid) +{ + return deletekey(pool->map, fid); +} blob - /dev/null blob + 79a8a11d4050d8c529170b1139ea0b81dcf2e25e (mode 644) --- /dev/null +++ src/lib9p/file.c @@ -0,0 +1,372 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +/* + * To avoid deadlock, the following rules must be followed. + * Always lock child then parent, never parent then child. + * If holding the free file lock, do not lock any Files. + */ +struct Filelist { + File *f; + Filelist *link; +}; + +static QLock filelk; +static File *freefilelist; + +static File* +allocfile(void) +{ + int i, a; + File *f; + enum { N = 16 }; + + qlock(&filelk); + if(freefilelist == nil){ + f = emalloc9p(N*sizeof(*f)); + for(i=0; iaux; + qunlock(&filelk); + + a = f->allocd; + memset(f, 0, sizeof *f); + f->allocd = a; + return f; +} + +static void +freefile(File *f) +{ + Filelist *fl, *flnext; + + for(fl=f->filelist; fl; fl=flnext){ + flnext = fl->link; + assert(fl->f == nil); + free(fl); + } + + free(f->dir.name); + free(f->dir.uid); + free(f->dir.gid); + free(f->dir.muid); + qlock(&filelk); + assert(f->ref.ref == 0); + f->aux = freefilelist; + freefilelist = f; + qunlock(&filelk); +} + +void +closefile(File *f) +{ + if(decref(&f->ref) == 0){ + f->tree->destroy(f); + freefile(f); + } +} + +static void +nop(File *f) +{ + USED(f); +} + +int +removefile(File *f) +{ + File *fp; + Filelist *fl; + + fp = f->parent; + if(fp == nil){ + werrstr("no parent"); + closefile(f); + return -1; + } + + if(fp == f){ + werrstr("cannot remove root"); + closefile(f); + return -1; + } + + wlock(&fp->rwlock); + wlock(&f->rwlock); + if(f->nchild != 0){ + werrstr("has children"); + wunlock(&f->rwlock); + wunlock(&fp->rwlock); + closefile(f); + return -1; + } + + if(f->parent != fp){ + werrstr("parent changed underfoot"); + wunlock(&f->rwlock); + wunlock(&fp->rwlock); + closefile(f); + return -1; + } + + for(fl=fp->filelist; fl; fl=fl->link) + if(fl->f == f) + break; + assert(fl != nil && fl->f == f); + + fl->f = nil; + fp->nchild--; + f->parent = nil; + wunlock(&fp->rwlock); + wunlock(&f->rwlock); + + closefile(fp); /* reference from child */ + closefile(f); /* reference from tree */ + closefile(f); + return 0; +} + +File* +createfile(File *fp, char *name, char *uid, ulong perm, void *aux) +{ + File *f; + Filelist *fl, *freel; + Tree *t; + + if((fp->dir.qid.type&QTDIR) == 0){ + werrstr("create in non-directory"); + return nil; + } + + freel = nil; + wlock(&fp->rwlock); + for(fl=fp->filelist; fl; fl=fl->link){ + if(fl->f == nil) + freel = fl; + else if(strcmp(fl->f->dir.name, name) == 0){ + wunlock(&fp->rwlock); + werrstr("file already exists"); + return nil; + } + } + + if(freel == nil){ + freel = emalloc9p(sizeof *freel); + freel->link = fp->filelist; + fp->filelist = freel; + } + + f = allocfile(); + f->dir.name = estrdup9p(name); + f->dir.uid = estrdup9p(uid ? uid : fp->dir.uid); + f->dir.gid = estrdup9p(fp->dir.gid); + f->dir.muid = estrdup9p(uid ? uid : "unknown"); + f->aux = aux; + f->dir.mode = perm; + + t = fp->tree; + lock(&t->genlock); + f->dir.qid.path = t->qidgen++; + unlock(&t->genlock); + if(perm & DMDIR) + f->dir.qid.type |= QTDIR; + if(perm & DMAPPEND) + f->dir.qid.type |= QTAPPEND; + if(perm & DMEXCL) + f->dir.qid.type |= QTEXCL; + + f->dir.mode = perm; + f->dir.atime = f->dir.mtime = time(0); + f->dir.length = 0; + f->parent = fp; + incref(&fp->ref); + f->tree = fp->tree; + + incref(&f->ref); /* being returned */ + incref(&f->ref); /* for the tree */ + freel->f = f; + fp->nchild++; + wunlock(&fp->rwlock); + + return f; +} + +static File* +walkfile1(File *dir, char *elem) +{ + File *fp; + Filelist *fl; + + rlock(&dir->rwlock); + if(strcmp(elem, "..") == 0){ + fp = dir->parent; + incref(&fp->ref); + runlock(&dir->rwlock); + closefile(dir); + return fp; + } + + fp = nil; + for(fl=dir->filelist; fl; fl=fl->link) + if(fl->f && strcmp(fl->f->dir.name, elem)==0){ + fp = fl->f; + incref(&fp->ref); + break; + } + + runlock(&dir->rwlock); + closefile(dir); + return fp; +} + +File* +walkfile(File *f, char *path) +{ + char *os, *s, *nexts; + File *nf; + + if(strchr(path, '/') == nil) + return walkfile1(f, path); /* avoid malloc */ + + os = s = estrdup9p(path); + incref(&f->ref); + for(; *s; s=nexts){ + if(nexts = strchr(s, '/')) + *nexts++ = '\0'; + else + nexts = s+strlen(s); + nf = walkfile1(f, s); + decref(&f->ref); + f = nf; + if(f == nil) + break; + } + free(os); + return f; +} + +Tree* +alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*)) +{ + char *muid; + Tree *t; + File *f; + + t = emalloc9p(sizeof *t); + f = allocfile(); + f->dir.name = estrdup9p("/"); + if(uid == nil){ + if(uid = getuser()) + uid = estrdup9p(uid); + } + if(uid == nil) + uid = estrdup9p("none"); + else + uid = estrdup9p(uid); + + if(gid == nil) + gid = estrdup9p(uid); + else + gid = estrdup9p(gid); + + muid = estrdup9p(uid); + + f->dir.qid = (Qid){0, 0, QTDIR}; + f->dir.length = 0; + f->dir.atime = f->dir.mtime = time(0); + f->dir.mode = DMDIR | mode; + f->tree = t; + f->parent = f; + f->dir.uid = uid; + f->dir.gid = gid; + f->dir.muid = muid; + + incref(&f->ref); + t->root = f; + t->qidgen = 0; + t->dirqidgen = 1; + if(destroy == nil) + destroy = nop; + t->destroy = destroy; + + return t; +} + +static void +_freefiles(File *f) +{ + Filelist *fl, *flnext; + + for(fl=f->filelist; fl; fl=flnext){ + flnext = fl->link; + _freefiles(fl->f); + free(fl); + } + + f->tree->destroy(f); + freefile(f); +} + +void +freetree(Tree *t) +{ + _freefiles(t->root); + free(t); +} + +struct Readdir { + Filelist *fl; +}; + +Readdir* +opendirfile(File *dir) +{ + Readdir *r; + + rlock(&dir->rwlock); + if((dir->dir.mode & DMDIR)==0){ + runlock(&dir->rwlock); + return nil; + } + r = emalloc9p(sizeof(*r)); + + /* + * This reference won't go away while we're using it + * since we are dir->rdir. + */ + r->fl = dir->filelist; + runlock(&dir->rwlock); + return r; +} + +long +readdirfile(Readdir *r, uchar *buf, long n) +{ + long x, m; + Filelist *fl; + + for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){ + if(fl->f == nil) + x = 0; + else if((x=convD2M(&fl->f->dir, buf+m, n-m)) <= BIT16SZ) + break; + } + r->fl = fl; + return m; +} + +void +closedirfile(Readdir *r) +{ + free(r); +} blob - /dev/null blob + 6692b7f16446f3024241e6f6d5605cb79219caed (mode 644) --- /dev/null +++ src/lib9p/ftest.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include "9p.h" + +void +main(void) +{ + Tree *t; + File *hello, *goodbye, *world; + + t = mktree(); + + hello = fcreate(t->root, "hello", CHDIR|0777); + assert(hello != nil); + + goodbye = fcreate(t->root, "goodbye", CHDIR|0777); + assert(goodbye != nil); + + world = fcreate(hello, "world", 0666); + assert(world != nil); + world = fcreate(goodbye, "world", 0666); + assert(world != nil); + fdump(t->root, 0); + + fremove(world); + fdump(t->root, 0); +} blob - /dev/null blob + 4869567523ec56c5363cfee49f0955ba08cd0634 (mode 644) --- /dev/null +++ src/lib9p/intmap.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +enum { + NHASH = 128 +}; + +typedef struct Intlist Intlist; +struct Intlist +{ + ulong id; + void* aux; + Intlist* link; +}; + +struct Intmap +{ + RWLock rwlock; + Intlist* hash[NHASH]; + void (*inc)(void*); +}; + +static ulong +hashid(ulong id) +{ + return id%NHASH; +} + +static void +nop(void *v) +{ + USED(v); +} + +Intmap* +allocmap(void (*inc)(void*)) +{ + Intmap *m; + + m = emalloc9p(sizeof(*m)); + if(inc == nil) + inc = nop; + m->inc = inc; + return m; +} + +void +freemap(Intmap *map, void (*destroy)(void*)) +{ + int i; + Intlist *p, *nlink; + + if(destroy == nil) + destroy = nop; + for(i=0; ihash[i]; p; p=nlink){ + nlink = p->link; + destroy(p->aux); + free(p); + } + } + + free(map); +} + +static Intlist** +llookup(Intmap *map, ulong id) +{ + Intlist **lf; + + for(lf=&map->hash[hashid(id)]; *lf; lf=&(*lf)->link) + if((*lf)->id == id) + break; + return lf; +} + +/* + * The RWlock is used as expected except that we allow + * inc() to be called while holding it. This is because we're + * locking changes to the tree structure, not to the references. + * Inc() is expected to have its own locking. + */ +void* +lookupkey(Intmap *map, ulong id) +{ + Intlist *f; + void *v; + + rlock(&map->rwlock); + if(f = *llookup(map, id)){ + v = f->aux; + map->inc(v); + }else + v = nil; + runlock(&map->rwlock); + return v; +} + +void* +insertkey(Intmap *map, ulong id, void *v) +{ + Intlist *f; + void *ov; + ulong h; + + wlock(&map->rwlock); + if(f = *llookup(map, id)){ + /* no decrement for ov because we're returning it */ + ov = f->aux; + f->aux = v; + }else{ + f = emalloc9p(sizeof(*f)); + f->id = id; + f->aux = v; + h = hashid(id); + f->link = map->hash[h]; + map->hash[h] = f; + ov = nil; + } + wunlock(&map->rwlock); + return ov; +} + +int +caninsertkey(Intmap *map, ulong id, void *v) +{ + Intlist *f; + int rv; + ulong h; + + wlock(&map->rwlock); + if(*llookup(map, id)) + rv = 0; + else{ + f = emalloc9p(sizeof *f); + f->id = id; + f->aux = v; + h = hashid(id); + f->link = map->hash[h]; + map->hash[h] = f; + rv = 1; + } + wunlock(&map->rwlock); + return rv; +} + +void* +deletekey(Intmap *map, ulong id) +{ + Intlist **lf, *f; + void *ov; + + wlock(&map->rwlock); + if(f = *(lf = llookup(map, id))){ + ov = f->aux; + *lf = f->link; + free(f); + }else + ov = nil; + wunlock(&map->rwlock); + return ov; +} blob - /dev/null blob + b4414951fe2d219a8d85fa1e97fae66d5a6f1aff (mode 644) --- /dev/null +++ src/lib9p/mem.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include "9p.h" + +void* +emalloc9p(ulong sz) +{ + void *v; + + if((v = malloc(sz)) == nil) { + fprint(2, "out of memory allocating %lud\n", sz); + exits("mem"); + } + memset(v, 0, sz); + setmalloctag(v, getcallerpc(&sz)); + return v; +} + +void* +erealloc9p(void *v, ulong sz) +{ + void *nv; + + if((nv = realloc(v, sz)) == nil) { + fprint(2, "out of memory allocating %lud\n", sz); + exits("mem"); + } + if(v == nil) + setmalloctag(nv, getcallerpc(&v)); + setrealloctag(nv, getcallerpc(&v)); + return nv; +} + +char* +estrdup9p(char *s) +{ + char *t; + + if((t = strdup(s)) == nil) { + fprint(2, "out of memory in strdup(%.10s)\n", s); + exits("mem"); + } + setmalloctag(t, getcallerpc(&s)); + return t; +} + blob - /dev/null blob + 17a10a3c18a03163b8542662ceb54214ee5da7b5 (mode 644) --- /dev/null +++ src/lib9p/mkfile @@ -0,0 +1,20 @@ +PLAN9=../.. +<$PLAN9/src/mkhdr + +LIB=lib9p.a +OFILES=\ + _post.$O\ + dirread.$O\ + fid.$O\ + file.$O\ + intmap.$O\ + mem.$O\ + req.$O\ + parse.$O\ + post.$O\ + srv.$O\ + tpost.$O\ + uid.$O\ + util.$O\ + +<$PLAN9/src/mksyslib blob - /dev/null blob + 753ae79db404da55f8ae9c85d4db067aa4522b9f (mode 644) --- /dev/null +++ src/lib9p/parse.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include <9p.h> + +/* + * Generous estimate of number of fields, including terminal nil pointer + */ +static int +ncmdfield(char *p, int n) +{ + int white, nwhite; + char *ep; + int nf; + + if(p == nil) + return 1; + + nf = 0; + ep = p+n; + white = 1; /* first text will start field */ + while(p < ep){ + nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */ + if(white && !nwhite) /* beginning of field */ + nf++; + white = nwhite; + } + return nf+1; /* +1 for nil */ +} + +/* + * parse a command written to a device + */ +Cmdbuf* +parsecmd(char *p, int n) +{ + Cmdbuf *cb; + int nf; + char *sp; + + nf = ncmdfield(p, n); + + /* allocate Cmdbuf plus string pointers plus copy of string including \0 */ + sp = emalloc9p(sizeof(*cb) + nf * sizeof(char*) + n + 1); + cb = (Cmdbuf*)sp; + cb->f = (char**)(&cb[1]); + cb->buf = (char*)(&cb->f[nf]); + + memmove(cb->buf, p, n); + + /* dump new line and null terminate */ + if(n > 0 && cb->buf[n-1] == '\n') + n--; + cb->buf[n] = '\0'; + + cb->nf = tokenize(cb->buf, cb->f, nf-1); + cb->f[cb->nf] = nil; + + return cb; +} + +/* + * Reconstruct original message, for error diagnostic + */ +void +respondcmderror(Req *r, Cmdbuf *cb, char *fmt, ...) +{ + int i; + va_list arg; + char *p, *e; + char err[ERRMAX]; + + e = err+ERRMAX-10; + va_start(arg, fmt); + p = vseprint(err, e, fmt, arg); + va_end(arg); + p = seprint(p, e, ": \""); + for(i=0; inf; i++){ + if(i > 0) + p = seprint(p, e, " "); + p = seprint(p, e, "%q", cb->f[i]); + } + strcpy(p, "\""); + respond(r, err); +} + +/* + * Look up entry in table + */ +Cmdtab* +lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab) +{ + int i; + Cmdtab *ct; + + if(cb->nf == 0){ + werrstr("empty control message"); + return nil; + } + + for(ct = ctab, i=0; icmd, "*") !=0) /* wildcard always matches */ + if(strcmp(ct->cmd, cb->f[0]) != 0) + continue; + if(ct->narg != 0 && ct->narg != cb->nf){ + werrstr("bad # args to command"); + return nil; + } + return ct; + } + + werrstr("unknown control message"); + return nil; +} blob - /dev/null blob + 09296a8899338c6a412742afeb4e53e61e67ae49 (mode 644) --- /dev/null +++ src/lib9p/post.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include <9p.h> +#include "post.h" + +void +postmountsrv(Srv *s, char *name, char *mtpt, int flag) +{ + Postcrud *p; + + p = _post1(s, name, mtpt, flag); + switch(rfork(RFPROC|RFNOTEG|RFNAMEG|RFMEM)){ + case -1: + sysfatal("rfork: %r"); + case 0: + _post2(s); + exits(nil); + default: + _post3(p); + } +} + blob - /dev/null blob + 069a7ce7b9a0ec446e76ee293d47a02168ca7481 (mode 644) --- /dev/null +++ src/lib9p/post.h @@ -0,0 +1,13 @@ +typedef struct Postcrud Postcrud; +struct Postcrud +{ + int fd[2]; + Srv *s; + char *name; + char *mtpt; + int flag; +}; + +Postcrud *_post1(Srv*, char*, char*, int); +void _post2(void*); +void _post3(Postcrud*); blob - /dev/null blob + a2f0b3d71824df0f665397fb4937820a47d44d25 (mode 644) --- /dev/null +++ src/lib9p/ramfs.c @@ -0,0 +1,163 @@ +#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; +}; + +void +fsread(Req *r) +{ + Ramfile *rf; + vlong offset; + long count; + + rf = r->fid->file->aux; + offset = r->ifcall.offset; + count = r->ifcall.count; + +//print("read %ld %lld\n", *count, offset); + if(offset >= rf->ndata){ + r->ofcall.count = 0; + respond(r, nil); + 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) +{ + void *v; + Ramfile *rf; + vlong offset; + long count; + + 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; + } + 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) +{ + Ramfile *rf; + File *f; + + if(f = createfile(r->fid->file, r->ifcall.name, r->fid->uid, r->ifcall.perm, nil)){ + rf = emalloc9p(sizeof *rf); + f->aux = rf; + r->fid->file = f; + r->ofcall.qid = f->qid; + respond(r, nil); + return; + } + respond(r, Ebad); +} + +void +fsopen(Req *r) +{ + Ramfile *rf; + + rf = r->fid->file->aux; + + if(rf && (r->ifcall.mode&OTRUNC)){ + rf->ndata = 0; + r->fid->file->length = 0; + } + + respond(r, nil); +} + +void +fsdestroyfile(File *f) +{ + Ramfile *rf; + +//fprint(2, "clunk\n"); + rf = f->aux; + if(rf){ + 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"); + 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; + + ARGBEGIN{ + case 'D': + chatty9p++; + break; + case 's': + srvname = EARGF(usage()); + break; + case 'm': + mtpt = EARGF(usage()); + break; + default: + usage(); + }ARGEND; + + if(argc) + 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); +} blob - /dev/null blob + 8e1aaab5480d6fab92a9d4c5c3687dd855386bb9 (mode 644) --- /dev/null +++ src/lib9p/req.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +static void +increqref(void *v) +{ + Req *r; + + r = v; + if(r){ +if(chatty9p > 1) + fprint(2, "increfreq %p %ld\n", r, r->ref.ref); + incref(&r->ref); + } +} + +Reqpool* +allocreqpool(void (*destroy)(Req*)) +{ + Reqpool *f; + + f = emalloc9p(sizeof *f); + f->map = allocmap(increqref); + f->destroy = destroy; + return f; +} + +void +freereqpool(Reqpool *p) +{ + freemap(p->map, (void(*)(void*))p->destroy); + free(p); +} + +Req* +allocreq(Reqpool *pool, ulong tag) +{ + Req *r; + + r = emalloc9p(sizeof *r); + r->tag = tag; + r->pool = pool; + + increqref(r); + increqref(r); + if(caninsertkey(pool->map, tag, r) == 0){ + closereq(r); + return nil; + } + + return r; +} + +Req* +lookupreq(Reqpool *pool, ulong tag) +{ +if(chatty9p > 1) + fprint(2, "lookupreq %lud\n", tag); + return lookupkey(pool->map, tag); +} + +void +closereq(Req *r) +{ + int i; + + if(r == nil) + return; + +if(chatty9p > 1) + fprint(2, "closereq %p %ld\n", r, r->ref.ref); + + if(decref(&r->ref) == 0){ + if(r->fid) + closefid(r->fid); + if(r->newfid) + closefid(r->newfid); + if(r->afid) + closefid(r->afid); + if(r->oldreq) + closereq(r->oldreq); + for(i=0; inflush; i++) + respond(r->flush[i], nil); + free(r->flush); + switch(r->ifcall.type){ + case Tstat: + free(r->ofcall.stat); + free(r->d.name); + free(r->d.uid); + free(r->d.gid); + free(r->d.muid); + break; + } + if(r->pool->destroy) + r->pool->destroy(r); + free(r->buf); + free(r->rbuf); + free(r); + } +} + +Req* +removereq(Reqpool *pool, ulong tag) +{ +if(chatty9p > 1) + fprint(2, "removereq %lud\n", tag); + return deletekey(pool->map, tag); +} blob - /dev/null blob + c5262f4c79c5b52ea4264335741f8c434edb4aba (mode 644) --- /dev/null +++ src/lib9p/srv.c @@ -0,0 +1,837 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +// static char Ebadattach[] = "unknown specifier in attach"; +static char Ebadoffset[] = "bad offset"; +// static char Ebadcount[] = "bad count"; +static char Ebotch[] = "9P protocol botch"; +static char Ecreatenondir[] = "create in non-directory"; +static char Edupfid[] = "duplicate fid"; +static char Eduptag[] = "duplicate tag"; +static char Eisdir[] = "is a directory"; +static char Enocreate[] = "create prohibited"; +// static char Enomem[] = "out of memory"; +static char Enoremove[] = "remove prohibited"; +static char Enostat[] = "stat prohibited"; +static char Enotfound[] = "file not found"; +// static char Enowrite[] = "write prohibited"; +static char Enowstat[] = "wstat prohibited"; +static char Eperm[] = "permission denied"; +static char Eunknownfid[] = "unknown fid"; +static char Ebaddir[] = "bad directory in wstat"; +static char Ewalknodir[] = "walk in non-directory"; + +static void +setfcallerror(Fcall *f, char *err) +{ + f->ename = err; + f->type = Rerror; +} + +static void +changemsize(Srv *srv, int msize) +{ + if(srv->rbuf && srv->wbuf && srv->msize == msize) + return; + qlock(&srv->rlock); + qlock(&srv->wlock); + srv->msize = msize; + free(srv->rbuf); + free(srv->wbuf); + srv->rbuf = emalloc9p(msize); + srv->wbuf = emalloc9p(msize); + qunlock(&srv->rlock); + qunlock(&srv->wlock); +} + +static Req* +getreq(Srv *s) +{ + long n; + uchar *buf; + Fcall f; + Req *r; + + qlock(&s->rlock); + if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){ + qunlock(&s->rlock); + return nil; + } + + buf = emalloc9p(n); + memmove(buf, s->rbuf, n); + qunlock(&s->rlock); + + if(convM2S(buf, n, &f) != n){ + free(buf); + return nil; + } + + if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */ + r = emalloc9p(sizeof *r); + incref(&r->ref); + r->tag = f.tag; + r->ifcall = f; + r->error = Eduptag; + r->buf = buf; + r->responded = 0; + r->type = 0; + r->srv = s; + r->pool = nil; +if(chatty9p) + fprint(2, "<-%d- %F: dup tag\n", s->infd, &f); + return r; + } + + r->srv = s; + r->responded = 0; + r->buf = buf; + r->ifcall = f; + memset(&r->ofcall, 0, sizeof r->ofcall); + r->type = r->ifcall.type; + +if(chatty9p) + if(r->error) + fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error); + else + fprint(2, "<-%d- %F\n", s->infd, &r->ifcall); + + return r; +} + +static void +filewalk(Req *r) +{ + int i; + File *f; + + f = r->fid->file; + assert(f != nil); + + incref(&f->ref); + for(i=0; iifcall.nwname; i++) + if(f = walkfile(f, r->ifcall.wname[i])) + r->ofcall.wqid[i] = f->dir.qid; + else + break; + + r->ofcall.nwqid = i; + if(f){ + r->newfid->file = f; + r->newfid->qid = r->newfid->file->dir.qid; + } + respond(r, nil); +} + +void +walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg) +{ + int i; + char *e; + + if(r->fid == r->newfid && r->ifcall.nwname > 1){ + respond(r, "lib9p: unused documented feature not implemented"); + return; + } + + if(r->fid != r->newfid){ + r->newfid->qid = r->fid->qid; + if(clone && (e = clone(r->fid, r->newfid, arg))){ + respond(r, e); + return; + } + } + + e = nil; + for(i=0; iifcall.nwname; i++){ + if(e = walk1(r->newfid, r->ifcall.wname[i], arg)) + break; + r->ofcall.wqid[i] = r->newfid->qid; + } + + r->ofcall.nwqid = i; + if(e && i==0) + respond(r, e); + else + respond(r, nil); +} + +static void +sversion(Srv *srv, Req *r) +{ + USED(srv); + + if(strncmp(r->ifcall.version, "9P", 2) != 0){ + r->ofcall.version = "unknown"; + respond(r, nil); + return; + } + + r->ofcall.version = "9P2000"; + r->ofcall.msize = r->ifcall.msize; + respond(r, nil); +} +static void +rversion(Req *r, char *error) +{ + assert(error == nil); + changemsize(r->srv, r->ofcall.msize); +} + +static void +sauth(Srv *srv, Req *r) +{ + char e[ERRMAX]; + + if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){ + respond(r, Edupfid); + return; + } + if(srv->auth) + srv->auth(r); + else{ + snprint(e, sizeof e, "%s: authentication not required", argv0); + respond(r, e); + } +} +static void +rauth(Req *r, char *error) +{ + if(error && r->afid) + closefid(removefid(r->srv->fpool, r->afid->fid)); +} + +static void +sattach(Srv *srv, Req *r) +{ + if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){ + respond(r, Edupfid); + return; + } + r->afid = nil; + if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){ + respond(r, Eunknownfid); + return; + } + r->fid->uid = estrdup9p(r->ifcall.uname); + if(srv->tree){ + r->fid->file = srv->tree->root; + /* BUG? incref(r->fid->file) ??? */ + r->ofcall.qid = r->fid->file->dir.qid; + r->fid->qid = r->ofcall.qid; + } + if(srv->attach) + srv->attach(r); + else + respond(r, nil); + return; +} +static void +rattach(Req *r, char *error) +{ + if(error && r->fid) + closefid(removefid(r->srv->fpool, r->fid->fid)); +} + +static void +sflush(Srv *srv, Req *r) +{ + r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag); + if(r->oldreq == nil || r->oldreq == r) + respond(r, nil); + else if(srv->flush) + srv->flush(r); + else + respond(r, nil); +} +static int +rflush(Req *r, char *error) +{ + Req *or; + + assert(error == nil); + or = r->oldreq; + if(or){ + qlock(&or->lk); + if(or->responded == 0){ + or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0])); + or->flush[or->nflush++] = r; + qunlock(&or->lk); + return -1; /* delay response until or is responded */ + } + qunlock(&or->lk); + closereq(or); + } + r->oldreq = nil; + return 0; +} + +static char* +oldwalk1(Fid *fid, char *name, void *arg) +{ + char *e; + Qid qid; + Srv *srv; + + srv = arg; + e = srv->walk1(fid, name, &qid); + if(e) + return e; + fid->qid = qid; + return nil; +} + +static char* +oldclone(Fid *fid, Fid *newfid, void *arg) +{ + Srv *srv; + + srv = arg; + if(srv->clone == nil) + return nil; + return srv->clone(fid, newfid); +} + +static void +swalk(Srv *srv, Req *r) +{ + if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ + respond(r, Eunknownfid); + return; + } + if(r->fid->omode != -1){ + respond(r, "cannot clone open fid"); + return; + } + if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){ + respond(r, Ewalknodir); + return; + } + if(r->ifcall.fid != r->ifcall.newfid){ + if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){ + respond(r, Edupfid); + return; + } + r->newfid->uid = estrdup9p(r->fid->uid); + }else{ + incref(&r->fid->ref); + r->newfid = r->fid; + } + if(r->fid->file){ + filewalk(r); + }else if(srv->walk1) + walkandclone(r, oldwalk1, oldclone, srv); + else if(srv->walk) + srv->walk(r); + else + sysfatal("no walk function, no file trees"); +} +static void +rwalk(Req *r, char *error) +{ + if(error || r->ofcall.nwqid < r->ifcall.nwname){ + if(r->ifcall.fid != r->ifcall.newfid && r->newfid) + closefid(removefid(r->srv->fpool, r->newfid->fid)); + if (r->ofcall.nwqid==0){ + if(error==nil && r->ifcall.nwname!=0) + r->error = Enotfound; + }else + r->error = nil; // No error on partial walks + }else{ + if(r->ofcall.nwqid == 0){ + /* Just a clone */ + r->newfid->qid = r->fid->qid; + }else{ + /* if file trees are in use, filewalk took care of the rest */ + r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1]; + } + } +} + +static void +sopen(Srv *srv, Req *r) +{ + int p; + + if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ + respond(r, Eunknownfid); + return; + } + if(r->fid->omode != -1){ + respond(r, Ebotch); + return; + } + if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){ + respond(r, Eisdir); + return; + } + r->ofcall.qid = r->fid->qid; + switch(r->ifcall.mode&3){ + default: + assert(0); + case OREAD: + p = AREAD; + break; + case OWRITE: + p = AWRITE; + break; + case ORDWR: + p = AREAD|AWRITE; + break; + case OEXEC: + p = AEXEC; + break; + } + if(r->ifcall.mode&OTRUNC) + p |= AWRITE; + if((r->fid->qid.type&QTDIR) && p!=AREAD){ + respond(r, Eperm); + return; + } + if(r->fid->file){ + if(!hasperm(r->fid->file, r->fid->uid, p)){ + respond(r, Eperm); + return; + } + /* BUG RACE */ + if((r->ifcall.mode&ORCLOSE) + && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){ + respond(r, Eperm); + return; + } + r->ofcall.qid = r->fid->file->dir.qid; + if((r->ofcall.qid.type&QTDIR) + && (r->fid->rdir = opendirfile(r->fid->file)) == nil){ + respond(r, "opendirfile failed"); + return; + } + } + if(srv->open) + srv->open(r); + else + respond(r, nil); +} +static void +ropen(Req *r, char *error) +{ + char errbuf[ERRMAX]; + if(error) + return; + if(chatty9p){ + snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode); + write(2, errbuf, strlen(errbuf)); + } + r->fid->omode = r->ifcall.mode; + r->fid->qid = r->ofcall.qid; + if(r->ofcall.qid.type&QTDIR) + r->fid->diroffset = 0; +} + +static void +screate(Srv *srv, Req *r) +{ + if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil) + respond(r, Eunknownfid); + else if(r->fid->omode != -1) + respond(r, Ebotch); + else if(!(r->fid->qid.type&QTDIR)) + respond(r, Ecreatenondir); + else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE)) + respond(r, Eperm); + else if(srv->create) + srv->create(r); + else + respond(r, Enocreate); +} +static void +rcreate(Req *r, char *error) +{ + if(error) + return; + r->fid->omode = r->ifcall.mode; + r->fid->qid = r->ofcall.qid; +} + +static void +sread(Srv *srv, Req *r) +{ + int o; + + if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ + respond(r, Eunknownfid); + return; + } + if(r->ifcall.count < 0){ + respond(r, Ebotch); + return; + } + if(r->ifcall.offset < 0 + || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){ + respond(r, Ebadoffset); + return; + } + + if(r->ifcall.count > srv->msize - IOHDRSZ) + r->ifcall.count = srv->msize - IOHDRSZ; + r->rbuf = emalloc9p(r->ifcall.count); + r->ofcall.data = r->rbuf; + o = r->fid->omode & 3; + if(o != OREAD && o != ORDWR && o != OEXEC){ + respond(r, Ebotch); + return; + } + if((r->fid->qid.type&QTDIR) && r->fid->file){ + r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count); + respond(r, nil); + return; + } + if(srv->read) + srv->read(r); + else + respond(r, "no srv->read"); +} +static void +rread(Req *r, char *error) +{ + if(error==nil && (r->fid->qid.type&QTDIR)) + r->fid->diroffset += r->ofcall.count; +} + +static void +swrite(Srv *srv, Req *r) +{ + int o; + char e[ERRMAX]; + + if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ + respond(r, Eunknownfid); + return; + } + if(r->ifcall.count < 0){ + respond(r, Ebotch); + return; + } + if(r->ifcall.offset < 0){ + respond(r, Ebotch); + return; + } + if(r->ifcall.count > srv->msize - IOHDRSZ) + r->ifcall.count = srv->msize - IOHDRSZ; + o = r->fid->omode & 3; + if(o != OWRITE && o != ORDWR){ + snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode); + respond(r, e); + return; + } + if(srv->write) + srv->write(r); + else + respond(r, "no srv->write"); +} +static void +rwrite(Req *r, char *error) +{ + if(error) + return; + if(r->fid->file) + r->fid->file->dir.qid.vers++; +} + +static void +sclunk(Srv *srv, Req *r) +{ + if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil) + respond(r, Eunknownfid); + else + respond(r, nil); +} +static void +rclunk(Req *r, char *msg) +{ + USED(r); + USED(msg); +} + +static void +sremove(Srv *srv, Req *r) +{ + if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){ + respond(r, Eunknownfid); + return; + } + /* BUG RACE */ + if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){ + respond(r, Eperm); + return; + } + if(srv->remove) + srv->remove(r); + else + respond(r, r->fid->file ? nil : Enoremove); +} +static void +rremove(Req *r, char *error, char *errbuf) +{ + if(error) + return; + if(r->fid->file){ + if(removefile(r->fid->file) < 0){ + snprint(errbuf, ERRMAX, "remove %s: %r", + r->fid->file->dir.name); + r->error = errbuf; + } + r->fid->file = nil; + } +} + +static void +sstat(Srv *srv, Req *r) +{ + if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ + respond(r, Eunknownfid); + return; + } + if(r->fid->file){ + r->d = r->fid->file->dir; + if(r->d.name) + r->d.name = estrdup9p(r->d.name); + if(r->d.uid) + r->d.uid = estrdup9p(r->d.uid); + if(r->d.gid) + r->d.gid = estrdup9p(r->d.gid); + if(r->d.muid) + r->d.muid = estrdup9p(r->d.muid); + } + if(srv->stat) + srv->stat(r); + else if(r->fid->file) + respond(r, nil); + else + respond(r, Enostat); +} +static void +rstat(Req *r, char *error) +{ + int n; + uchar *statbuf; + uchar tmp[BIT16SZ]; + + if(error) + return; + if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){ + r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ"; + return; + } + n = GBIT16(tmp)+BIT16SZ; + statbuf = emalloc9p(n); + if(statbuf == nil){ + r->error = "out of memory"; + return; + } + r->ofcall.nstat = convD2M(&r->d, statbuf, n); + r->ofcall.stat = statbuf; /* freed in closereq */ + if(r->ofcall.nstat <= BIT16SZ){ + r->error = "convD2M fails"; + free(statbuf); + return; + } +} + +static void +swstat(Srv *srv, Req *r) +{ + if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){ + respond(r, Eunknownfid); + return; + } + if(srv->wstat == nil){ + respond(r, Enowstat); + return; + } + if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){ + respond(r, Ebaddir); + return; + } + if((ushort)~r->d.type){ + respond(r, "wstat -- attempt to change type"); + return; + } + if((uint)~r->d.dev){ + respond(r, "wstat -- attempt to change dev"); + return; + } + if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){ + respond(r, "wstat -- attempt to change qid"); + return; + } + if(r->d.muid && r->d.muid[0]){ + 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; + } + srv->wstat(r); +} +static void +rwstat(Req *r, char *msg) +{ + USED(r); + USED(msg); +} + +void +srv(Srv *srv) +{ + Req *r; + + fmtinstall('D', dirfmt); + fmtinstall('F', fcallfmt); + + if(srv->fpool == nil) + srv->fpool = allocfidpool(srv->destroyfid); + if(srv->rpool == nil) + srv->rpool = allocreqpool(srv->destroyreq); + if(srv->msize == 0) + srv->msize = 8192+IOHDRSZ; + + changemsize(srv, srv->msize); + + srv->fpool->srv = srv; + srv->rpool->srv = srv; + + while(r = getreq(srv)){ + if(r->error){ + respond(r, r->error); + continue; + } + switch(r->ifcall.type){ + default: + respond(r, "unknown message"); + break; + case Tversion: sversion(srv, r); break; + case Tauth: sauth(srv, r); break; + case Tattach: sattach(srv, r); break; + case Tflush: sflush(srv, r); break; + case Twalk: swalk(srv, r); break; + case Topen: sopen(srv, r); break; + case Tcreate: screate(srv, r); break; + case Tread: sread(srv, r); break; + case Twrite: swrite(srv, r); break; + case Tclunk: sclunk(srv, r); break; + case Tremove: sremove(srv, r); break; + case Tstat: sstat(srv, r); break; + case Twstat: swstat(srv, r); break; + } + } + + if(srv->end) + srv->end(srv); +} + +void +respond(Req *r, char *error) +{ + int i, m, n; + char errbuf[ERRMAX]; + Srv *srv; + + srv = r->srv; + assert(srv != nil); + + assert(r->responded == 0); + r->error = error; + + switch(r->ifcall.type){ + default: + assert(0); + /* + * Flush is special. If the handler says so, we return + * without further processing. Respond will be called + * again once it is safe. + */ + case Tflush: + if(rflush(r, error)<0) + return; + break; + case Tversion: rversion(r, error); break; + case Tauth: rauth(r, error); break; + case Tattach: rattach(r, error); break; + case Twalk: rwalk(r, error); break; + case Topen: ropen(r, error); break; + case Tcreate: rcreate(r, error); break; + case Tread: rread(r, error); break; + case Twrite: rwrite(r, error); break; + case Tclunk: rclunk(r, error); break; + case Tremove: rremove(r, error, errbuf); break; + case Tstat: rstat(r, error); break; + case Twstat: rwstat(r, error); break; + } + + r->ofcall.tag = r->ifcall.tag; + r->ofcall.type = r->ifcall.type+1; + if(r->error) + setfcallerror(&r->ofcall, r->error); + +if(chatty9p) + fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall); + + qlock(&srv->wlock); + n = convS2M(&r->ofcall, srv->wbuf, srv->msize); + if(n <= 0){ + fprint(2, "n = %d %F\n", n, &r->ofcall); + abort(); + } + assert(n > 2); + if(r->pool) /* not a fake */ + closereq(removereq(r->pool, r->ifcall.tag)); + m = write(srv->outfd, srv->wbuf, n); + if(m != n) + sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd); + qunlock(&srv->wlock); + + qlock(&r->lk); /* no one will add flushes now */ + r->responded = 1; + qunlock(&r->lk); + + for(i=0; inflush; i++) + respond(r->flush[i], nil); + free(r->flush); + + if(r->pool) + closereq(r); + else + free(r); +} + +int +postfd(char *name, int pfd) +{ + int fd; + char buf[80]; + + snprint(buf, sizeof buf, "/srv/%s", name); + if(chatty9p) + fprint(2, "postfd %s\n", buf); + fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600); + if(fd < 0){ + if(chatty9p) + fprint(2, "create fails: %r\n"); + return -1; + } + if(fprint(fd, "%d", pfd) < 0){ + if(chatty9p) + fprint(2, "write fails: %r\n"); + close(fd); + return -1; + } + if(chatty9p) + fprint(2, "postfd successful\n"); + return 0; +} + blob - /dev/null blob + 68da19a74bc025289f251a87a8b91d086925562f (mode 644) --- /dev/null +++ src/lib9p/tpost.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include <9p.h> +#include "post.h" + +void +threadpostmountsrv(Srv *s, char *name, char *mtpt, int flag) +{ + Postcrud *p; + + p = _post1(s, name, mtpt, flag); + if(procrfork(_post2, s, 32*1024, RFNAMEG|RFNOTEG) < 0) + sysfatal("procrfork: %r"); + _post3(p); +} + blob - /dev/null blob + 84b98bde13dd96efc542b481eafe3b6309b6b6c8 (mode 644) --- /dev/null +++ src/lib9p/uid.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +/* + * simplistic permission checking. assume that + * each user is the leader of her own group. + */ +int +hasperm(File *f, char *uid, int p) +{ + int m; + + m = f->dir.mode & 7; /* other */ + if((p & m) == p) + return 1; + + if(strcmp(f->dir.uid, uid) == 0) { + m |= (f->dir.mode>>6) & 7; + if((p & m) == p) + return 1; + } + + if(strcmp(f->dir.gid, uid) == 0) { + m |= (f->dir.mode>>3) & 7; + if((p & m) == p) + return 1; + } + + return 0; +} blob - /dev/null blob + 17588a95e8c6ce231d86b7adf10180eff145da64 (mode 644) --- /dev/null +++ src/lib9p/util.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include +#include "9p.h" + +void +readbuf(Req *r, void *s, long n) +{ + r->ofcall.count = r->ifcall.count; + if(r->ifcall.offset >= n){ + r->ofcall.count = 0; + return; + } + if(r->ifcall.offset+r->ofcall.count > n) + r->ofcall.count = n - r->ifcall.offset; + memmove(r->ofcall.data, (char*)s+r->ifcall.offset, r->ofcall.count); +} + +void +readstr(Req *r, char *s) +{ + readbuf(r, s, strlen(s)); +} blob - d912391f8a0ac0ab12a6858aea06977071f5b3d4 blob + 3151525080c0ef7feaeec1cbfd41428e48a318bb --- src/libfs/fs.c +++ src/libfs/fs.c @@ -259,7 +259,7 @@ _fssend(Mux *mux, void *pkt) Fsys *fs; fs = mux->aux; - return write(fs->fd, pkt, GBIT32((uchar*)pkt)); + return threadwrite(fs->fd, pkt, GBIT32((uchar*)pkt)); } static void* blob - c05d40d106e4c8b22f470575957efd5f821fab59 blob + f868e1237c37ab1ae7eb2ec3c45e7a88fcd00d4e --- src/libfs/read.c +++ src/libfs/read.c @@ -52,3 +52,21 @@ fsread(Fid *fid, void *buf, long n) { return fspread(fid, buf, n, -1); } + +long +fsreadn(Fid *fid, void *buf, long n) +{ + long tot, nn; + + for(tot=0; tothin; +fprint(2, "hgethead top %p - %p\n", hin->pos, hin->stop); for(;;){ s = (char*)hin->pos; pp = s; +fprint(2, "hgethead %p - %p\n", pp, hin->stop); while(p = memchr(pp, '\n', (char*)hin->stop - pp)){ - if(!many || p == pp || p == pp + 1 && *pp == '\r'){ +fprint(2, "hgethead %p - %p newline at %p %d\n", pp, hin->stop, p, *pp); + if(!many || p == pp || (p == pp + 1 && *pp == '\r')){ +fprint(2, "breaking\n"); pp = p + 1; break; } @@ -32,6 +36,7 @@ hgethead(HConnect *c, int many) memmove(c->hstop, s, n); c->hstop += n; *c->hstop = '\0'; +fprint(2, "p %p\n", p); if(p != nil) return 1; if(hreadbuf(hin, hin->pos) == nil || hin->state == Hend) blob - 7cfb555721bd2701c4224a02238d8482492422e5 blob + 34d3a3a2c5f5c957d6b75e7dfc74c9459da34f20 --- src/libhttpd/hio.c +++ src/libhttpd/hio.c @@ -157,10 +157,15 @@ hreadbuf(Hio *h, void *vsave) memmove(h->start + cpy, hh->pos, in); hh->pos += in; } - }else if(in && (in = read(h->fd, h->start + cpy, in)) < 0){ - h->state = Herr; - h->pos = h->stop; - return nil; + }else if(in){ +fprint(2, "read %d from %d\n", in, h->fd); + if((in = read(h->fd, h->start + cpy, in)) < 0){ +fprint(2, "got error: %r\n"); + h->state = Herr; + h->pos = h->stop; + return nil; + } +fprint(2, "got %d\n", in); } if(in == 0) h->state = Hend; blob - b8330d85220057b5fd0e693faaee698a5bffbed4 blob + b08999a621dfa081728a0615c13e48f291e2bbdd --- src/libip/mkfile +++ src/libip/mkfile @@ -3,16 +3,17 @@ PLAN9=../.. LIB=libip.a OFILES=\ + bo.$O\ + classmask.$O\ eipfmt.$O\ - parseip.$O\ - parseether.$O\ + ipaux.$O\ myetheraddr.$O\ myipaddr.$O\ - classmask.$O\ - bo.$O\ - readipifc.$O\ - ipaux.$O\ + parseether.$O\ + parseip.$O\ ptclbsum.$O\ + readipifc.$O\ + udp.$O\ HFILES=\ ip.h blob - fd95810b3e06ebdb605a9201b7fd67bae91e966d blob + c4094fa485c45e39500cc4a3baabf197000bf5f9 --- src/libplumb/mesg.c +++ src/libplumb/mesg.c @@ -13,8 +13,6 @@ static Fid *pfid; int plumbopen(char *name, int omode) { - int fd; - if(fsplumb == nil) fsplumb = nsmount("plumb", ""); if(fsplumb == nil) @@ -47,26 +45,42 @@ plumbopen(char *name, int omode) return pfd; } - fd = fsopenfd(fsplumb, name, omode); - return fd; + return fsopenfd(fsplumb, name, omode); } +Fid* +plumbopenfid(char *name, int mode) +{ + if(fsplumb == nil) + fsplumb = nsmount("plumb", ""); + if(fsplumb == nil) + return nil; + return fsopen(fsplumb, name, mode); +} + int -plumbsend(int fd, Plumbmsg *m) +plumbsendtofid(Fid *fid, Plumbmsg *m) { char *buf; int n; - if(fd != pfd){ - werrstr("fd is not the plumber"); - return -1; - } buf = plumbpack(m, &n); if(buf == nil) return -1; - n = fswrite(pfid, buf, n); + n = fswrite(fid, buf, n); +fprint(2, "fswrite %d\n", n); free(buf); return n; +} + +int +plumbsend(int fd, Plumbmsg *m) +{ + if(fd != pfd){ + werrstr("fd is not the plumber"); + return -1; + } + return plumbsendtofid(pfid, m); } static int @@ -421,9 +435,37 @@ plumbrecv(int fd) if(buf == nil) return nil; if(readn(fd, buf+n, more) == more) + m = plumbunpackpartial(buf, n+more, nil); + } + } + free(buf); + return m; +} + +Plumbmsg* +plumbrecvfid(Fid *fid) +{ + char *buf; + Plumbmsg *m; + int n, more; + + buf = malloc(8192); + if(buf == nil) + return nil; + n = fsread(fid, buf, 8192); + m = nil; + if(n > 0){ + m = plumbunpackpartial(buf, n, &more); + if(m==nil && more>0){ + /* we now know how many more bytes to read for complete message */ + buf = realloc(buf, n+more); + if(buf == nil) + return nil; + if(fsreadn(fid, buf+n, more) == more) m = plumbunpackpartial(buf, n+more, nil); } } free(buf); return m; } + blob - c31fdfb1972c7a6e6a928c1791194ed29f5e1e9e blob + 5b3752570b78f1344f4a72f9d21c618dc803c060 --- src/libplumb/thread.c +++ src/libplumb/thread.c @@ -31,3 +31,4 @@ threadplumbrecv(int fd) free(buf); return m; } + blob - fce96fd3ef3bd4bd9022824edbc10a56e001dfce blob + dda9c8a52a95e96a1eee6824a84b4cb188b6cc91 --- src/libsec/port/aes.c +++ src/libsec/port/aes.c @@ -43,7 +43,7 @@ static const u32 Td3[256]; static const u8 Te4[256]; static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits); -// static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits); +static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits); static int rijndaelKeySetup(u32 erk[/*4*(Nr + 1)*/], u32 drk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits); static void rijndaelEncrypt(const u32int rk[], int Nr, const uchar pt[16], uchar ct[16]); static void rijndaelDecrypt(const u32int rk[], int Nr, const uchar ct[16], uchar pt[16]); @@ -950,7 +950,6 @@ static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], return 0; } -#if 0 /** * Expand the cipher key into the decryption key schedule. * @@ -995,7 +994,6 @@ static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], } return Nr; } -#endif static void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; blob - 294111e26a5b7f1f70c9dff4b943a1d9dc163555 blob + 2cbaeb8c4ea0138597b4dd6f66c0a72882b6b13f --- src/libsec/port/genrandom.c +++ src/libsec/port/genrandom.c @@ -1,4 +1,5 @@ #include "os.h" +#include #include typedef struct State{ blob - 97aa29528b35bbffb49f439677ecd3b23235a793 blob + eb0a7e028c66fdfc1b36f96c96012aa247994f62 --- src/libsec/port/mkfile +++ src/libsec/port/mkfile @@ -5,14 +5,55 @@ LIB=libsec.a OFILES=\ aes.$O\ + blowfish.$O\ + decodepem.$O\ des.$O\ + des3CBC.$O\ + des3ECB.$O\ + desCBC.$O\ + desECB.$O\ desmodes.$O\ + dsaalloc.$O\ + dsagen.$O\ + dsaprimes.$O\ + dsaprivtopub.$O\ + dsasign.$O\ + dsaverify.$O\ + egalloc.$O\ + egdecrypt.$O\ + egencrypt.$O\ + eggen.$O\ + egprivtopub.$O\ + egsign.$O\ + egverify.$O\ fastrand.$O\ + genprime.$O\ genrandom.$O\ + gensafeprime.$O\ + genstrongprime.$O\ + hmac.$O\ + md4.$O\ md5.$O\ md5block.$O\ + md5pickle.$O\ + nfastrand.$O\ + prng.$O\ + probably_prime.$O\ + rc4.$O\ + readcert.$O\ + rsaalloc.$O\ + rsadecrypt.$O\ + rsaencrypt.$O\ + rsafill.$O\ + rsagen.$O\ + rsaprivtopub.$O\ sha1.$O\ sha1block.$O\ + sha1pickle.$O\ + smallprimetest.$O\ + thumb.$O\ + tlshand.$O\ + x509.$O\ HFILES=$PLAN9/include/libsec.h blob - dabe75f0dc0c543c74f4050ee8af55d6dbba8a98 blob + 946f028389efc1793d4718a24aa9ebc418e23f7a --- src/libsec/port/sha1.c +++ src/libsec/port/sha1.c @@ -1,5 +1,4 @@ -#include -#include +#include "os.h" #include static void encode(uchar*, u32int*, ulong); @@ -11,8 +10,6 @@ extern void _sha1block(uchar*, ulong, u32int*); * the last call. There must be room in the input buffer * to pad. */ -ulong lastlen; - SHA1state* sha1(uchar *p, ulong len, uchar *digest, SHA1state *s) { @@ -21,15 +18,12 @@ sha1(uchar *p, ulong len, uchar *digest, SHA1state *s) int i; uchar *e; -lastlen = len; if(s == nil){ s = malloc(sizeof(*s)); if(s == nil) return nil; memset(s, 0, sizeof(*s)); s->malloced = 1; - assert(!s->seeded); - assert(!s->blen); } if(s->seeded == 0){ @@ -42,11 +36,8 @@ lastlen = len; s->seeded = 1; } -assert(len < 100000); - /* fill out the partial 64 byte block from previous calls */ if(s->blen){ -assert(s); i = 64 - s->blen; if(len < i) i = len; @@ -61,11 +52,9 @@ assert(s); } } -assert(len < 1000000); /* do 64 byte blocks */ i = len & ~0x3f; if(i){ -assert(i < 1000000); _sha1block(p, i, s->state); s->len += i; len -= i; blob - 0ae920e365350352d1bf30a5c1cd2de4ba552d37 blob + 82566f21029c551fb9cd42bb09c2c86b31959906 --- src/libsec/port/sha1block.c +++ src/libsec/port/sha1block.c @@ -1,11 +1,5 @@ -#include -#include +#include "os.h" -uchar* lastsbp; -ulong lastsblen; -u32int* lastsbs; -Lock slock; - void _sha1block(uchar *p, ulong len, u32int *s) { @@ -14,11 +8,6 @@ _sha1block(uchar *p, ulong len, u32int *s) u32int *wp, *wend; u32int w[80]; -lock(&slock); -lastsbp=p; -lastsblen=len; -lastsbs=s; - /* at this point, we have a multiple of 64 bytes */ for(end = p+len; p < end;){ a = s[0]; @@ -195,5 +184,4 @@ lastsbs=s; s[3] += d; s[4] += e; } -unlock(&slock); } blob - cbecad53d741034bec9c1f7d1058ea300623e9f9 blob + bcab77c76afa833e411dd0bd509bc9d45f3ec90e --- src/libthread/main.c +++ src/libthread/main.c @@ -45,6 +45,7 @@ main(int argc, char **argv) signal(SIGTERM, _threaddie); signal(SIGCHLD, _nop); + signal(SIGALRM, _nop); // signal(SIGINFO, _threadstatus); // rfork(RFREND); blob - 60d253df82fa80b2598ce31affc61a475b579b08 blob + 7ecaf7c355f0a030f4bc5211bff3e59a38f8c00d --- src/libventi/server.c +++ src/libventi/server.c @@ -55,11 +55,13 @@ listenproc(void *v) srv = v; for(;;){ +fprint(2, "listen for venti\n"); ctl = listen(srv->adir, dir); if(ctl < 0){ srv->dead = 1; break; } +fprint(2, "got one\n"); sc = vtmallocz(sizeof(VtSconn)); sc->ctl = ctl; sc->srv = srv; blob - 0465041dcfa1f10e9f0c38859bcdb6027b7e772a blob + 45683608e52ebb5bcf763e113846686d4af82af0 --- src/mkfile +++ src/mkfile @@ -1,6 +1,6 @@