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