Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * keyfs
3
 */
4
#include <u.h>
5
#include <libc.h>
6
#include <ctype.h>
7
#include <authsrv.h>
8
#include <fcall.h>
9
#include <bio.h>
10
#include <mp.h>
11
#include <libsec.h>
12
#include "authcmdlib.h"
13
 
14
#pragma	varargck	type	"W"	char*
15
 
16
char authkey[8];
17
 
18
typedef struct Fid	Fid;
19
typedef struct User	User;
20
 
21
enum {
22
	Qroot,
23
	Quser,
24
	Qkey,
25
	Qsecret,
26
	Qlog,
27
	Qstatus,
28
	Qexpire,
29
	Qwarnings,
30
	Qmax,
31
 
32
	Nuser	= 512,
33
	MAXBAD	= 10,	/* max # of bad attempts before disabling the account */
34
	/* file must be randomly addressible, so names have fixed length */
35
	Namelen	= ANAMELEN,
36
};
37
 
38
enum {
39
	Sok,
40
	Sdisabled,
41
	Smax,
42
};
43
 
44
struct Fid {
45
	int	fid;
46
	ulong	qtype;
47
	User	*user;
48
	int	busy;
49
	Fid	*next;
50
};
51
 
52
struct User {
53
	char	*name;
54
	char	key[DESKEYLEN];
55
	char	secret[SECRETLEN];
56
	ulong	expire;			/* 0 == never */
57
	uchar	status;
58
	ulong	bad;		/* # of consecutive bad authentication attempts */
59
	int	ref;
60
	char	removed;
61
	uchar	warnings;
62
	long	purgatory;		/* time purgatory ends */
63
	ulong	uniq;
64
	User	*link;
65
};
66
 
67
char	*qinfo[Qmax] = {
68
	[Qroot]		"keys",
69
	[Quser]		".",
70
	[Qkey]		"key",
71
	[Qsecret]	"secret",
72
	[Qlog]		"log",
73
	[Qexpire]	"expire",
74
	[Qstatus]	"status",
75
	[Qwarnings]	"warnings",
76
};
77
 
78
char	*status[Smax] = {
79
	[Sok]		"ok",
80
	[Sdisabled]	"disabled",
81
};
82
 
83
Fid	*fids;
84
User	*users[Nuser];
85
char	*userkeys;
86
int	nuser;
87
ulong	uniq = 1;
88
Fcall	rhdr,
89
	thdr;
90
int	usepass;
91
char	*warnarg;
92
uchar	mdata[8192 + IOHDRSZ];
93
int	messagesize = sizeof mdata;
94
 
95
int	readusers(void);
96
ulong	hash(char*);
97
Fid	*findfid(int);
98
User	*finduser(char*);
99
User	*installuser(char*);
100
int	removeuser(User*);
101
void	insertuser(User*);
102
void	writeusers(void);
103
void	io(int, int);
104
void	*emalloc(ulong);
105
Qid	mkqid(User*, ulong);
106
int	dostat(User*, ulong, void*, int);
107
int	newkeys(void);
108
void	warning(void);
109
int	weirdfmt(Fmt *f);
110
 
111
char	*Auth(Fid*), *Attach(Fid*), *Version(Fid*),
112
	*Flush(Fid*), *Walk(Fid*),
113
	*Open(Fid*), *Create(Fid*),
114
	*Read(Fid *), *Write(Fid*), *Clunk(Fid*),
115
	*Remove(Fid *), *Stat(Fid*), *Wstat(Fid*);
116
char 	*(*fcalls[])(Fid*) = {
117
	[Tattach]	Attach,
118
	[Tauth]	Auth,
119
	[Tclunk]	Clunk,
120
	[Tcreate]	Create,
121
	[Tflush]	Flush,
122
	[Topen]		Open,
123
	[Tread]		Read,
124
	[Tremove]	Remove,
125
	[Tstat]		Stat,
126
	[Tversion]	Version,
127
	[Twalk]		Walk,
128
	[Twrite]	Write,
129
	[Twstat]	Wstat,
130
};
131
 
132
static void
133
usage(void)
134
{
135
	fprint(2, "usage: %s [-p] [-m mtpt] [-w warn] [keyfile]\n", argv0);
136
	exits("usage");
137
}
138
 
139
void
140
main(int argc, char *argv[])
141
{
142
	char *mntpt;
143
	int p[2];
144
 
145
	fmtinstall('W', weirdfmt);
146
	mntpt = "/mnt/keys";
147
	ARGBEGIN{
148
	case 'm':
149
		mntpt = EARGF(usage());
150
		break;
151
	case 'p':
152
		usepass = 1;
153
		break;
154
	case 'w':
155
		warnarg = EARGF(usage());
156
		break;
157
	default:
158
		usage();
159
		break;
160
	}ARGEND
161
	argv0 = "keyfs";
162
 
163
	userkeys = "/adm/keys";
164
	if(argc > 1)
165
		usage();
166
	if(argc == 1)
167
		userkeys = argv[0];
168
 
169
	if(pipe(p) < 0)
170
		error("can't make pipe: %r");
171
 
172
	if(usepass) {
173
		getpass(authkey, nil, 0, 0);
174
	} else {
175
		if(!getauthkey(authkey))
176
			print("keyfs: warning: can't read NVRAM\n");
177
	}
178
 
179
	switch(rfork(RFPROC|RFNAMEG|RFNOTEG|RFNOWAIT|RFENVG|RFFDG)){
180
	case 0:
181
		close(p[0]);
182
		io(p[1], p[1]);
183
		exits(0);
184
	case -1:
185
		error("fork");
186
	default:
187
		close(p[1]);
188
		if(mount(p[0], -1, mntpt, MREPL|MCREATE, "") < 0)
189
			error("can't mount: %r");
190
		exits(0);
191
	}
192
}
193
 
194
char *
195
Flush(Fid *f)
196
{
197
	USED(f);
198
	return 0;
199
}
200
 
201
char *
202
Auth(Fid *)
203
{
204
	return "keyfs: authentication not required";
205
}
206
 
207
char *
208
Attach(Fid *f)
209
{
210
	if(f->busy)
211
		Clunk(f);
212
	f->user = 0;
213
	f->qtype = Qroot;
214
	f->busy = 1;
215
	thdr.qid = mkqid(f->user, f->qtype);
216
	return 0;
217
}
218
 
219
char*
220
Version(Fid*)
221
{
222
	Fid *f;
223
 
224
	for(f = fids; f; f = f->next)
225
		if(f->busy)
226
			Clunk(f);
227
	if(rhdr.msize > sizeof mdata)
228
		thdr.msize = sizeof mdata;
229
	else
230
		thdr.msize = rhdr.msize;
231
	messagesize = thdr.msize;
232
	if(strncmp(rhdr.version, "9P2000", 6) != 0)
233
		return "bad 9P version";
234
	thdr.version = "9P2000";
235
	return 0;
236
}
237
 
238
char *
239
Walk(Fid *f)
240
{
241
	char *name, *err;
242
	int i, j, max;
243
	Fid *nf;
244
	ulong qtype;
245
	User *user;
246
 
247
	if(!f->busy)
248
		return "walk of unused fid";
249
	nf = nil;
250
	qtype = f->qtype;
251
	user = f->user;
252
	if(rhdr.fid != rhdr.newfid){
253
		nf = findfid(rhdr.newfid);
254
		if(nf->busy)
255
			return "fid in use";
256
		f = nf;	/* walk f */
257
	}
258
 
259
	err = nil;
260
	i = 0;
261
	if(rhdr.nwname > 0){
262
		for(; i<rhdr.nwname; i++){
263
			if(i >= MAXWELEM){
264
				err = "too many path name elements";
265
				break;
266
			}
267
			name = rhdr.wname[i];
268
			switch(qtype){
269
			case Qroot:
270
				if(strcmp(name, "..") == 0)
271
					goto Accept;
272
				user = finduser(name);
273
				if(!user)
274
					goto Out;
275
				qtype = Quser;
276
 
277
			Accept:
278
				thdr.wqid[i] = mkqid(user, qtype);
279
				break;
280
 
281
			case Quser:
282
				if(strcmp(name, "..") == 0) {
283
					qtype = Qroot;
284
					user = 0;
285
					goto Accept;
286
				}
287
				max = Qmax;
288
				for(j = Quser + 1; j < Qmax; j++)
289
					if(strcmp(name, qinfo[j]) == 0){
290
						qtype = j;
291
						break;
292
					}
293
				if(j < max)
294
					goto Accept;
295
				goto Out;
296
 
297
			default:
298
				err = "file is not a directory";
299
				goto Out;
300
			}
301
		}
302
	    Out:
303
		if(i < rhdr.nwname && err == nil)
304
			err = "file not found";
305
	}
306
 
307
	if(err != nil){
308
		return err;
309
	}
310
 
311
	/* if we cloned and then completed the walk, update new fid */
312
	if(rhdr.fid != rhdr.newfid && i == rhdr.nwname){
313
		nf->busy = 1;
314
		nf->qtype = qtype;
315
		if(nf->user = user)
316
			nf->user->ref++;
317
	}else if(nf == nil && rhdr.nwname > 0){	/* walk without clone (rare) */
318
		Clunk(f);
319
		f->busy = 1;
320
		f->qtype = qtype;
321
		if(f->user = user)
322
			f->user->ref++;
323
	}
324
 
325
	thdr.nwqid = i;
326
	return 0;
327
}
328
 
329
char *
330
Clunk(Fid *f)
331
{
332
	f->busy = 0;
333
	if(f->user && --f->user->ref == 0 && f->user->removed) {
334
		free(f->user->name);
335
		free(f->user);
336
	}
337
	f->user = 0;
338
	return 0;
339
}
340
 
341
char *
342
Open(Fid *f)
343
{
344
	int mode;
345
 
346
	if(!f->busy)
347
		return "open of unused fid";
348
	mode = rhdr.mode;
349
	if(f->qtype == Quser && (mode & (OWRITE|OTRUNC)))
350
		return "user already exists";
351
	thdr.qid = mkqid(f->user, f->qtype);
352
	thdr.iounit = messagesize - IOHDRSZ;
353
	return 0;
354
}
355
 
356
char *
357
Create(Fid *f)
358
{
359
	char *name;
360
	long perm;
361
 
362
	if(!f->busy)
363
		return "create of unused fid";
364
	name = rhdr.name;
365
	if(f->user){
366
		return "permission denied";
367
	}else{
368
		perm = rhdr.perm;
369
		if(!(perm & DMDIR))
370
			return "permission denied";
371
		if(strcmp(name, "") == 0)
372
			return "empty file name";
373
		if(strlen(name) >= Namelen)
374
			return "file name too long";
375
		if(finduser(name))
376
			return "user already exists";
377
		f->user = installuser(name);
378
		f->user->ref++;
379
		f->qtype = Quser;
380
	}
381
	thdr.qid = mkqid(f->user, f->qtype);
382
	thdr.iounit = messagesize - IOHDRSZ;
383
	writeusers();
384
	return 0;
385
}
386
 
387
char *
388
Read(Fid *f)
389
{
390
	User *u;
391
	char *data;
392
	ulong off, n, m;
393
	int i, j, max;
394
 
395
	if(!f->busy)
396
		return "read of unused fid";
397
	n = rhdr.count;
398
	off = rhdr.offset;
399
	thdr.count = 0;
400
	data = thdr.data;
401
	switch(f->qtype){
402
	case Qroot:
403
		j = 0;
404
		for(i = 0; i < Nuser; i++)
405
			for(u = users[i]; u; j += m, u = u->link){
406
				m = dostat(u, Quser, data, n);
407
				if(m <= BIT16SZ)
408
					break;
409
				if(j < off)
410
					continue;
411
				data += m;
412
				n -= m;
413
			}
414
		thdr.count = data - thdr.data;
415
		return 0;
416
	case Quser:
417
		max = Qmax;
418
		max -= Quser + 1;
419
		j = 0;
420
		for(i = 0; i < max; j += m, i++){
421
			m = dostat(f->user, i + Quser + 1, data, n);
422
			if(m <= BIT16SZ)
423
				break;
424
			if(j < off)
425
				continue;
426
			data += m;
427
			n -= m;
428
		}
429
		thdr.count = data - thdr.data;
430
		return 0;
431
	case Qkey:
432
		if(f->user->status != Sok)
433
			return "user disabled";
434
		if(f->user->purgatory > time(0))
435
			return "user in purgatory";
436
		if(f->user->expire != 0 && f->user->expire < time(0))
437
			return "user expired";
438
		if(off != 0)
439
			return 0;
440
		if(n > DESKEYLEN)
441
			n = DESKEYLEN;
442
		memmove(thdr.data, f->user->key, n);
443
		thdr.count = n;
444
		return 0;
445
	case Qsecret:
446
		if(f->user->status != Sok)
447
			return "user disabled";
448
		if(f->user->purgatory > time(0))
449
			return "user in purgatory";
450
		if(f->user->expire != 0 && f->user->expire < time(0))
451
			return "user expired";
452
		if(off != 0)
453
			return 0;
454
		if(n > strlen(f->user->secret))
455
			n = strlen(f->user->secret);
456
		memmove(thdr.data, f->user->secret, n);
457
		thdr.count = n;
458
		return 0;
459
	case Qstatus:
460
		if(off != 0){
461
			thdr.count = 0;
462
			return 0;
463
		}
464
		if(f->user->status == Sok && f->user->expire && f->user->expire < time(0))
465
			sprint(thdr.data, "expired\n");
466
		else
467
			sprint(thdr.data, "%s\n", status[f->user->status]);
468
		thdr.count = strlen(thdr.data);
469
		return 0;
470
	case Qexpire:
471
		if(off != 0){
472
			thdr.count = 0;
473
			return 0;
474
		}
475
		if(!f->user->expire)
476
			strcpy(data, "never\n");
477
		else
478
			sprint(data, "%lud\n", f->user->expire);
479
		if(n > strlen(data))
480
			n = strlen(data);
481
		thdr.count = n;
482
		return 0;
483
	case Qlog:
484
		if(off != 0){
485
			thdr.count = 0;
486
			return 0;
487
		}
488
		sprint(data, "%lud\n", f->user->bad);
489
		if(n > strlen(data))
490
			n = strlen(data);
491
		thdr.count = n;
492
		return 0;
493
	case Qwarnings:
494
		if(off != 0){
495
			thdr.count = 0;
496
			return 0;
497
		}
498
		sprint(data, "%ud\n", f->user->warnings);
499
		if(n > strlen(data))
500
			n = strlen(data);
501
		thdr.count = n;
502
		return 0;
503
	default:
504
		return "permission denied: unknown qid";
505
	}
506
}
507
 
508
char *
509
Write(Fid *f)
510
{
511
	char *data, *p;
512
	ulong n, expire;
513
	int i;
514
 
515
	if(!f->busy)
516
		return "permission denied";
517
	n = rhdr.count;
518
	data = rhdr.data;
519
	switch(f->qtype){
520
	case Qkey:
521
		if(n != DESKEYLEN)
522
			return "garbled write data";
523
		memmove(f->user->key, data, DESKEYLEN);
524
		thdr.count = DESKEYLEN;
525
		break;
526
	case Qsecret:
527
		if(n >= SECRETLEN)
528
			return "garbled write data";
529
		memmove(f->user->secret, data, n);
530
		f->user->secret[n] = 0;
531
		thdr.count = n;
532
		break;
533
	case Qstatus:
534
		data[n] = '\0';
535
		if(p = strchr(data, '\n'))
536
			*p = '\0';
537
		for(i = 0; i < Smax; i++)
538
			if(strcmp(data, status[i]) == 0){
539
				f->user->status = i;
540
				break;
541
			}
542
		if(i == Smax)
543
			return "unknown status";
544
		f->user->bad = 0;
545
		thdr.count = n;
546
		break;
547
	case Qexpire:
548
		data[n] = '\0';
549
		if(p = strchr(data, '\n'))
550
			*p = '\0';
551
		else
552
			p = &data[n];
553
		if(strcmp(data, "never") == 0)
554
			expire = 0;
555
		else{
556
			expire = strtoul(data, &data, 10);
557
			if(data != p)
558
				return "bad expiration date";
559
		}
560
		f->user->expire = expire;
561
		f->user->warnings = 0;
562
		thdr.count = n;
563
		break;
564
	case Qlog:
565
		data[n] = '\0';
566
		if(strcmp(data, "good") == 0)
567
			f->user->bad = 0;
568
		else
569
			f->user->bad++;
570
		if(f->user->bad && ((f->user->bad)%MAXBAD) == 0)
571
			f->user->purgatory = time(0) + f->user->bad;
572
		return 0;
573
	case Qwarnings:
574
		data[n] = '\0';
575
		f->user->warnings = strtoul(data, 0, 10);
576
		thdr.count = n;
577
		break;
578
	case Qroot:
579
	case Quser:
580
	default:
581
		return "permission denied";
582
	}
583
	writeusers();
584
	return 0;
585
}
586
 
587
char *
588
Remove(Fid *f)
589
{
590
	if(!f->busy)
591
		return "permission denied";
592
	if(f->qtype == Qwarnings)
593
		f->user->warnings = 0;
594
	else if(f->qtype == Quser)
595
		removeuser(f->user);
596
	else {
597
		Clunk(f);
598
		return "permission denied";
599
	}
600
	Clunk(f);
601
	writeusers();
602
	return 0;
603
}
604
 
605
char *
606
Stat(Fid *f)
607
{
608
	static uchar statbuf[1024];
609
 
610
	if(!f->busy)
611
		return "stat on unattached fid";
612
	thdr.nstat = dostat(f->user, f->qtype, statbuf, sizeof statbuf);
613
	if(thdr.nstat <= BIT16SZ)
614
		return "stat buffer too small";
615
	thdr.stat = statbuf;
616
	return 0;
617
}
618
 
619
char *
620
Wstat(Fid *f)
621
{
622
	Dir d;
623
	int n;
624
	char buf[1024];
625
 
626
	if(!f->busy || f->qtype != Quser)
627
		return "permission denied";
628
	if(rhdr.nstat > sizeof buf)
629
		return "wstat buffer too big";
630
	if(convM2D(rhdr.stat, rhdr.nstat, &d, buf) == 0)
631
		return "bad stat buffer";
632
	n = strlen(d.name);
633
	if(n == 0 || n >= Namelen)
634
		return "bad user name";
635
	if(finduser(d.name))
636
		return "user already exists";
637
	if(!removeuser(f->user))
638
		return "user previously removed";
639
	free(f->user->name);
640
	f->user->name = strdup(d.name);
641
	if(f->user->name == nil)
642
		error("wstat: malloc failed: %r");
643
	insertuser(f->user);
644
	writeusers();
645
	return 0;
646
}
647
 
648
Qid
649
mkqid(User *u, ulong qtype)
650
{
651
	Qid q;
652
 
653
	q.vers = 0;
654
	q.path = qtype;
655
	if(u)
656
		q.path |= u->uniq * 0x100;
657
	if(qtype == Quser || qtype == Qroot)
658
		q.type = QTDIR;
659
	else
660
		q.type = QTFILE;
661
	return q;
662
}
663
 
664
int
665
dostat(User *user, ulong qtype, void *p, int n)
666
{
667
	Dir d;
668
 
669
	if(qtype == Quser)
670
		d.name = user->name;
671
	else
672
		d.name = qinfo[qtype];
673
	d.uid = d.gid = d.muid = "auth";
674
	d.qid = mkqid(user, qtype);
675
	if(d.qid.type & QTDIR)
676
		d.mode = 0777|DMDIR;
677
	else
678
		d.mode = 0666;
679
	d.atime = d.mtime = time(0);
680
	d.length = 0;
681
	return convD2M(&d, p, n);
682
}
683
 
684
int
685
passline(Biobuf *b, void *vbuf)
686
{
687
	char *buf = vbuf;
688
 
689
	if(Bread(b, buf, KEYDBLEN) != KEYDBLEN)
690
		return 0;
691
	decrypt(authkey, buf, KEYDBLEN);
692
	buf[Namelen-1] = '\0';
693
	return 1;
694
}
695
 
696
void
697
randombytes(uchar *p, int len)
698
{
699
	int i, fd;
700
 
701
	fd = open("/dev/random", OREAD);
702
	if(fd < 0){
703
		fprint(2, "keyfs: can't open /dev/random, using rand()\n");
704
		srand(time(0));
705
		for(i = 0; i < len; i++)
706
			p[i] = rand();
707
		return;
708
	}
709
	read(fd, p, len);
710
	close(fd);
711
}
712
 
713
void
714
oldCBCencrypt(char *key7, uchar *p, int len)
715
{
716
	uchar ivec[8];
717
	uchar key[8];
718
	DESstate s;
719
 
720
	memset(ivec, 0, 8);
721
	des56to64((uchar*)key7, key);
722
	setupDESstate(&s, key, ivec);
723
	desCBCencrypt((uchar*)p, len, &s);
724
}
725
 
726
void
727
oldCBCdecrypt(char *key7, uchar *p, int len)
728
{
729
	uchar ivec[8];
730
	uchar key[8];
731
	DESstate s;
732
 
733
	memset(ivec, 0, 8);
734
	des56to64((uchar*)key7, key);
735
	setupDESstate(&s, key, ivec);
736
	desCBCdecrypt((uchar*)p, len, &s);
737
 
738
}
739
 
740
void
741
writeusers(void)
742
{
743
	int fd, i, nu;
744
	User *u;
745
	uchar *p, *buf;
746
	ulong expire;
747
 
748
	/* count users */
749
	nu = 0;
750
	for(i = 0; i < Nuser; i++)
751
		for(u = users[i]; u; u = u->link)
752
			nu++;
753
 
754
	/* pack into buffer */
755
	buf = malloc(KEYDBOFF + nu*KEYDBLEN);
756
	if(buf == 0){
757
		fprint(2, "keyfs: can't write keys file, out of memory\n");
758
		return;
759
	}
760
	p = buf;
761
	randombytes(p, KEYDBOFF);
762
	p += KEYDBOFF;
763
	for(i = 0; i < Nuser; i++)
764
		for(u = users[i]; u; u = u->link){
765
			strncpy((char*)p, u->name, Namelen);
766
			p += Namelen;
767
			memmove(p, u->key, DESKEYLEN);
768
			p += DESKEYLEN;
769
			*p++ = u->status;
770
			*p++ = u->warnings;
771
			expire = u->expire;
772
			*p++ = expire;
773
			*p++ = expire >> 8;
774
			*p++ = expire >> 16;
775
			*p++ = expire >> 24;
776
			memmove(p, u->secret, SECRETLEN);
777
			p += SECRETLEN;
778
		}
779
 
780
	/* encrypt */
781
	oldCBCencrypt(authkey, buf, p - buf);
782
 
783
	/* write file */
784
	fd = create(userkeys, OWRITE, 0660);
785
	if(fd < 0){
786
		free(buf);
787
		fprint(2, "keyfs: can't write keys file\n");
788
		return;
789
	}
790
	if(write(fd, buf, p - buf) != (p - buf))
791
		fprint(2, "keyfs: can't write keys file\n");
792
 
793
	free(buf);
794
	close(fd);
795
}
796
 
797
int
798
weirdfmt(Fmt *f)
799
{
800
	char *s, *p, *ep, buf[ANAMELEN*4 + 1];
801
	int i, n;
802
	Rune r;
803
 
804
	s = va_arg(f->args, char*);
805
	p = buf;
806
	ep = buf + sizeof buf;
807
	for(i = 0; i < ANAMELEN; i += n){
808
		n = chartorune(&r, s + i);
809
		if(r == Runeerror)
810
			p = seprint(p, ep, "[%.2x]", buf[i]);
811
		else if(isascii(r) && iscntrl(r))
812
			p = seprint(p, ep, "[%.2x]", r);
813
		else if(r == ' ' || r == '/')
814
			p = seprint(p, ep, "[%c]", r);
815
		else
816
			p = seprint(p, ep, "%C", r);
817
	}
818
	return fmtstrcpy(f, buf);
819
}
820
 
821
int
822
userok(char *user, int nu)
823
{
824
	int i, n, rv;
825
	Rune r;
826
	char buf[ANAMELEN+1];
827
 
828
	memset(buf, 0, sizeof buf);
829
	memmove(buf, user, ANAMELEN);
830
 
831
	if(buf[ANAMELEN-1] != 0){
832
		fprint(2, "keyfs: %d: no termination: %W\n", nu, buf);
833
		return -1;
834
	}
835
 
836
	rv = 0;
837
	for(i = 0; buf[i]; i += n){
838
		n = chartorune(&r, buf+i);
839
		if(r == Runeerror){
840
//			fprint(2, "keyfs: name %W bad rune byte %d\n", buf, i);
841
			rv = -1;
842
		} else if(isascii(r) && iscntrl(r) || r == ' ' || r == '/'){
843
//			fprint(2, "keyfs: name %W bad char %C\n", buf, r);
844
			rv = -1;
845
		}
846
	}
847
 
848
	if(i == 0){
849
		fprint(2, "keyfs: %d: nil name\n", nu);
850
		return -1;
851
	}
852
	if(rv == -1)
853
		fprint(2, "keyfs: %d: bad syntax: %W\n", nu, buf);
854
	return rv;
855
}
856
 
857
int
858
readusers(void)
859
{
860
	int fd, i, n, nu;
861
	uchar *p, *buf, *ep;
862
	User *u;
863
	Dir *d;
864
 
865
	/* read file into an array */
866
	fd = open(userkeys, OREAD);
867
	if(fd < 0)
868
		return 0;
869
	d = dirfstat(fd);
870
	if(d == nil){
871
		close(fd);
872
		return 0;
873
	}
874
	buf = malloc(d->length);
875
	if(buf == 0){
876
		close(fd);
877
		free(d);
878
		return 0;
879
	}
880
	n = readn(fd, buf, d->length);
881
	close(fd);
882
	free(d);
883
	if(n != d->length){
884
		free(buf);
885
		return 0;
886
	}
887
 
888
	/* decrypt */
889
	n -= n % KEYDBLEN;
890
	oldCBCdecrypt(authkey, buf, n);
891
 
892
	/* unpack */
893
	nu = 0;
894
	for(i = KEYDBOFF; i < n; i += KEYDBLEN){
895
		ep = buf + i;
896
		if(userok((char*)ep, i/KEYDBLEN) < 0)
897
			continue;
898
		u = finduser((char*)ep);
899
		if(u == 0)
900
			u = installuser((char*)ep);
901
		memmove(u->key, ep + Namelen, DESKEYLEN);
902
		p = ep + Namelen + DESKEYLEN;
903
		u->status = *p++;
904
		u->warnings = *p++;
905
		if(u->status >= Smax)
906
			fprint(2, "keyfs: warning: bad status in key file\n");
907
		u->expire = p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24);
908
		p += 4;
909
		memmove(u->secret, p, SECRETLEN);
910
		u->secret[SECRETLEN-1] = 0;
911
		nu++;
912
	}
913
	free(buf);
914
 
915
	print("%d keys read\n", nu);
916
	return 1;
917
}
918
 
919
User *
920
installuser(char *name)
921
{
922
	User *u;
923
	int h;
924
 
925
	h = hash(name);
926
	u = emalloc(sizeof *u);
927
	u->name = strdup(name);
928
	if(u->name == nil)
929
		error("malloc failed: %r");
930
	u->removed = 0;
931
	u->ref = 0;
932
	u->purgatory = 0;
933
	u->expire = 0;
934
	u->status = Sok;
935
	u->bad = 0;
936
	u->warnings = 0;
937
	u->uniq = uniq++;
938
	u->link = users[h];
939
	users[h] = u;
940
	return u;
941
}
942
 
943
User *
944
finduser(char *name)
945
{
946
	User *u;
947
 
948
	for(u = users[hash(name)]; u; u = u->link)
949
		if(strcmp(name, u->name) == 0)
950
			return u;
951
	return 0;
952
}
953
 
954
int
955
removeuser(User *user)
956
{
957
	User *u, **last;
958
	char *name;
959
 
960
	user->removed = 1;
961
	name = user->name;
962
	last = &users[hash(name)];
963
	for(u = *last; u; u = *last){
964
		if(strcmp(name, u->name) == 0){
965
			*last = u->link;
966
			return 1;
967
		}
968
		last = &u->link;
969
	}
970
	return 0;
971
}
972
 
973
void
974
insertuser(User *user)
975
{
976
	int h;
977
 
978
	user->removed = 0;
979
	h = hash(user->name);
980
	user->link = users[h];
981
	users[h] = user;
982
}
983
 
984
ulong
985
hash(char *s)
986
{
987
	ulong h;
988
 
989
	h = 0;
990
	while(*s)
991
		h = (h << 1) ^ *s++;
992
	return h % Nuser;
993
}
994
 
995
Fid *
996
findfid(int fid)
997
{
998
	Fid *f, *ff;
999
 
1000
	ff = 0;
1001
	for(f = fids; f; f = f->next)
1002
		if(f->fid == fid)
1003
			return f;
1004
		else if(!ff && !f->busy)
1005
			ff = f;
1006
	if(ff){
1007
		ff->fid = fid;
1008
		return ff;
1009
	}
1010
	f = emalloc(sizeof *f);
1011
	f->fid = fid;
1012
	f->busy = 0;
1013
	f->user = 0;
1014
	f->next = fids;
1015
	fids = f;
1016
	return f;
1017
}
1018
 
1019
void
1020
io(int in, int out)
1021
{
1022
	char *err;
1023
	int n;
1024
	long now, lastwarning;
1025
 
1026
	/* after restart, let the system settle for 5 mins before warning */
1027
	lastwarning = time(0) - 24*60*60 + 5*60;
1028
 
1029
	for(;;){
1030
		n = read9pmsg(in, mdata, messagesize);
1031
		if(n == 0)
1032
			continue;
1033
		if(n < 0)
1034
			error("mount read %d", n);
1035
		if(convM2S(mdata, n, &rhdr) == 0)
1036
			continue;
1037
 
1038
		if(newkeys())
1039
			readusers();
1040
 
1041
		thdr.data = (char*)mdata + IOHDRSZ;
1042
		thdr.fid = rhdr.fid;
1043
		if(!fcalls[rhdr.type])
1044
			err = "fcall request";
1045
		else
1046
			err = (*fcalls[rhdr.type])(findfid(rhdr.fid));
1047
		thdr.tag = rhdr.tag;
1048
		thdr.type = rhdr.type+1;
1049
		if(err){
1050
			thdr.type = Rerror;
1051
			thdr.ename = err;
1052
		}
1053
		n = convS2M(&thdr, mdata, messagesize);
1054
		if(write(out, mdata, n) != n)
1055
			error("mount write");
1056
 
1057
		now = time(0);
1058
		if(warnarg && (now - lastwarning > 24*60*60)){
1059
			syslog(0, "auth", "keyfs starting warnings: %lux %lux",
1060
				now, lastwarning);
1061
			warning();
1062
			lastwarning = now;
1063
		}
1064
	}
1065
}
1066
 
1067
int
1068
newkeys(void)
1069
{
1070
	Dir *d;
1071
	static long ftime;
1072
 
1073
	d = dirstat(userkeys);
1074
	if(d == nil)
1075
		return 0;
1076
	if(d->mtime > ftime){
1077
		ftime = d->mtime;
1078
		free(d);
1079
		return 1;
1080
	}
1081
	free(d);
1082
	return 0;
1083
}
1084
 
1085
void *
1086
emalloc(ulong n)
1087
{
1088
	void *p;
1089
 
1090
	if(p = malloc(n))
1091
		return p;
1092
	error("out of memory");
1093
	return 0;		/* not reached */
1094
}
1095
 
1096
void
1097
warning(void)
1098
{
1099
	int i;
1100
	char buf[64];
1101
 
1102
	snprint(buf, sizeof buf, "-%s", warnarg);
1103
	switch(rfork(RFPROC|RFNAMEG|RFNOTEG|RFNOWAIT|RFENVG|RFFDG)){
1104
	case 0:
1105
		i = open("/sys/log/auth", OWRITE);
1106
		if(i >= 0){
1107
			dup(i, 2);
1108
			seek(2, 0, 2);
1109
			close(i);
1110
		}
1111
		execl("/bin/auth/warning", "warning", warnarg, nil);
1112
		error("can't exec warning");
1113
	}
1114
}