xref: /plan9/sys/src/cmd/auth/keyfs.c (revision f54edc786b9c49b2c7ab1c0695cdc8c698b11f4d)
12bef681aSDavid du Colombier /*
22bef681aSDavid du Colombier  * keyfs
32bef681aSDavid du Colombier  */
4219b2ee8SDavid du Colombier #include <u.h>
5219b2ee8SDavid du Colombier #include <libc.h>
62bef681aSDavid du Colombier #include <ctype.h>
79a747e4fSDavid du Colombier #include <authsrv.h>
8219b2ee8SDavid du Colombier #include <fcall.h>
9219b2ee8SDavid du Colombier #include <bio.h>
107dd7cddfSDavid du Colombier #include <mp.h>
117dd7cddfSDavid du Colombier #include <libsec.h>
129a747e4fSDavid du Colombier #include "authcmdlib.h"
13219b2ee8SDavid du Colombier 
142bef681aSDavid du Colombier #pragma	varargck	type	"W"	char*
152bef681aSDavid du Colombier 
167dd7cddfSDavid du Colombier char authkey[8];
17219b2ee8SDavid du Colombier 
18219b2ee8SDavid du Colombier typedef struct Fid	Fid;
19219b2ee8SDavid du Colombier typedef struct User	User;
20219b2ee8SDavid du Colombier 
21219b2ee8SDavid du Colombier enum {
22219b2ee8SDavid du Colombier 	Qroot,
23219b2ee8SDavid du Colombier 	Quser,
24219b2ee8SDavid du Colombier 	Qkey,
257dd7cddfSDavid du Colombier 	Qsecret,
26219b2ee8SDavid du Colombier 	Qlog,
27219b2ee8SDavid du Colombier 	Qstatus,
28219b2ee8SDavid du Colombier 	Qexpire,
29219b2ee8SDavid du Colombier 	Qwarnings,
30219b2ee8SDavid du Colombier 	Qmax,
31219b2ee8SDavid du Colombier 
32219b2ee8SDavid du Colombier 	Nuser	= 512,
332bef681aSDavid du Colombier 	MAXBAD	= 10,	/* max # of bad attempts before disabling the account */
342bef681aSDavid du Colombier 	/* file must be randomly addressible, so names have fixed length */
352bef681aSDavid du Colombier 	Namelen	= ANAMELEN,
36219b2ee8SDavid du Colombier };
37219b2ee8SDavid du Colombier 
38219b2ee8SDavid du Colombier enum {
39219b2ee8SDavid du Colombier 	Sok,
40219b2ee8SDavid du Colombier 	Sdisabled,
41219b2ee8SDavid du Colombier 	Smax,
42219b2ee8SDavid du Colombier };
43219b2ee8SDavid du Colombier 
44219b2ee8SDavid du Colombier struct Fid {
45219b2ee8SDavid du Colombier 	int	fid;
46219b2ee8SDavid du Colombier 	ulong	qtype;
47219b2ee8SDavid du Colombier 	User	*user;
48219b2ee8SDavid du Colombier 	int	busy;
49219b2ee8SDavid du Colombier 	Fid	*next;
50219b2ee8SDavid du Colombier };
51219b2ee8SDavid du Colombier 
52219b2ee8SDavid du Colombier struct User {
539a747e4fSDavid du Colombier 	char	*name;
54219b2ee8SDavid du Colombier 	char	key[DESKEYLEN];
557dd7cddfSDavid du Colombier 	char	secret[SECRETLEN];
56219b2ee8SDavid du Colombier 	ulong	expire;			/* 0 == never */
57219b2ee8SDavid du Colombier 	uchar	status;
582bef681aSDavid du Colombier 	ulong	bad;		/* # of consecutive bad authentication attempts */
59219b2ee8SDavid du Colombier 	int	ref;
60219b2ee8SDavid du Colombier 	char	removed;
61219b2ee8SDavid du Colombier 	uchar	warnings;
620dac3555SDavid du Colombier 	long	purgatory;		/* time purgatory ends */
63219b2ee8SDavid du Colombier 	ulong	uniq;
64219b2ee8SDavid du Colombier 	User	*link;
65219b2ee8SDavid du Colombier };
66219b2ee8SDavid du Colombier 
67219b2ee8SDavid du Colombier char	*qinfo[Qmax] = {
68219b2ee8SDavid du Colombier 	[Qroot]		"keys",
69219b2ee8SDavid du Colombier 	[Quser]		".",
70219b2ee8SDavid du Colombier 	[Qkey]		"key",
717dd7cddfSDavid du Colombier 	[Qsecret]	"secret",
72219b2ee8SDavid du Colombier 	[Qlog]		"log",
73219b2ee8SDavid du Colombier 	[Qexpire]	"expire",
74219b2ee8SDavid du Colombier 	[Qstatus]	"status",
75219b2ee8SDavid du Colombier 	[Qwarnings]	"warnings",
76219b2ee8SDavid du Colombier };
77219b2ee8SDavid du Colombier 
78219b2ee8SDavid du Colombier char	*status[Smax] = {
79219b2ee8SDavid du Colombier 	[Sok]		"ok",
80219b2ee8SDavid du Colombier 	[Sdisabled]	"disabled",
81219b2ee8SDavid du Colombier };
82219b2ee8SDavid du Colombier 
83219b2ee8SDavid du Colombier Fid	*fids;
84219b2ee8SDavid du Colombier User	*users[Nuser];
85219b2ee8SDavid du Colombier char	*userkeys;
86219b2ee8SDavid du Colombier int	nuser;
87219b2ee8SDavid du Colombier ulong	uniq = 1;
88219b2ee8SDavid du Colombier Fcall	rhdr,
89219b2ee8SDavid du Colombier 	thdr;
90219b2ee8SDavid du Colombier int	usepass;
91219b2ee8SDavid du Colombier char	*warnarg;
929a747e4fSDavid du Colombier uchar	mdata[8192 + IOHDRSZ];
939a747e4fSDavid du Colombier int	messagesize = sizeof mdata;
94219b2ee8SDavid du Colombier 
95219b2ee8SDavid du Colombier int	readusers(void);
96219b2ee8SDavid du Colombier ulong	hash(char*);
97219b2ee8SDavid du Colombier Fid	*findfid(int);
98219b2ee8SDavid du Colombier User	*finduser(char*);
99219b2ee8SDavid du Colombier User	*installuser(char*);
100219b2ee8SDavid du Colombier int	removeuser(User*);
101219b2ee8SDavid du Colombier void	insertuser(User*);
102219b2ee8SDavid du Colombier void	writeusers(void);
103219b2ee8SDavid du Colombier void	io(int, int);
104219b2ee8SDavid du Colombier void	*emalloc(ulong);
105219b2ee8SDavid du Colombier Qid	mkqid(User*, ulong);
1069a747e4fSDavid du Colombier int	dostat(User*, ulong, void*, int);
107219b2ee8SDavid du Colombier int	newkeys(void);
108219b2ee8SDavid du Colombier void	warning(void);
1095864cca7SDavid du Colombier int	weirdfmt(Fmt *f);
110219b2ee8SDavid du Colombier 
1119a747e4fSDavid du Colombier char	*Auth(Fid*), *Attach(Fid*), *Version(Fid*),
1129a747e4fSDavid du Colombier 	*Flush(Fid*), *Walk(Fid*),
1139a747e4fSDavid du Colombier 	*Open(Fid*), *Create(Fid*),
114219b2ee8SDavid du Colombier 	*Read(Fid *), *Write(Fid*), *Clunk(Fid*),
115219b2ee8SDavid du Colombier 	*Remove(Fid *), *Stat(Fid*), *Wstat(Fid*);
116219b2ee8SDavid du Colombier char 	*(*fcalls[])(Fid*) = {
117219b2ee8SDavid du Colombier 	[Tattach]	Attach,
1189a747e4fSDavid du Colombier 	[Tauth]	Auth,
119219b2ee8SDavid du Colombier 	[Tclunk]	Clunk,
120219b2ee8SDavid du Colombier 	[Tcreate]	Create,
121219b2ee8SDavid du Colombier 	[Tflush]	Flush,
122219b2ee8SDavid du Colombier 	[Topen]		Open,
123219b2ee8SDavid du Colombier 	[Tread]		Read,
124219b2ee8SDavid du Colombier 	[Tremove]	Remove,
125219b2ee8SDavid du Colombier 	[Tstat]		Stat,
1269a747e4fSDavid du Colombier 	[Tversion]	Version,
127219b2ee8SDavid du Colombier 	[Twalk]		Walk,
128219b2ee8SDavid du Colombier 	[Twrite]	Write,
129219b2ee8SDavid du Colombier 	[Twstat]	Wstat,
130219b2ee8SDavid du Colombier };
131219b2ee8SDavid du Colombier 
1322bef681aSDavid du Colombier static void
usage(void)1332bef681aSDavid du Colombier usage(void)
1342bef681aSDavid du Colombier {
1352bef681aSDavid du Colombier 	fprint(2, "usage: %s [-p] [-m mtpt] [-w warn] [keyfile]\n", argv0);
1362bef681aSDavid du Colombier 	exits("usage");
1372bef681aSDavid du Colombier }
1382bef681aSDavid du Colombier 
139219b2ee8SDavid du Colombier void
main(int argc,char * argv[])140219b2ee8SDavid du Colombier main(int argc, char *argv[])
141219b2ee8SDavid du Colombier {
142219b2ee8SDavid du Colombier 	char *mntpt;
143219b2ee8SDavid du Colombier 	int p[2];
144219b2ee8SDavid du Colombier 
1455864cca7SDavid du Colombier 	fmtinstall('W', weirdfmt);
146219b2ee8SDavid du Colombier 	mntpt = "/mnt/keys";
147219b2ee8SDavid du Colombier 	ARGBEGIN{
148219b2ee8SDavid du Colombier 	case 'm':
1492bef681aSDavid du Colombier 		mntpt = EARGF(usage());
150219b2ee8SDavid du Colombier 		break;
151219b2ee8SDavid du Colombier 	case 'p':
152219b2ee8SDavid du Colombier 		usepass = 1;
153219b2ee8SDavid du Colombier 		break;
1542bef681aSDavid du Colombier 	case 'w':
1552bef681aSDavid du Colombier 		warnarg = EARGF(usage());
1562bef681aSDavid du Colombier 		break;
1572bef681aSDavid du Colombier 	default:
1582bef681aSDavid du Colombier 		usage();
1592bef681aSDavid du Colombier 		break;
160219b2ee8SDavid du Colombier 	}ARGEND
161219b2ee8SDavid du Colombier 	argv0 = "keyfs";
162219b2ee8SDavid du Colombier 
163219b2ee8SDavid du Colombier 	userkeys = "/adm/keys";
1642bef681aSDavid du Colombier 	if(argc > 1)
1652bef681aSDavid du Colombier 		usage();
1662bef681aSDavid du Colombier 	if(argc == 1)
167219b2ee8SDavid du Colombier 		userkeys = argv[0];
168219b2ee8SDavid du Colombier 
169219b2ee8SDavid du Colombier 	if(pipe(p) < 0)
170219b2ee8SDavid du Colombier 		error("can't make pipe: %r");
171219b2ee8SDavid du Colombier 
1726b8bc682SDavid du Colombier 	if(usepass) {
1736b8bc682SDavid du Colombier 		getpass(authkey, nil, 0, 0);
1746b8bc682SDavid du Colombier 	} else {
1756b8bc682SDavid du Colombier 		if(!getauthkey(authkey))
1762bef681aSDavid du Colombier 			print("keyfs: warning: can't read NVRAM\n");
1776b8bc682SDavid du Colombier 	}
1786b8bc682SDavid du Colombier 
1797dd7cddfSDavid du Colombier 	switch(rfork(RFPROC|RFNAMEG|RFNOTEG|RFNOWAIT|RFENVG|RFFDG)){
180219b2ee8SDavid du Colombier 	case 0:
1817dd7cddfSDavid du Colombier 		close(p[0]);
182219b2ee8SDavid du Colombier 		io(p[1], p[1]);
183219b2ee8SDavid du Colombier 		exits(0);
184219b2ee8SDavid du Colombier 	case -1:
185219b2ee8SDavid du Colombier 		error("fork");
186219b2ee8SDavid du Colombier 	default:
1877dd7cddfSDavid du Colombier 		close(p[1]);
1889a747e4fSDavid du Colombier 		if(mount(p[0], -1, mntpt, MREPL|MCREATE, "") < 0)
189219b2ee8SDavid du Colombier 			error("can't mount: %r");
190219b2ee8SDavid du Colombier 		exits(0);
191219b2ee8SDavid du Colombier 	}
192219b2ee8SDavid du Colombier }
193219b2ee8SDavid du Colombier 
194219b2ee8SDavid du Colombier char *
Flush(Fid * f)195219b2ee8SDavid du Colombier Flush(Fid *f)
196219b2ee8SDavid du Colombier {
197219b2ee8SDavid du Colombier 	USED(f);
198219b2ee8SDavid du Colombier 	return 0;
199219b2ee8SDavid du Colombier }
200219b2ee8SDavid du Colombier 
201219b2ee8SDavid du Colombier char *
Auth(Fid *)2029a747e4fSDavid du Colombier Auth(Fid *)
203219b2ee8SDavid du Colombier {
2043ff48bf5SDavid du Colombier 	return "keyfs: authentication not required";
205219b2ee8SDavid du Colombier }
206219b2ee8SDavid du Colombier 
207219b2ee8SDavid du Colombier char *
Attach(Fid * f)208219b2ee8SDavid du Colombier Attach(Fid *f)
209219b2ee8SDavid du Colombier {
210219b2ee8SDavid du Colombier 	if(f->busy)
211219b2ee8SDavid du Colombier 		Clunk(f);
212219b2ee8SDavid du Colombier 	f->user = 0;
213219b2ee8SDavid du Colombier 	f->qtype = Qroot;
214219b2ee8SDavid du Colombier 	f->busy = 1;
215219b2ee8SDavid du Colombier 	thdr.qid = mkqid(f->user, f->qtype);
216219b2ee8SDavid du Colombier 	return 0;
217219b2ee8SDavid du Colombier }
218219b2ee8SDavid du Colombier 
219219b2ee8SDavid du Colombier char*
Version(Fid *)2209a747e4fSDavid du Colombier Version(Fid*)
221219b2ee8SDavid du Colombier {
2229a747e4fSDavid du Colombier 	Fid *f;
223219b2ee8SDavid du Colombier 
2249a747e4fSDavid du Colombier 	for(f = fids; f; f = f->next)
2259a747e4fSDavid du Colombier 		if(f->busy)
2269a747e4fSDavid du Colombier 			Clunk(f);
2279a747e4fSDavid du Colombier 	if(rhdr.msize > sizeof mdata)
2289a747e4fSDavid du Colombier 		thdr.msize = sizeof mdata;
2299a747e4fSDavid du Colombier 	else
2309a747e4fSDavid du Colombier 		thdr.msize = rhdr.msize;
2319a747e4fSDavid du Colombier 	messagesize = thdr.msize;
2329a747e4fSDavid du Colombier 	if(strncmp(rhdr.version, "9P2000", 6) != 0)
2339a747e4fSDavid du Colombier 		return "bad 9P version";
2349a747e4fSDavid du Colombier 	thdr.version = "9P2000";
235219b2ee8SDavid du Colombier 	return 0;
236219b2ee8SDavid du Colombier }
237219b2ee8SDavid du Colombier 
238219b2ee8SDavid du Colombier char *
Walk(Fid * f)239219b2ee8SDavid du Colombier Walk(Fid *f)
240219b2ee8SDavid du Colombier {
2419a747e4fSDavid du Colombier 	char *name, *err;
2429a747e4fSDavid du Colombier 	int i, j, max;
2439a747e4fSDavid du Colombier 	Fid *nf;
2449a747e4fSDavid du Colombier 	ulong qtype;
2459a747e4fSDavid du Colombier 	User *user;
246219b2ee8SDavid du Colombier 
247219b2ee8SDavid du Colombier 	if(!f->busy)
2489a747e4fSDavid du Colombier 		return "walk of unused fid";
2499a747e4fSDavid du Colombier 	nf = nil;
2509a747e4fSDavid du Colombier 	qtype = f->qtype;
2519a747e4fSDavid du Colombier 	user = f->user;
2529a747e4fSDavid du Colombier 	if(rhdr.fid != rhdr.newfid){
2539a747e4fSDavid du Colombier 		nf = findfid(rhdr.newfid);
2549a747e4fSDavid du Colombier 		if(nf->busy)
2559a747e4fSDavid du Colombier 			return "fid in use";
2569a747e4fSDavid du Colombier 		f = nf;	/* walk f */
2579a747e4fSDavid du Colombier 	}
2589a747e4fSDavid du Colombier 
2599a747e4fSDavid du Colombier 	err = nil;
2609a747e4fSDavid du Colombier 	i = 0;
2619a747e4fSDavid du Colombier 	if(rhdr.nwname > 0){
2629a747e4fSDavid du Colombier 		for(; i<rhdr.nwname; i++){
2639a747e4fSDavid du Colombier 			if(i >= MAXWELEM){
2649a747e4fSDavid du Colombier 				err = "too many path name elements";
265219b2ee8SDavid du Colombier 				break;
2669a747e4fSDavid du Colombier 			}
2679a747e4fSDavid du Colombier 			name = rhdr.wname[i];
2689a747e4fSDavid du Colombier 			switch(qtype){
2699a747e4fSDavid du Colombier 			case Qroot:
2709a747e4fSDavid du Colombier 				if(strcmp(name, "..") == 0)
2719a747e4fSDavid du Colombier 					goto Accept;
2729a747e4fSDavid du Colombier 				user = finduser(name);
2739a747e4fSDavid du Colombier 				if(!user)
2749a747e4fSDavid du Colombier 					goto Out;
2759a747e4fSDavid du Colombier 				qtype = Quser;
2769a747e4fSDavid du Colombier 
2779a747e4fSDavid du Colombier 			Accept:
2789a747e4fSDavid du Colombier 				thdr.wqid[i] = mkqid(user, qtype);
2799a747e4fSDavid du Colombier 				break;
2809a747e4fSDavid du Colombier 
281219b2ee8SDavid du Colombier 			case Quser:
2826b8bc682SDavid du Colombier 				if(strcmp(name, "..") == 0) {
2836b8bc682SDavid du Colombier 					qtype = Qroot;
2846b8bc682SDavid du Colombier 					user = 0;
2856b8bc682SDavid du Colombier 					goto Accept;
2866b8bc682SDavid du Colombier 				}
287219b2ee8SDavid du Colombier 				max = Qmax;
2889a747e4fSDavid du Colombier 				for(j = Quser + 1; j < Qmax; j++)
2899a747e4fSDavid du Colombier 					if(strcmp(name, qinfo[j]) == 0){
2909a747e4fSDavid du Colombier 						qtype = j;
291219b2ee8SDavid du Colombier 						break;
292219b2ee8SDavid du Colombier 					}
2939a747e4fSDavid du Colombier 				if(j < max)
2949a747e4fSDavid du Colombier 					goto Accept;
2959a747e4fSDavid du Colombier 				goto Out;
2969a747e4fSDavid du Colombier 
297219b2ee8SDavid du Colombier 			default:
2989a747e4fSDavid du Colombier 				err = "file is not a directory";
2999a747e4fSDavid du Colombier 				goto Out;
300219b2ee8SDavid du Colombier 			}
3019a747e4fSDavid du Colombier 		}
3029a747e4fSDavid du Colombier 	    Out:
3039a747e4fSDavid du Colombier 		if(i < rhdr.nwname && err == nil)
3049a747e4fSDavid du Colombier 			err = "file not found";
3059a747e4fSDavid du Colombier 	}
3069a747e4fSDavid du Colombier 
3079a747e4fSDavid du Colombier 	if(err != nil){
3089a747e4fSDavid du Colombier 		return err;
3099a747e4fSDavid du Colombier 	}
3109a747e4fSDavid du Colombier 
3119a747e4fSDavid du Colombier 	/* if we cloned and then completed the walk, update new fid */
3129a747e4fSDavid du Colombier 	if(rhdr.fid != rhdr.newfid && i == rhdr.nwname){
3139a747e4fSDavid du Colombier 		nf->busy = 1;
3149a747e4fSDavid du Colombier 		nf->qtype = qtype;
3159a747e4fSDavid du Colombier 		if(nf->user = user)
3169a747e4fSDavid du Colombier 			nf->user->ref++;
3179a747e4fSDavid du Colombier 	}else if(nf == nil && rhdr.nwname > 0){	/* walk without clone (rare) */
3189a747e4fSDavid du Colombier 		Clunk(f);
3199a747e4fSDavid du Colombier 		f->busy = 1;
3209a747e4fSDavid du Colombier 		f->qtype = qtype;
3219a747e4fSDavid du Colombier 		if(f->user = user)
3229a747e4fSDavid du Colombier 			f->user->ref++;
3239a747e4fSDavid du Colombier 	}
3249a747e4fSDavid du Colombier 
3259a747e4fSDavid du Colombier 	thdr.nwqid = i;
326219b2ee8SDavid du Colombier 	return 0;
327219b2ee8SDavid du Colombier }
328219b2ee8SDavid du Colombier 
329219b2ee8SDavid du Colombier char *
Clunk(Fid * f)330219b2ee8SDavid du Colombier Clunk(Fid *f)
331219b2ee8SDavid du Colombier {
332219b2ee8SDavid du Colombier 	f->busy = 0;
3336b8bc682SDavid du Colombier 	if(f->user && --f->user->ref == 0 && f->user->removed) {
3346b8bc682SDavid du Colombier 		free(f->user->name);
335219b2ee8SDavid du Colombier 		free(f->user);
3366b8bc682SDavid du Colombier 	}
337219b2ee8SDavid du Colombier 	f->user = 0;
338219b2ee8SDavid du Colombier 	return 0;
339219b2ee8SDavid du Colombier }
340219b2ee8SDavid du Colombier 
341219b2ee8SDavid du Colombier char *
Open(Fid * f)342219b2ee8SDavid du Colombier Open(Fid *f)
343219b2ee8SDavid du Colombier {
344219b2ee8SDavid du Colombier 	int mode;
345219b2ee8SDavid du Colombier 
346219b2ee8SDavid du Colombier 	if(!f->busy)
3479a747e4fSDavid du Colombier 		return "open of unused fid";
348219b2ee8SDavid du Colombier 	mode = rhdr.mode;
349219b2ee8SDavid du Colombier 	if(f->qtype == Quser && (mode & (OWRITE|OTRUNC)))
350219b2ee8SDavid du Colombier 		return "user already exists";
351219b2ee8SDavid du Colombier 	thdr.qid = mkqid(f->user, f->qtype);
3529a747e4fSDavid du Colombier 	thdr.iounit = messagesize - IOHDRSZ;
353219b2ee8SDavid du Colombier 	return 0;
354219b2ee8SDavid du Colombier }
355219b2ee8SDavid du Colombier 
356219b2ee8SDavid du Colombier char *
Create(Fid * f)357219b2ee8SDavid du Colombier Create(Fid *f)
358219b2ee8SDavid du Colombier {
359219b2ee8SDavid du Colombier 	char *name;
360219b2ee8SDavid du Colombier 	long perm;
361219b2ee8SDavid du Colombier 
362219b2ee8SDavid du Colombier 	if(!f->busy)
3639a747e4fSDavid du Colombier 		return "create of unused fid";
364219b2ee8SDavid du Colombier 	name = rhdr.name;
365219b2ee8SDavid du Colombier 	if(f->user){
366219b2ee8SDavid du Colombier 		return "permission denied";
367219b2ee8SDavid du Colombier 	}else{
368219b2ee8SDavid du Colombier 		perm = rhdr.perm;
3699a747e4fSDavid du Colombier 		if(!(perm & DMDIR))
370219b2ee8SDavid du Colombier 			return "permission denied";
3719a747e4fSDavid du Colombier 		if(strcmp(name, "") == 0)
3729a747e4fSDavid du Colombier 			return "empty file name";
3739a747e4fSDavid du Colombier 		if(strlen(name) >= Namelen)
3749a747e4fSDavid du Colombier 			return "file name too long";
375219b2ee8SDavid du Colombier 		if(finduser(name))
376219b2ee8SDavid du Colombier 			return "user already exists";
377219b2ee8SDavid du Colombier 		f->user = installuser(name);
378219b2ee8SDavid du Colombier 		f->user->ref++;
379219b2ee8SDavid du Colombier 		f->qtype = Quser;
380219b2ee8SDavid du Colombier 	}
381219b2ee8SDavid du Colombier 	thdr.qid = mkqid(f->user, f->qtype);
3829a747e4fSDavid du Colombier 	thdr.iounit = messagesize - IOHDRSZ;
383219b2ee8SDavid du Colombier 	writeusers();
384219b2ee8SDavid du Colombier 	return 0;
385219b2ee8SDavid du Colombier }
386219b2ee8SDavid du Colombier 
387219b2ee8SDavid du Colombier char *
Read(Fid * f)388219b2ee8SDavid du Colombier Read(Fid *f)
389219b2ee8SDavid du Colombier {
390219b2ee8SDavid du Colombier 	User *u;
391219b2ee8SDavid du Colombier 	char *data;
3929a747e4fSDavid du Colombier 	ulong off, n, m;
393219b2ee8SDavid du Colombier 	int i, j, max;
394219b2ee8SDavid du Colombier 
395219b2ee8SDavid du Colombier 	if(!f->busy)
3969a747e4fSDavid du Colombier 		return "read of unused fid";
397219b2ee8SDavid du Colombier 	n = rhdr.count;
398219b2ee8SDavid du Colombier 	off = rhdr.offset;
399219b2ee8SDavid du Colombier 	thdr.count = 0;
400219b2ee8SDavid du Colombier 	data = thdr.data;
401219b2ee8SDavid du Colombier 	switch(f->qtype){
402219b2ee8SDavid du Colombier 	case Qroot:
403219b2ee8SDavid du Colombier 		j = 0;
404219b2ee8SDavid du Colombier 		for(i = 0; i < Nuser; i++)
4059a747e4fSDavid du Colombier 			for(u = users[i]; u; j += m, u = u->link){
4069a747e4fSDavid du Colombier 				m = dostat(u, Quser, data, n);
4079a747e4fSDavid du Colombier 				if(m <= BIT16SZ)
4089a747e4fSDavid du Colombier 					break;
409219b2ee8SDavid du Colombier 				if(j < off)
410219b2ee8SDavid du Colombier 					continue;
4119a747e4fSDavid du Colombier 				data += m;
4129a747e4fSDavid du Colombier 				n -= m;
413219b2ee8SDavid du Colombier 			}
414219b2ee8SDavid du Colombier 		thdr.count = data - thdr.data;
415219b2ee8SDavid du Colombier 		return 0;
416219b2ee8SDavid du Colombier 	case Quser:
417219b2ee8SDavid du Colombier 		max = Qmax;
418219b2ee8SDavid du Colombier 		max -= Quser + 1;
4199a747e4fSDavid du Colombier 		j = 0;
4209a747e4fSDavid du Colombier 		for(i = 0; i < max; j += m, i++){
4219a747e4fSDavid du Colombier 			m = dostat(f->user, i + Quser + 1, data, n);
4229a747e4fSDavid du Colombier 			if(m <= BIT16SZ)
423219b2ee8SDavid du Colombier 				break;
4249a747e4fSDavid du Colombier 			if(j < off)
4259a747e4fSDavid du Colombier 				continue;
4269a747e4fSDavid du Colombier 			data += m;
4279a747e4fSDavid du Colombier 			n -= m;
428219b2ee8SDavid du Colombier 		}
429219b2ee8SDavid du Colombier 		thdr.count = data - thdr.data;
430219b2ee8SDavid du Colombier 		return 0;
431219b2ee8SDavid du Colombier 	case Qkey:
432219b2ee8SDavid du Colombier 		if(f->user->status != Sok)
433219b2ee8SDavid du Colombier 			return "user disabled";
4340dac3555SDavid du Colombier 		if(f->user->purgatory > time(0))
4350dac3555SDavid du Colombier 			return "user in purgatory";
436219b2ee8SDavid du Colombier 		if(f->user->expire != 0 && f->user->expire < time(0))
437219b2ee8SDavid du Colombier 			return "user expired";
438219b2ee8SDavid du Colombier 		if(off != 0)
439219b2ee8SDavid du Colombier 			return 0;
440219b2ee8SDavid du Colombier 		if(n > DESKEYLEN)
441219b2ee8SDavid du Colombier 			n = DESKEYLEN;
442219b2ee8SDavid du Colombier 		memmove(thdr.data, f->user->key, n);
443219b2ee8SDavid du Colombier 		thdr.count = n;
444219b2ee8SDavid du Colombier 		return 0;
4457dd7cddfSDavid du Colombier 	case Qsecret:
4467dd7cddfSDavid du Colombier 		if(f->user->status != Sok)
4477dd7cddfSDavid du Colombier 			return "user disabled";
4480dac3555SDavid du Colombier 		if(f->user->purgatory > time(0))
4490dac3555SDavid du Colombier 			return "user in purgatory";
4507dd7cddfSDavid du Colombier 		if(f->user->expire != 0 && f->user->expire < time(0))
4517dd7cddfSDavid du Colombier 			return "user expired";
4527dd7cddfSDavid du Colombier 		if(off != 0)
4537dd7cddfSDavid du Colombier 			return 0;
4547dd7cddfSDavid du Colombier 		if(n > strlen(f->user->secret))
4557dd7cddfSDavid du Colombier 			n = strlen(f->user->secret);
4567dd7cddfSDavid du Colombier 		memmove(thdr.data, f->user->secret, n);
4577dd7cddfSDavid du Colombier 		thdr.count = n;
4587dd7cddfSDavid du Colombier 		return 0;
459219b2ee8SDavid du Colombier 	case Qstatus:
460219b2ee8SDavid du Colombier 		if(off != 0){
461219b2ee8SDavid du Colombier 			thdr.count = 0;
462219b2ee8SDavid du Colombier 			return 0;
463219b2ee8SDavid du Colombier 		}
464219b2ee8SDavid du Colombier 		if(f->user->status == Sok && f->user->expire && f->user->expire < time(0))
465219b2ee8SDavid du Colombier 			sprint(thdr.data, "expired\n");
466219b2ee8SDavid du Colombier 		else
467219b2ee8SDavid du Colombier 			sprint(thdr.data, "%s\n", status[f->user->status]);
468219b2ee8SDavid du Colombier 		thdr.count = strlen(thdr.data);
469219b2ee8SDavid du Colombier 		return 0;
470219b2ee8SDavid du Colombier 	case Qexpire:
471219b2ee8SDavid du Colombier 		if(off != 0){
472219b2ee8SDavid du Colombier 			thdr.count = 0;
473219b2ee8SDavid du Colombier 			return 0;
474219b2ee8SDavid du Colombier 		}
475219b2ee8SDavid du Colombier 		if(!f->user->expire)
476219b2ee8SDavid du Colombier 			strcpy(data, "never\n");
477219b2ee8SDavid du Colombier 		else
478219b2ee8SDavid du Colombier 			sprint(data, "%lud\n", f->user->expire);
479219b2ee8SDavid du Colombier 		if(n > strlen(data))
480219b2ee8SDavid du Colombier 			n = strlen(data);
481219b2ee8SDavid du Colombier 		thdr.count = n;
482219b2ee8SDavid du Colombier 		return 0;
483219b2ee8SDavid du Colombier 	case Qlog:
484219b2ee8SDavid du Colombier 		if(off != 0){
485219b2ee8SDavid du Colombier 			thdr.count = 0;
486219b2ee8SDavid du Colombier 			return 0;
487219b2ee8SDavid du Colombier 		}
48839734e7eSDavid du Colombier 		sprint(data, "%lud\n", f->user->bad);
489219b2ee8SDavid du Colombier 		if(n > strlen(data))
490219b2ee8SDavid du Colombier 			n = strlen(data);
491219b2ee8SDavid du Colombier 		thdr.count = n;
492219b2ee8SDavid du Colombier 		return 0;
493219b2ee8SDavid du Colombier 	case Qwarnings:
494219b2ee8SDavid du Colombier 		if(off != 0){
495219b2ee8SDavid du Colombier 			thdr.count = 0;
496219b2ee8SDavid du Colombier 			return 0;
497219b2ee8SDavid du Colombier 		}
4987dd7cddfSDavid du Colombier 		sprint(data, "%ud\n", f->user->warnings);
499219b2ee8SDavid du Colombier 		if(n > strlen(data))
500219b2ee8SDavid du Colombier 			n = strlen(data);
501219b2ee8SDavid du Colombier 		thdr.count = n;
502219b2ee8SDavid du Colombier 		return 0;
503219b2ee8SDavid du Colombier 	default:
5049a747e4fSDavid du Colombier 		return "permission denied: unknown qid";
505219b2ee8SDavid du Colombier 	}
506219b2ee8SDavid du Colombier }
507219b2ee8SDavid du Colombier 
508219b2ee8SDavid du Colombier char *
Write(Fid * f)509219b2ee8SDavid du Colombier Write(Fid *f)
510219b2ee8SDavid du Colombier {
511219b2ee8SDavid du Colombier 	char *data, *p;
512219b2ee8SDavid du Colombier 	ulong n, expire;
513219b2ee8SDavid du Colombier 	int i;
514219b2ee8SDavid du Colombier 
515219b2ee8SDavid du Colombier 	if(!f->busy)
516219b2ee8SDavid du Colombier 		return "permission denied";
517219b2ee8SDavid du Colombier 	n = rhdr.count;
518219b2ee8SDavid du Colombier 	data = rhdr.data;
519219b2ee8SDavid du Colombier 	switch(f->qtype){
520219b2ee8SDavid du Colombier 	case Qkey:
521219b2ee8SDavid du Colombier 		if(n != DESKEYLEN)
522219b2ee8SDavid du Colombier 			return "garbled write data";
523219b2ee8SDavid du Colombier 		memmove(f->user->key, data, DESKEYLEN);
524219b2ee8SDavid du Colombier 		thdr.count = DESKEYLEN;
525219b2ee8SDavid du Colombier 		break;
5267dd7cddfSDavid du Colombier 	case Qsecret:
5277dd7cddfSDavid du Colombier 		if(n >= SECRETLEN)
5287dd7cddfSDavid du Colombier 			return "garbled write data";
5297dd7cddfSDavid du Colombier 		memmove(f->user->secret, data, n);
5307dd7cddfSDavid du Colombier 		f->user->secret[n] = 0;
5317dd7cddfSDavid du Colombier 		thdr.count = n;
5327dd7cddfSDavid du Colombier 		break;
533219b2ee8SDavid du Colombier 	case Qstatus:
534219b2ee8SDavid du Colombier 		data[n] = '\0';
535219b2ee8SDavid du Colombier 		if(p = strchr(data, '\n'))
536219b2ee8SDavid du Colombier 			*p = '\0';
537219b2ee8SDavid du Colombier 		for(i = 0; i < Smax; i++)
538219b2ee8SDavid du Colombier 			if(strcmp(data, status[i]) == 0){
539219b2ee8SDavid du Colombier 				f->user->status = i;
540219b2ee8SDavid du Colombier 				break;
541219b2ee8SDavid du Colombier 			}
542219b2ee8SDavid du Colombier 		if(i == Smax)
543219b2ee8SDavid du Colombier 			return "unknown status";
544219b2ee8SDavid du Colombier 		f->user->bad = 0;
545219b2ee8SDavid du Colombier 		thdr.count = n;
546219b2ee8SDavid du Colombier 		break;
547219b2ee8SDavid du Colombier 	case Qexpire:
548219b2ee8SDavid du Colombier 		data[n] = '\0';
549219b2ee8SDavid du Colombier 		if(p = strchr(data, '\n'))
550219b2ee8SDavid du Colombier 			*p = '\0';
551219b2ee8SDavid du Colombier 		else
552219b2ee8SDavid du Colombier 			p = &data[n];
553219b2ee8SDavid du Colombier 		if(strcmp(data, "never") == 0)
554219b2ee8SDavid du Colombier 			expire = 0;
555219b2ee8SDavid du Colombier 		else{
556219b2ee8SDavid du Colombier 			expire = strtoul(data, &data, 10);
557219b2ee8SDavid du Colombier 			if(data != p)
558219b2ee8SDavid du Colombier 				return "bad expiration date";
559219b2ee8SDavid du Colombier 		}
560219b2ee8SDavid du Colombier 		f->user->expire = expire;
561219b2ee8SDavid du Colombier 		f->user->warnings = 0;
562219b2ee8SDavid du Colombier 		thdr.count = n;
563219b2ee8SDavid du Colombier 		break;
564219b2ee8SDavid du Colombier 	case Qlog:
565219b2ee8SDavid du Colombier 		data[n] = '\0';
566219b2ee8SDavid du Colombier 		if(strcmp(data, "good") == 0)
567219b2ee8SDavid du Colombier 			f->user->bad = 0;
568219b2ee8SDavid du Colombier 		else
569219b2ee8SDavid du Colombier 			f->user->bad++;
57039734e7eSDavid du Colombier 		if(f->user->bad && ((f->user->bad)%MAXBAD) == 0)
57139734e7eSDavid du Colombier 			f->user->purgatory = time(0) + f->user->bad;
572219b2ee8SDavid du Colombier 		return 0;
573219b2ee8SDavid du Colombier 	case Qwarnings:
574219b2ee8SDavid du Colombier 		data[n] = '\0';
575219b2ee8SDavid du Colombier 		f->user->warnings = strtoul(data, 0, 10);
576219b2ee8SDavid du Colombier 		thdr.count = n;
577219b2ee8SDavid du Colombier 		break;
578219b2ee8SDavid du Colombier 	case Qroot:
579219b2ee8SDavid du Colombier 	case Quser:
580219b2ee8SDavid du Colombier 	default:
581219b2ee8SDavid du Colombier 		return "permission denied";
582219b2ee8SDavid du Colombier 	}
583219b2ee8SDavid du Colombier 	writeusers();
584219b2ee8SDavid du Colombier 	return 0;
585219b2ee8SDavid du Colombier }
586219b2ee8SDavid du Colombier 
587219b2ee8SDavid du Colombier char *
Remove(Fid * f)588219b2ee8SDavid du Colombier Remove(Fid *f)
589219b2ee8SDavid du Colombier {
590219b2ee8SDavid du Colombier 	if(!f->busy)
591219b2ee8SDavid du Colombier 		return "permission denied";
592219b2ee8SDavid du Colombier 	if(f->qtype == Qwarnings)
593219b2ee8SDavid du Colombier 		f->user->warnings = 0;
594219b2ee8SDavid du Colombier 	else if(f->qtype == Quser)
595219b2ee8SDavid du Colombier 		removeuser(f->user);
5966b8bc682SDavid du Colombier 	else {
5976b8bc682SDavid du Colombier 		Clunk(f);
598219b2ee8SDavid du Colombier 		return "permission denied";
5996b8bc682SDavid du Colombier 	}
600219b2ee8SDavid du Colombier 	Clunk(f);
601219b2ee8SDavid du Colombier 	writeusers();
602219b2ee8SDavid du Colombier 	return 0;
603219b2ee8SDavid du Colombier }
604219b2ee8SDavid du Colombier 
605219b2ee8SDavid du Colombier char *
Stat(Fid * f)606219b2ee8SDavid du Colombier Stat(Fid *f)
607219b2ee8SDavid du Colombier {
6089a747e4fSDavid du Colombier 	static uchar statbuf[1024];
6099a747e4fSDavid du Colombier 
610219b2ee8SDavid du Colombier 	if(!f->busy)
611219b2ee8SDavid du Colombier 		return "stat on unattached fid";
6129a747e4fSDavid du Colombier 	thdr.nstat = dostat(f->user, f->qtype, statbuf, sizeof statbuf);
6139a747e4fSDavid du Colombier 	if(thdr.nstat <= BIT16SZ)
6149a747e4fSDavid du Colombier 		return "stat buffer too small";
6159a747e4fSDavid du Colombier 	thdr.stat = statbuf;
616219b2ee8SDavid du Colombier 	return 0;
617219b2ee8SDavid du Colombier }
618219b2ee8SDavid du Colombier 
619219b2ee8SDavid du Colombier char *
Wstat(Fid * f)620219b2ee8SDavid du Colombier Wstat(Fid *f)
621219b2ee8SDavid du Colombier {
622219b2ee8SDavid du Colombier 	Dir d;
6239a747e4fSDavid du Colombier 	int n;
6249a747e4fSDavid du Colombier 	char buf[1024];
625219b2ee8SDavid du Colombier 
626219b2ee8SDavid du Colombier 	if(!f->busy || f->qtype != Quser)
627219b2ee8SDavid du Colombier 		return "permission denied";
6289a747e4fSDavid du Colombier 	if(rhdr.nstat > sizeof buf)
6299a747e4fSDavid du Colombier 		return "wstat buffer too big";
6309a747e4fSDavid du Colombier 	if(convM2D(rhdr.stat, rhdr.nstat, &d, buf) == 0)
631219b2ee8SDavid du Colombier 		return "bad stat buffer";
6329a747e4fSDavid du Colombier 	n = strlen(d.name);
6339a747e4fSDavid du Colombier 	if(n == 0 || n >= Namelen)
634219b2ee8SDavid du Colombier 		return "bad user name";
635219b2ee8SDavid du Colombier 	if(finduser(d.name))
636219b2ee8SDavid du Colombier 		return "user already exists";
637219b2ee8SDavid du Colombier 	if(!removeuser(f->user))
638219b2ee8SDavid du Colombier 		return "user previously removed";
6399a747e4fSDavid du Colombier 	free(f->user->name);
6409a747e4fSDavid du Colombier 	f->user->name = strdup(d.name);
6419a747e4fSDavid du Colombier 	if(f->user->name == nil)
6429a747e4fSDavid du Colombier 		error("wstat: malloc failed: %r");
643219b2ee8SDavid du Colombier 	insertuser(f->user);
644219b2ee8SDavid du Colombier 	writeusers();
645219b2ee8SDavid du Colombier 	return 0;
646219b2ee8SDavid du Colombier }
647219b2ee8SDavid du Colombier 
648219b2ee8SDavid du Colombier Qid
mkqid(User * u,ulong qtype)649219b2ee8SDavid du Colombier mkqid(User *u, ulong qtype)
650219b2ee8SDavid du Colombier {
651219b2ee8SDavid du Colombier 	Qid q;
652219b2ee8SDavid du Colombier 
653219b2ee8SDavid du Colombier 	q.vers = 0;
654219b2ee8SDavid du Colombier 	q.path = qtype;
655219b2ee8SDavid du Colombier 	if(u)
656219b2ee8SDavid du Colombier 		q.path |= u->uniq * 0x100;
657219b2ee8SDavid du Colombier 	if(qtype == Quser || qtype == Qroot)
6589a747e4fSDavid du Colombier 		q.type = QTDIR;
6599a747e4fSDavid du Colombier 	else
6609a747e4fSDavid du Colombier 		q.type = QTFILE;
661219b2ee8SDavid du Colombier 	return q;
662219b2ee8SDavid du Colombier }
663219b2ee8SDavid du Colombier 
6649a747e4fSDavid du Colombier int
dostat(User * user,ulong qtype,void * p,int n)6659a747e4fSDavid du Colombier dostat(User *user, ulong qtype, void *p, int n)
666219b2ee8SDavid du Colombier {
667219b2ee8SDavid du Colombier 	Dir d;
668219b2ee8SDavid du Colombier 
669219b2ee8SDavid du Colombier 	if(qtype == Quser)
6709a747e4fSDavid du Colombier 		d.name = user->name;
671219b2ee8SDavid du Colombier 	else
6729a747e4fSDavid du Colombier 		d.name = qinfo[qtype];
6739a747e4fSDavid du Colombier 	d.uid = d.gid = d.muid = "auth";
674219b2ee8SDavid du Colombier 	d.qid = mkqid(user, qtype);
6759a747e4fSDavid du Colombier 	if(d.qid.type & QTDIR)
6769a747e4fSDavid du Colombier 		d.mode = 0777|DMDIR;
677219b2ee8SDavid du Colombier 	else
678219b2ee8SDavid du Colombier 		d.mode = 0666;
679219b2ee8SDavid du Colombier 	d.atime = d.mtime = time(0);
6807dd7cddfSDavid du Colombier 	d.length = 0;
6819a747e4fSDavid du Colombier 	return convD2M(&d, p, n);
682219b2ee8SDavid du Colombier }
683219b2ee8SDavid du Colombier 
684219b2ee8SDavid du Colombier int
passline(Biobuf * b,void * vbuf)685219b2ee8SDavid du Colombier passline(Biobuf *b, void *vbuf)
686219b2ee8SDavid du Colombier {
687219b2ee8SDavid du Colombier 	char *buf = vbuf;
688219b2ee8SDavid du Colombier 
689219b2ee8SDavid du Colombier 	if(Bread(b, buf, KEYDBLEN) != KEYDBLEN)
690219b2ee8SDavid du Colombier 		return 0;
691219b2ee8SDavid du Colombier 	decrypt(authkey, buf, KEYDBLEN);
6929a747e4fSDavid du Colombier 	buf[Namelen-1] = '\0';
693219b2ee8SDavid du Colombier 	return 1;
694219b2ee8SDavid du Colombier }
695219b2ee8SDavid du Colombier 
696219b2ee8SDavid du Colombier void
randombytes(uchar * p,int len)6977dd7cddfSDavid du Colombier randombytes(uchar *p, int len)
698219b2ee8SDavid du Colombier {
6997dd7cddfSDavid du Colombier 	int i, fd;
700219b2ee8SDavid du Colombier 
7017dd7cddfSDavid du Colombier 	fd = open("/dev/random", OREAD);
7027dd7cddfSDavid du Colombier 	if(fd < 0){
703d854de59SDavid du Colombier 		fprint(2, "keyfs: can't open /dev/random, using rand()\n");
7047dd7cddfSDavid du Colombier 		srand(time(0));
7057dd7cddfSDavid du Colombier 		for(i = 0; i < len; i++)
7067dd7cddfSDavid du Colombier 			p[i] = rand();
707219b2ee8SDavid du Colombier 		return;
708219b2ee8SDavid du Colombier 	}
7097dd7cddfSDavid du Colombier 	read(fd, p, len);
7107dd7cddfSDavid du Colombier 	close(fd);
7117dd7cddfSDavid du Colombier }
7127dd7cddfSDavid du Colombier 
7137dd7cddfSDavid du Colombier void
oldCBCencrypt(char * key7,uchar * p,int len)7147dd7cddfSDavid du Colombier oldCBCencrypt(char *key7, uchar *p, int len)
7157dd7cddfSDavid du Colombier {
7167dd7cddfSDavid du Colombier 	uchar ivec[8];
7177dd7cddfSDavid du Colombier 	uchar key[8];
7187dd7cddfSDavid du Colombier 	DESstate s;
7197dd7cddfSDavid du Colombier 
7207dd7cddfSDavid du Colombier 	memset(ivec, 0, 8);
7217dd7cddfSDavid du Colombier 	des56to64((uchar*)key7, key);
7227dd7cddfSDavid du Colombier 	setupDESstate(&s, key, ivec);
7237dd7cddfSDavid du Colombier 	desCBCencrypt((uchar*)p, len, &s);
7247dd7cddfSDavid du Colombier }
7257dd7cddfSDavid du Colombier 
7267dd7cddfSDavid du Colombier void
oldCBCdecrypt(char * key7,uchar * p,int len)7277dd7cddfSDavid du Colombier oldCBCdecrypt(char *key7, uchar *p, int len)
7287dd7cddfSDavid du Colombier {
7297dd7cddfSDavid du Colombier 	uchar ivec[8];
7307dd7cddfSDavid du Colombier 	uchar key[8];
7317dd7cddfSDavid du Colombier 	DESstate s;
7327dd7cddfSDavid du Colombier 
7337dd7cddfSDavid du Colombier 	memset(ivec, 0, 8);
7347dd7cddfSDavid du Colombier 	des56to64((uchar*)key7, key);
7357dd7cddfSDavid du Colombier 	setupDESstate(&s, key, ivec);
7367dd7cddfSDavid du Colombier 	desCBCdecrypt((uchar*)p, len, &s);
7377dd7cddfSDavid du Colombier 
7387dd7cddfSDavid du Colombier }
7397dd7cddfSDavid du Colombier 
7407dd7cddfSDavid du Colombier void
writeusers(void)7417dd7cddfSDavid du Colombier writeusers(void)
7427dd7cddfSDavid du Colombier {
7437dd7cddfSDavid du Colombier 	int fd, i, nu;
7447dd7cddfSDavid du Colombier 	User *u;
7457dd7cddfSDavid du Colombier 	uchar *p, *buf;
7467dd7cddfSDavid du Colombier 	ulong expire;
7477dd7cddfSDavid du Colombier 
7487dd7cddfSDavid du Colombier 	/* count users */
7497dd7cddfSDavid du Colombier 	nu = 0;
7507dd7cddfSDavid du Colombier 	for(i = 0; i < Nuser; i++)
7517dd7cddfSDavid du Colombier 		for(u = users[i]; u; u = u->link)
7527dd7cddfSDavid du Colombier 			nu++;
7537dd7cddfSDavid du Colombier 
7547dd7cddfSDavid du Colombier 	/* pack into buffer */
7557dd7cddfSDavid du Colombier 	buf = malloc(KEYDBOFF + nu*KEYDBLEN);
7567dd7cddfSDavid du Colombier 	if(buf == 0){
7577dd7cddfSDavid du Colombier 		fprint(2, "keyfs: can't write keys file, out of memory\n");
7587dd7cddfSDavid du Colombier 		return;
7597dd7cddfSDavid du Colombier 	}
7607dd7cddfSDavid du Colombier 	p = buf;
7617dd7cddfSDavid du Colombier 	randombytes(p, KEYDBOFF);
7627dd7cddfSDavid du Colombier 	p += KEYDBOFF;
763219b2ee8SDavid du Colombier 	for(i = 0; i < Nuser; i++)
764219b2ee8SDavid du Colombier 		for(u = users[i]; u; u = u->link){
7659a747e4fSDavid du Colombier 			strncpy((char*)p, u->name, Namelen);
7669a747e4fSDavid du Colombier 			p += Namelen;
7677dd7cddfSDavid du Colombier 			memmove(p, u->key, DESKEYLEN);
7687dd7cddfSDavid du Colombier 			p += DESKEYLEN;
769219b2ee8SDavid du Colombier 			*p++ = u->status;
770219b2ee8SDavid du Colombier 			*p++ = u->warnings;
771219b2ee8SDavid du Colombier 			expire = u->expire;
772219b2ee8SDavid du Colombier 			*p++ = expire;
773219b2ee8SDavid du Colombier 			*p++ = expire >> 8;
774219b2ee8SDavid du Colombier 			*p++ = expire >> 16;
7757dd7cddfSDavid du Colombier 			*p++ = expire >> 24;
7767dd7cddfSDavid du Colombier 			memmove(p, u->secret, SECRETLEN);
7777dd7cddfSDavid du Colombier 			p += SECRETLEN;
778219b2ee8SDavid du Colombier 		}
7797dd7cddfSDavid du Colombier 
7807dd7cddfSDavid du Colombier 	/* encrypt */
7817dd7cddfSDavid du Colombier 	oldCBCencrypt(authkey, buf, p - buf);
7827dd7cddfSDavid du Colombier 
7837dd7cddfSDavid du Colombier 	/* write file */
7847dd7cddfSDavid du Colombier 	fd = create(userkeys, OWRITE, 0660);
7857dd7cddfSDavid du Colombier 	if(fd < 0){
7867dd7cddfSDavid du Colombier 		free(buf);
7877dd7cddfSDavid du Colombier 		fprint(2, "keyfs: can't write keys file\n");
7887dd7cddfSDavid du Colombier 		return;
7897dd7cddfSDavid du Colombier 	}
7907dd7cddfSDavid du Colombier 	if(write(fd, buf, p - buf) != (p - buf))
7917dd7cddfSDavid du Colombier 		fprint(2, "keyfs: can't write keys file\n");
7927dd7cddfSDavid du Colombier 
7937dd7cddfSDavid du Colombier 	free(buf);
7947dd7cddfSDavid du Colombier 	close(fd);
795219b2ee8SDavid du Colombier }
796219b2ee8SDavid du Colombier 
797219b2ee8SDavid du Colombier int
weirdfmt(Fmt * f)7985864cca7SDavid du Colombier weirdfmt(Fmt *f)
7992bef681aSDavid du Colombier {
800*f54edc78SDavid du Colombier 	char *s, *p, *ep, buf[ANAMELEN*4 + 1];
801*f54edc78SDavid du Colombier 	int i, n;
8022bef681aSDavid du Colombier 	Rune r;
8032bef681aSDavid du Colombier 
8042bef681aSDavid du Colombier 	s = va_arg(f->args, char*);
805*f54edc78SDavid du Colombier 	p = buf;
806*f54edc78SDavid du Colombier 	ep = buf + sizeof buf;
8072bef681aSDavid du Colombier 	for(i = 0; i < ANAMELEN; i += n){
8082bef681aSDavid du Colombier 		n = chartorune(&r, s + i);
8092bef681aSDavid du Colombier 		if(r == Runeerror)
810*f54edc78SDavid du Colombier 			p = seprint(p, ep, "[%.2x]", buf[i]);
8112bef681aSDavid du Colombier 		else if(isascii(r) && iscntrl(r))
812*f54edc78SDavid du Colombier 			p = seprint(p, ep, "[%.2x]", r);
8132bef681aSDavid du Colombier 		else if(r == ' ' || r == '/')
814*f54edc78SDavid du Colombier 			p = seprint(p, ep, "[%c]", r);
8152bef681aSDavid du Colombier 		else
816*f54edc78SDavid du Colombier 			p = seprint(p, ep, "%C", r);
8172bef681aSDavid du Colombier 	}
8182bef681aSDavid du Colombier 	return fmtstrcpy(f, buf);
8192bef681aSDavid du Colombier }
8202bef681aSDavid du Colombier 
8212bef681aSDavid du Colombier int
userok(char * user,int nu)8222bef681aSDavid du Colombier userok(char *user, int nu)
8232bef681aSDavid du Colombier {
8242bef681aSDavid du Colombier 	int i, n, rv;
8252bef681aSDavid du Colombier 	Rune r;
8262bef681aSDavid du Colombier 	char buf[ANAMELEN+1];
8272bef681aSDavid du Colombier 
8282bef681aSDavid du Colombier 	memset(buf, 0, sizeof buf);
8292bef681aSDavid du Colombier 	memmove(buf, user, ANAMELEN);
8302bef681aSDavid du Colombier 
8312bef681aSDavid du Colombier 	if(buf[ANAMELEN-1] != 0){
8322bef681aSDavid du Colombier 		fprint(2, "keyfs: %d: no termination: %W\n", nu, buf);
8332bef681aSDavid du Colombier 		return -1;
8342bef681aSDavid du Colombier 	}
8352bef681aSDavid du Colombier 
8362bef681aSDavid du Colombier 	rv = 0;
8372bef681aSDavid du Colombier 	for(i = 0; buf[i]; i += n){
8382bef681aSDavid du Colombier 		n = chartorune(&r, buf+i);
8392bef681aSDavid du Colombier 		if(r == Runeerror){
8402bef681aSDavid du Colombier //			fprint(2, "keyfs: name %W bad rune byte %d\n", buf, i);
8412bef681aSDavid du Colombier 			rv = -1;
8422bef681aSDavid du Colombier 		} else if(isascii(r) && iscntrl(r) || r == ' ' || r == '/'){
8432bef681aSDavid du Colombier //			fprint(2, "keyfs: name %W bad char %C\n", buf, r);
8442bef681aSDavid du Colombier 			rv = -1;
8452bef681aSDavid du Colombier 		}
8462bef681aSDavid du Colombier 	}
8472bef681aSDavid du Colombier 
8482bef681aSDavid du Colombier 	if(i == 0){
8492bef681aSDavid du Colombier 		fprint(2, "keyfs: %d: nil name\n", nu);
8502bef681aSDavid du Colombier 		return -1;
8512bef681aSDavid du Colombier 	}
8522bef681aSDavid du Colombier 	if(rv == -1)
8532bef681aSDavid du Colombier 		fprint(2, "keyfs: %d: bad syntax: %W\n", nu, buf);
8542bef681aSDavid du Colombier 	return rv;
8552bef681aSDavid du Colombier }
8562bef681aSDavid du Colombier 
8572bef681aSDavid du Colombier int
readusers(void)858219b2ee8SDavid du Colombier readusers(void)
859219b2ee8SDavid du Colombier {
8607dd7cddfSDavid du Colombier 	int fd, i, n, nu;
8617dd7cddfSDavid du Colombier 	uchar *p, *buf, *ep;
862219b2ee8SDavid du Colombier 	User *u;
8639a747e4fSDavid du Colombier 	Dir *d;
864219b2ee8SDavid du Colombier 
8657dd7cddfSDavid du Colombier 	/* read file into an array */
8667dd7cddfSDavid du Colombier 	fd = open(userkeys, OREAD);
8677dd7cddfSDavid du Colombier 	if(fd < 0)
8687dd7cddfSDavid du Colombier 		return 0;
8699a747e4fSDavid du Colombier 	d = dirfstat(fd);
8709a747e4fSDavid du Colombier 	if(d == nil){
8717dd7cddfSDavid du Colombier 		close(fd);
8727dd7cddfSDavid du Colombier 		return 0;
8737dd7cddfSDavid du Colombier 	}
8749a747e4fSDavid du Colombier 	buf = malloc(d->length);
8757dd7cddfSDavid du Colombier 	if(buf == 0){
8767dd7cddfSDavid du Colombier 		close(fd);
8779a747e4fSDavid du Colombier 		free(d);
8787dd7cddfSDavid du Colombier 		return 0;
8797dd7cddfSDavid du Colombier 	}
8809a747e4fSDavid du Colombier 	n = readn(fd, buf, d->length);
8817dd7cddfSDavid du Colombier 	close(fd);
8829a747e4fSDavid du Colombier 	free(d);
8839a747e4fSDavid du Colombier 	if(n != d->length){
8847dd7cddfSDavid du Colombier 		free(buf);
8857dd7cddfSDavid du Colombier 		return 0;
8867dd7cddfSDavid du Colombier 	}
8877dd7cddfSDavid du Colombier 
8887dd7cddfSDavid du Colombier 	/* decrypt */
8897dd7cddfSDavid du Colombier 	n -= n % KEYDBLEN;
8907dd7cddfSDavid du Colombier 	oldCBCdecrypt(authkey, buf, n);
8917dd7cddfSDavid du Colombier 
8927dd7cddfSDavid du Colombier 	/* unpack */
893219b2ee8SDavid du Colombier 	nu = 0;
8947dd7cddfSDavid du Colombier 	for(i = KEYDBOFF; i < n; i += KEYDBLEN){
8957dd7cddfSDavid du Colombier 		ep = buf + i;
8962bef681aSDavid du Colombier 		if(userok((char*)ep, i/KEYDBLEN) < 0)
8972bef681aSDavid du Colombier 			continue;
8987dd7cddfSDavid du Colombier 		u = finduser((char*)ep);
899219b2ee8SDavid du Colombier 		if(u == 0)
9007dd7cddfSDavid du Colombier 			u = installuser((char*)ep);
9019a747e4fSDavid du Colombier 		memmove(u->key, ep + Namelen, DESKEYLEN);
9029a747e4fSDavid du Colombier 		p = ep + Namelen + DESKEYLEN;
903219b2ee8SDavid du Colombier 		u->status = *p++;
904219b2ee8SDavid du Colombier 		u->warnings = *p++;
905219b2ee8SDavid du Colombier 		if(u->status >= Smax)
906219b2ee8SDavid du Colombier 			fprint(2, "keyfs: warning: bad status in key file\n");
907219b2ee8SDavid du Colombier 		u->expire = p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24);
9087dd7cddfSDavid du Colombier 		p += 4;
9097dd7cddfSDavid du Colombier 		memmove(u->secret, p, SECRETLEN);
9107dd7cddfSDavid du Colombier 		u->secret[SECRETLEN-1] = 0;
911219b2ee8SDavid du Colombier 		nu++;
912219b2ee8SDavid du Colombier 	}
9137dd7cddfSDavid du Colombier 	free(buf);
9147dd7cddfSDavid du Colombier 
915219b2ee8SDavid du Colombier 	print("%d keys read\n", nu);
9167dd7cddfSDavid du Colombier 	return 1;
917219b2ee8SDavid du Colombier }
918219b2ee8SDavid du Colombier 
919219b2ee8SDavid du Colombier User *
installuser(char * name)920219b2ee8SDavid du Colombier installuser(char *name)
921219b2ee8SDavid du Colombier {
922219b2ee8SDavid du Colombier 	User *u;
923219b2ee8SDavid du Colombier 	int h;
924219b2ee8SDavid du Colombier 
925219b2ee8SDavid du Colombier 	h = hash(name);
926219b2ee8SDavid du Colombier 	u = emalloc(sizeof *u);
9279a747e4fSDavid du Colombier 	u->name = strdup(name);
9289a747e4fSDavid du Colombier 	if(u->name == nil)
9299a747e4fSDavid du Colombier 		error("malloc failed: %r");
930219b2ee8SDavid du Colombier 	u->removed = 0;
931219b2ee8SDavid du Colombier 	u->ref = 0;
9320dac3555SDavid du Colombier 	u->purgatory = 0;
933219b2ee8SDavid du Colombier 	u->expire = 0;
934219b2ee8SDavid du Colombier 	u->status = Sok;
935219b2ee8SDavid du Colombier 	u->bad = 0;
936219b2ee8SDavid du Colombier 	u->warnings = 0;
937219b2ee8SDavid du Colombier 	u->uniq = uniq++;
938219b2ee8SDavid du Colombier 	u->link = users[h];
939219b2ee8SDavid du Colombier 	users[h] = u;
940219b2ee8SDavid du Colombier 	return u;
941219b2ee8SDavid du Colombier }
942219b2ee8SDavid du Colombier 
943219b2ee8SDavid du Colombier User *
finduser(char * name)944219b2ee8SDavid du Colombier finduser(char *name)
945219b2ee8SDavid du Colombier {
946219b2ee8SDavid du Colombier 	User *u;
947219b2ee8SDavid du Colombier 
948219b2ee8SDavid du Colombier 	for(u = users[hash(name)]; u; u = u->link)
949219b2ee8SDavid du Colombier 		if(strcmp(name, u->name) == 0)
950219b2ee8SDavid du Colombier 			return u;
951219b2ee8SDavid du Colombier 	return 0;
952219b2ee8SDavid du Colombier }
953219b2ee8SDavid du Colombier 
954219b2ee8SDavid du Colombier int
removeuser(User * user)955219b2ee8SDavid du Colombier removeuser(User *user)
956219b2ee8SDavid du Colombier {
957219b2ee8SDavid du Colombier 	User *u, **last;
958219b2ee8SDavid du Colombier 	char *name;
959219b2ee8SDavid du Colombier 
960219b2ee8SDavid du Colombier 	user->removed = 1;
961219b2ee8SDavid du Colombier 	name = user->name;
962219b2ee8SDavid du Colombier 	last = &users[hash(name)];
963219b2ee8SDavid du Colombier 	for(u = *last; u; u = *last){
964219b2ee8SDavid du Colombier 		if(strcmp(name, u->name) == 0){
965219b2ee8SDavid du Colombier 			*last = u->link;
966219b2ee8SDavid du Colombier 			return 1;
967219b2ee8SDavid du Colombier 		}
968219b2ee8SDavid du Colombier 		last = &u->link;
969219b2ee8SDavid du Colombier 	}
970219b2ee8SDavid du Colombier 	return 0;
971219b2ee8SDavid du Colombier }
972219b2ee8SDavid du Colombier 
973219b2ee8SDavid du Colombier void
insertuser(User * user)974219b2ee8SDavid du Colombier insertuser(User *user)
975219b2ee8SDavid du Colombier {
976219b2ee8SDavid du Colombier 	int h;
977219b2ee8SDavid du Colombier 
978219b2ee8SDavid du Colombier 	user->removed = 0;
979219b2ee8SDavid du Colombier 	h = hash(user->name);
980219b2ee8SDavid du Colombier 	user->link = users[h];
981219b2ee8SDavid du Colombier 	users[h] = user;
982219b2ee8SDavid du Colombier }
983219b2ee8SDavid du Colombier 
984219b2ee8SDavid du Colombier ulong
hash(char * s)985219b2ee8SDavid du Colombier hash(char *s)
986219b2ee8SDavid du Colombier {
987219b2ee8SDavid du Colombier 	ulong h;
988219b2ee8SDavid du Colombier 
989219b2ee8SDavid du Colombier 	h = 0;
990219b2ee8SDavid du Colombier 	while(*s)
991219b2ee8SDavid du Colombier 		h = (h << 1) ^ *s++;
992219b2ee8SDavid du Colombier 	return h % Nuser;
993219b2ee8SDavid du Colombier }
994219b2ee8SDavid du Colombier 
995219b2ee8SDavid du Colombier Fid *
findfid(int fid)996219b2ee8SDavid du Colombier findfid(int fid)
997219b2ee8SDavid du Colombier {
998219b2ee8SDavid du Colombier 	Fid *f, *ff;
999219b2ee8SDavid du Colombier 
1000219b2ee8SDavid du Colombier 	ff = 0;
1001219b2ee8SDavid du Colombier 	for(f = fids; f; f = f->next)
1002219b2ee8SDavid du Colombier 		if(f->fid == fid)
1003219b2ee8SDavid du Colombier 			return f;
1004219b2ee8SDavid du Colombier 		else if(!ff && !f->busy)
1005219b2ee8SDavid du Colombier 			ff = f;
1006219b2ee8SDavid du Colombier 	if(ff){
1007219b2ee8SDavid du Colombier 		ff->fid = fid;
1008219b2ee8SDavid du Colombier 		return ff;
1009219b2ee8SDavid du Colombier 	}
1010219b2ee8SDavid du Colombier 	f = emalloc(sizeof *f);
1011219b2ee8SDavid du Colombier 	f->fid = fid;
1012219b2ee8SDavid du Colombier 	f->busy = 0;
1013219b2ee8SDavid du Colombier 	f->user = 0;
1014219b2ee8SDavid du Colombier 	f->next = fids;
1015219b2ee8SDavid du Colombier 	fids = f;
1016219b2ee8SDavid du Colombier 	return f;
1017219b2ee8SDavid du Colombier }
1018219b2ee8SDavid du Colombier 
1019219b2ee8SDavid du Colombier void
io(int in,int out)1020219b2ee8SDavid du Colombier io(int in, int out)
1021219b2ee8SDavid du Colombier {
10229a747e4fSDavid du Colombier 	char *err;
1023219b2ee8SDavid du Colombier 	int n;
10247dd7cddfSDavid du Colombier 	long now, lastwarning;
10257dd7cddfSDavid du Colombier 
10267dd7cddfSDavid du Colombier 	/* after restart, let the system settle for 5 mins before warning */
10277dd7cddfSDavid du Colombier 	lastwarning = time(0) - 24*60*60 + 5*60;
1028219b2ee8SDavid du Colombier 
1029219b2ee8SDavid du Colombier 	for(;;){
10309a747e4fSDavid du Colombier 		n = read9pmsg(in, mdata, messagesize);
1031219b2ee8SDavid du Colombier 		if(n == 0)
1032219b2ee8SDavid du Colombier 			continue;
1033219b2ee8SDavid du Colombier 		if(n < 0)
1034219b2ee8SDavid du Colombier 			error("mount read %d", n);
10359a747e4fSDavid du Colombier 		if(convM2S(mdata, n, &rhdr) == 0)
1036219b2ee8SDavid du Colombier 			continue;
1037219b2ee8SDavid du Colombier 
1038219b2ee8SDavid du Colombier 		if(newkeys())
1039219b2ee8SDavid du Colombier 			readusers();
1040219b2ee8SDavid du Colombier 
10419a747e4fSDavid du Colombier 		thdr.data = (char*)mdata + IOHDRSZ;
1042219b2ee8SDavid du Colombier 		thdr.fid = rhdr.fid;
1043219b2ee8SDavid du Colombier 		if(!fcalls[rhdr.type])
1044219b2ee8SDavid du Colombier 			err = "fcall request";
1045219b2ee8SDavid du Colombier 		else
1046219b2ee8SDavid du Colombier 			err = (*fcalls[rhdr.type])(findfid(rhdr.fid));
1047219b2ee8SDavid du Colombier 		thdr.tag = rhdr.tag;
1048219b2ee8SDavid du Colombier 		thdr.type = rhdr.type+1;
1049219b2ee8SDavid du Colombier 		if(err){
1050219b2ee8SDavid du Colombier 			thdr.type = Rerror;
10519a747e4fSDavid du Colombier 			thdr.ename = err;
1052219b2ee8SDavid du Colombier 		}
10539a747e4fSDavid du Colombier 		n = convS2M(&thdr, mdata, messagesize);
1054219b2ee8SDavid du Colombier 		if(write(out, mdata, n) != n)
1055219b2ee8SDavid du Colombier 			error("mount write");
1056219b2ee8SDavid du Colombier 
10577dd7cddfSDavid du Colombier 		now = time(0);
10587dd7cddfSDavid du Colombier 		if(warnarg && (now - lastwarning > 24*60*60)){
10592bef681aSDavid du Colombier 			syslog(0, "auth", "keyfs starting warnings: %lux %lux",
10602bef681aSDavid du Colombier 				now, lastwarning);
1061219b2ee8SDavid du Colombier 			warning();
10627dd7cddfSDavid du Colombier 			lastwarning = now;
1063219b2ee8SDavid du Colombier 		}
1064219b2ee8SDavid du Colombier 	}
1065219b2ee8SDavid du Colombier }
1066219b2ee8SDavid du Colombier 
1067219b2ee8SDavid du Colombier int
newkeys(void)1068219b2ee8SDavid du Colombier newkeys(void)
1069219b2ee8SDavid du Colombier {
10709a747e4fSDavid du Colombier 	Dir *d;
1071219b2ee8SDavid du Colombier 	static long ftime;
1072219b2ee8SDavid du Colombier 
10739a747e4fSDavid du Colombier 	d = dirstat(userkeys);
10749a747e4fSDavid du Colombier 	if(d == nil)
1075219b2ee8SDavid du Colombier 		return 0;
10769a747e4fSDavid du Colombier 	if(d->mtime > ftime){
10779a747e4fSDavid du Colombier 		ftime = d->mtime;
10789a747e4fSDavid du Colombier 		free(d);
1079219b2ee8SDavid du Colombier 		return 1;
1080219b2ee8SDavid du Colombier 	}
10819a747e4fSDavid du Colombier 	free(d);
1082219b2ee8SDavid du Colombier 	return 0;
1083219b2ee8SDavid du Colombier }
1084219b2ee8SDavid du Colombier 
1085219b2ee8SDavid du Colombier void *
emalloc(ulong n)1086219b2ee8SDavid du Colombier emalloc(ulong n)
1087219b2ee8SDavid du Colombier {
1088219b2ee8SDavid du Colombier 	void *p;
1089219b2ee8SDavid du Colombier 
1090219b2ee8SDavid du Colombier 	if(p = malloc(n))
1091219b2ee8SDavid du Colombier 		return p;
1092219b2ee8SDavid du Colombier 	error("out of memory");
1093219b2ee8SDavid du Colombier 	return 0;		/* not reached */
1094219b2ee8SDavid du Colombier }
1095219b2ee8SDavid du Colombier 
1096219b2ee8SDavid du Colombier void
warning(void)1097219b2ee8SDavid du Colombier warning(void)
1098219b2ee8SDavid du Colombier {
10997dd7cddfSDavid du Colombier 	int i;
1100219b2ee8SDavid du Colombier 	char buf[64];
1101219b2ee8SDavid du Colombier 
1102219b2ee8SDavid du Colombier 	snprint(buf, sizeof buf, "-%s", warnarg);
11037dd7cddfSDavid du Colombier 	switch(rfork(RFPROC|RFNAMEG|RFNOTEG|RFNOWAIT|RFENVG|RFFDG)){
1104219b2ee8SDavid du Colombier 	case 0:
11057dd7cddfSDavid du Colombier 		i = open("/sys/log/auth", OWRITE);
11067dd7cddfSDavid du Colombier 		if(i >= 0){
11077dd7cddfSDavid du Colombier 			dup(i, 2);
11087dd7cddfSDavid du Colombier 			seek(2, 0, 2);
11097dd7cddfSDavid du Colombier 			close(i);
1110219b2ee8SDavid du Colombier 		}
1111f19e7b74SDavid du Colombier 		execl("/bin/auth/warning", "warning", warnarg, nil);
11127dd7cddfSDavid du Colombier 		error("can't exec warning");
11137dd7cddfSDavid du Colombier 	}
11147dd7cddfSDavid du Colombier }
1115