11 #include "authcmdlib.h"
13 #pragma varargck type "W" char*
19 typedef struct Fid Fid;
20 typedef struct User User;
36 MAXBAD = 10, /* max # of bad attempts before disabling the account */
37 /* file must be randomly addressible, so names have fixed length */
58 char secret[SECRETLEN];
59 ulong expire; /* 0 == never */
61 ulong bad; /* # of consecutive bad authentication attempts */
65 ulong purgatory; /* time purgatory ends */
80 [Qwarnings] "warnings",
83 char *status[Smax] = {
85 [Sdisabled] "disabled",
97 uchar mdata[8192 + IOHDRSZ];
98 int messagesize = sizeof mdata;
103 User *finduser(char*);
104 User *installuser(char*);
105 int removeuser(User*);
106 void insertuser(User*);
107 void writeusers(void);
109 void *emalloc(ulong);
110 char *estrdup(char*);
111 Qid mkqid(User*, ulong);
112 int dostat(User*, ulong, void*, int);
115 int weirdfmt(Fmt *f);
117 char *Auth(Fid*), *Attach(Fid*), *Version(Fid*),
118 *Flush(Fid*), *Walk(Fid*),
119 *Open(Fid*), *Create(Fid*),
120 *Read(Fid *), *Write(Fid*), *Clunk(Fid*),
121 *Remove(Fid *), *Stat(Fid*), *Wstat(Fid*);
122 char *(*fcalls[])(Fid*) = {
141 fprint(2, "usage: %s [-p] [-r] [-m mtpt] [-w warn] [keyfile]\n", argv0);
148 return memcmp(authkey.aes, zeros, 16) != 0;
152 main(int argc, char *argv[])
157 fmtinstall('W', weirdfmt);
161 mntpt = EARGF(usage());
167 warnarg = EARGF(usage());
178 userkeys = "/adm/keys";
185 error("can't make pipe: %r");
189 getpass(&authkey, nil, 0, 0);
191 if(!getauthkey(&authkey))
192 fprint(2, "keyfs: warning: can't read NVRAM\n");
196 if(!newkeys() || !readusers()){
198 keydbaes = haveaeskey();
199 else if(!haveaeskey()){
200 fprint(2, "keyfs: no aes key in NVRAM\n");
201 getpass(&authkey, nil, 0, 0);
206 switch(rfork(RFPROC|RFNAMEG|RFNOTEG|RFNOWAIT|RFENVG|RFFDG)){
215 if(mount(p[0], -1, mntpt, MREPL|MCREATE, "") < 0)
216 error("can't mount: %r");
231 return "keyfs: authentication not required";
242 thdr.qid = mkqid(f->user, f->qtype);
251 for(f = fids; f; f = f->next)
255 return "message size too small";
256 if(rhdr.msize > sizeof mdata)
257 thdr.msize = sizeof mdata;
259 thdr.msize = rhdr.msize;
260 messagesize = thdr.msize;
261 if(strncmp(rhdr.version, "9P2000", 6) != 0)
262 return "bad 9P version";
263 thdr.version = "9P2000";
277 return "walk of unused fid";
281 if(rhdr.fid != rhdr.newfid){
282 nf = findfid(rhdr.newfid);
291 for(; i<rhdr.nwname; i++){
293 err = "too many path name elements";
296 name = rhdr.wname[i];
299 if(strcmp(name, "..") == 0)
301 user = finduser(name);
307 thdr.wqid[i] = mkqid(user, qtype);
311 if(strcmp(name, "..") == 0) {
317 for(j = Quser + 1; j < Qmax; j++)
318 if(strcmp(name, qinfo[j]) == 0){
327 err = "file is not a directory";
332 if(i < rhdr.nwname && err == nil)
333 err = "file not found";
340 /* if we cloned and then completed the walk, update new fid */
341 if(rhdr.fid != rhdr.newfid && i == rhdr.nwname){
347 }else if(nf == nil && rhdr.nwname > 0){ /* walk without clone (rare) */
364 if(f->user != nil && --f->user->ref == 0 && f->user->removed) {
378 return "open of unused fid";
380 if(f->qtype == Quser && (mode & (OWRITE|OTRUNC)))
381 return "user already exists";
382 if((f->qtype == Qaeskey || f->qtype == Qpakhash) && !keydbaes)
383 return "keyfile not in aes format";
384 thdr.qid = mkqid(f->user, f->qtype);
385 thdr.iounit = messagesize - IOHDRSZ;
396 return "create of unused fid";
398 return "mounted read-only";
401 return "permission denied";
405 return "permission denied";
406 if(strcmp(name, "") == 0)
407 return "empty file name";
408 if(strlen(name) >= Namelen)
409 return "file name too long";
410 if(finduser(name) != nil)
411 return "user already exists";
412 f->user = installuser(name);
416 thdr.qid = mkqid(f->user, f->qtype);
417 thdr.iounit = messagesize - IOHDRSZ;
431 return "read of unused fid";
439 for(i = 0; i < Nuser; i++)
440 for(u = users[i]; u != nil; j += m, u = u->link){
441 m = dostat(u, Quser, data, n);
449 thdr.count = data - thdr.data;
455 for(i = 0; i < max; j += m, i++){
456 m = dostat(f->user, i + Quser + 1, data, n);
464 thdr.count = data - thdr.data;
470 if(f->user->status != Sok)
471 return "user disabled";
472 if(f->user->purgatory > (ulong)time(0))
473 return "user in purgatory";
474 if(f->user->expire != 0 && f->user->expire < (ulong)time(0))
475 return "user expired";
479 data = (char*)f->user->key.des;
483 data = (char*)f->user->key.aes;
487 data = (char*)f->user->key.pakhash;
491 data = f->user->secret;
504 if(data != thdr.data)
505 memmove(thdr.data, data, n);
509 if(f->user->status == Sok && f->user->expire && f->user->expire < (ulong)time(0))
510 sprint(data, "expired\n");
512 sprint(data, "%s\n", status[f->user->status]);
516 strcpy(data, "never\n");
518 sprint(data, "%lud\n", f->user->expire);
521 sprint(data, "%lud\n", f->user->bad);
524 sprint(data, "%ud\n", f->user->warnings);
527 return "permission denied: unknown qid";
539 return "permission denied";
541 return "mounted read-only";
547 return "garbled write data";
548 memmove(f->user->key.des, data, n);
553 return "garbled write data";
554 memmove(f->user->key.aes, data, n);
555 authpak_hash(&f->user->key, f->user->name);
560 return "garbled write data";
561 memmove(f->user->secret, data, n);
562 f->user->secret[n] = '\0';
567 if(p = strchr(data, '\n'))
569 for(i = 0; i < Smax; i++)
570 if(strcmp(data, status[i]) == 0){
575 return "unknown status";
581 if(p = strchr(data, '\n'))
585 if(strcmp(data, "never") == 0)
588 expire = strtoul(data, &data, 10);
590 return "bad expiration date";
592 f->user->expire = expire;
593 f->user->warnings = 0;
598 if(strcmp(data, "good") == 0)
602 if(f->user->bad && ((f->user->bad)%MAXBAD) == 0)
603 f->user->purgatory = (ulong)time(0) + f->user->bad;
607 f->user->warnings = strtoul(data, nil, 10);
613 return "permission denied";
623 return "permission denied";
626 return "mounted read-only";
628 if(f->qtype == Qwarnings)
629 f->user->warnings = 0;
630 else if(f->qtype == Quser)
634 return "permission denied";
644 static uchar statbuf[1024];
647 return "stat on unattached fid";
648 thdr.nstat = dostat(f->user, f->qtype, statbuf, sizeof statbuf);
649 if(thdr.nstat <= BIT16SZ)
650 return "stat buffer too small";
662 if(!f->busy || f->qtype != Quser)
663 return "permission denied";
665 return "mounted read-only";
666 if(rhdr.nstat > sizeof buf)
667 return "wstat buffer too big";
668 if(convM2D(rhdr.stat, rhdr.nstat, &d, buf) == 0)
669 return "bad stat buffer";
671 if(n == 0 || n >= Namelen)
672 return "bad user name";
674 return "user already exists";
675 if(!removeuser(f->user))
676 return "user previously removed";
678 f->user->name = estrdup(d.name);
685 mkqid(User *u, ulong qtype)
692 q.path |= u->uniq * 0x100;
693 if(qtype == Quser || qtype == Qroot)
701 dostat(User *user, ulong qtype, void *p, int n)
708 d.name = qinfo[qtype];
709 d.uid = d.gid = d.muid = "auth";
710 d.qid = mkqid(user, qtype);
711 if(d.qid.type & QTDIR)
715 d.atime = d.mtime = time(0);
717 return convD2M(&d, p, n);
723 int keydblen, keydboff;
730 fprint(2, "writeusers called while read-only; shouldn't happen\n");
734 /* what format to use */
738 keydblen += AESKEYLEN;
739 keydboff = 8+16; /* segnature[8] + iv[16] */
744 for(i = 0; i < Nuser; i++)
745 for(u = users[i]; u != nil; u = u->link)
748 /* pack into buffer */
749 buf = emalloc(keydboff + nu*keydblen);
751 genrandom(p, keydboff);
753 for(i = 0; i < Nuser; i++)
754 for(u = users[i]; u != nil; u = u->link){
755 strncpy((char*)p, u->name, Namelen);
757 memmove(p, u->key.des, DESKEYLEN);
766 memmove(p, u->secret, SECRETLEN);
769 memmove(p, u->key.aes, AESKEYLEN);
778 memmove(buf, "AES KEYS", 8);
779 setupAESstate(&s, authkey.aes, AESKEYLEN, zeros);
780 aesCBCencrypt(buf+8, (p - (buf+8)), &s);
785 des56to64((uchar*)authkey.des, key);
786 setupDESstate(&s, key, zeros);
787 desCBCencrypt(buf, p - buf, &s);
791 fd = create(userkeys, OWRITE, 0660);
793 fprint(2, "keyfs: can't write %s: %r\n", userkeys);
797 if(write(fd, buf, p - buf) != (p - buf))
798 fprint(2, "keyfs: can't write %s: %r\n", userkeys);
808 char *s, buf[ANAMELEN*4 + 1];
812 s = va_arg(f->args, char*);
814 for(i = 0; i < ANAMELEN; i += n){
815 n = chartorune(&r, s + i);
817 j += sprint(buf+j, "[%.2x]", buf[i]);
818 else if(isascii(r) && iscntrl(r))
819 j += sprint(buf+j, "[%.2x]", r);
820 else if(r == ' ' || r == '/')
821 j += sprint(buf+j, "[%c]", r);
823 j += sprint(buf+j, "%C", r);
825 return fmtstrcpy(f, buf);
829 userok(char *user, int nu)
833 char buf[ANAMELEN+1];
835 memset(buf, 0, sizeof buf);
836 memmove(buf, user, ANAMELEN);
838 if(buf[ANAMELEN-1] != 0){
839 fprint(2, "keyfs: %d: no termination: %W\n", nu, buf);
844 for(i = 0; buf[i]; i += n){
845 n = chartorune(&r, buf+i);
847 // fprint(2, "keyfs: name %W bad rune byte %d\n", buf, i);
849 } else if(isascii(r) && iscntrl(r) || r == ' ' || r == '/'){
850 // fprint(2, "keyfs: name %W bad char %C\n", buf, r);
856 fprint(2, "keyfs: %d: nil name\n", nu);
860 fprint(2, "keyfs: %d: bad syntax: %W\n", nu, buf);
867 int keydblen, keydboff;
873 /* read file into an array */
874 fd = open(userkeys, OREAD);
876 fprint(2, "keyfs: can't read %s: %r\n", userkeys);
884 buf = emalloc(d->length);
885 n = readn(fd, buf, d->length);
895 keydbaes = n > 24 && memcmp(buf, "AES KEYS", 8) == 0;
901 /* make sure we have AES encryption key */
906 keydblen += AESKEYLEN;
907 keydboff = 8+16; /* signature[8] + iv[16] */
908 setupAESstate(&s, authkey.aes, AESKEYLEN, zeros);
909 aesCBCdecrypt(buf+8, n-8, &s);
914 des56to64((uchar*)authkey.des, key);
915 setupDESstate(&s, key, zeros);
916 desCBCdecrypt(buf, n, &s);
921 n = (n - keydboff) / keydblen;
923 for(i = 0; i < n; ep += keydblen, i++){
924 if(userok((char*)ep, i) < 0)
926 u = finduser((char*)ep);
928 u = installuser((char*)ep);
929 memmove(u->key.des, ep + Namelen, DESKEYLEN);
930 p = ep + Namelen + DESKEYLEN;
933 if(u->status >= Smax)
934 fprint(2, "keyfs: warning: bad status in key file\n");
935 u->expire = p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24);
937 memmove(u->secret, p, SECRETLEN);
938 u->secret[SECRETLEN-1] = 0;
941 memmove(u->key.aes, p, AESKEYLEN);
942 authpak_hash(&u->key, u->name);
948 print("%d keys read in %s format\n", nu, keydbaes ? "AES" : "DES");
953 installuser(char *name)
959 u = emalloc(sizeof *u);
960 u->name = estrdup(name);
979 for(u = users[hash(name)]; u != nil; u = u->link)
980 if(strcmp(name, u->name) == 0)
986 removeuser(User *user)
993 last = &users[hash(name)];
994 for(u = *last; u != nil; u = *last){
995 if(strcmp(name, u->name) == 0){
1005 insertuser(User *user)
1010 h = hash(user->name);
1011 user->link = users[h];
1022 h = (h << 1) ^ *s++;
1032 for(f = fids; f != nil; f = f->next)
1035 else if(!ff && !f->busy)
1041 f = emalloc(sizeof *f);
1055 ulong now, lastwarning;
1057 /* after restart, let the system settle for 5 mins before warning */
1058 lastwarning = (ulong)time(0) - 24*60*60 + 5*60;
1060 while((n = read9pmsg(in, mdata, messagesize)) != 0){
1062 error("mount read: %r");
1063 if(convM2S(mdata, n, &rhdr) != n)
1064 error("convM2S format error: %r");
1069 thdr.data = (char*)mdata + IOHDRSZ;
1070 thdr.fid = rhdr.fid;
1071 if(!fcalls[rhdr.type])
1072 err = "fcall request";
1074 err = (*fcalls[rhdr.type])(findfid(rhdr.fid));
1075 thdr.tag = rhdr.tag;
1076 thdr.type = rhdr.type+1;
1081 n = convS2M(&thdr, mdata, messagesize);
1082 if(write(out, mdata, n) != n)
1083 error("mount write");
1086 if(warnarg && (long)(now - lastwarning) > 24*60*60){
1087 syslog(0, "auth", "keyfs starting warnings: %lux %lux",
1101 d = dirstat(userkeys);
1104 if(d->mtime > ftime){
1118 if((p = malloc(n)) != nil){
1122 error("out of memory");
1123 return nil; /* not reached */
1144 snprint(buf, sizeof buf, "-%s", warnarg);
1145 switch(rfork(RFPROC|RFNAMEG|RFNOTEG|RFNOWAIT|RFENVG|RFFDG)){
1147 i = open("/sys/log/auth", OWRITE);
1153 execl("/bin/auth/warning", "warning", warnarg, nil);
1154 error("can't exec warning");