xref: /plan9/sys/src/cmd/auth/keyfs.c (revision f54edc786b9c49b2c7ab1c0695cdc8c698b11f4d)
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
usage(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
main(int argc,char * argv[])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 *
Flush(Fid * f)195 Flush(Fid *f)
196 {
197 	USED(f);
198 	return 0;
199 }
200 
201 char *
Auth(Fid *)202 Auth(Fid *)
203 {
204 	return "keyfs: authentication not required";
205 }
206 
207 char *
Attach(Fid * f)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*
Version(Fid *)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 *
Walk(Fid * f)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 *
Clunk(Fid * f)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 *
Open(Fid * f)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 *
Create(Fid * f)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 *
Read(Fid * f)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 *
Write(Fid * f)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 *
Remove(Fid * f)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 *
Stat(Fid * f)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 *
Wstat(Fid * f)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
mkqid(User * u,ulong qtype)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
dostat(User * user,ulong qtype,void * p,int n)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
passline(Biobuf * b,void * vbuf)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
randombytes(uchar * p,int len)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
oldCBCencrypt(char * key7,uchar * p,int len)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
oldCBCdecrypt(char * key7,uchar * p,int len)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
writeusers(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
weirdfmt(Fmt * f)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
userok(char * user,int nu)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
readusers(void)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 *
installuser(char * name)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 *
finduser(char * name)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
removeuser(User * user)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
insertuser(User * user)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
hash(char * s)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 *
findfid(int 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
io(int in,int out)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
newkeys(void)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 *
emalloc(ulong n)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
warning(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 }
1115