8 #include "authcmdlib.h"
14 typedef struct Keyslot Keyslot;
20 Keyslot hkey, akey, ukey;
21 uchar keyseed[SHA2_256dlen];
25 /* Microsoft auth constants */
34 void ticketrequest(Ticketreq*);
35 void challengebox(Ticketreq*);
36 void changepasswd(Ticketreq*);
37 void apop(Ticketreq*, int);
38 void chap(Ticketreq*);
39 void ntlm(Ticketreq*);
40 void mschap(Ticketreq*, int);
42 int speaksfor(char*, char*);
43 void replyerror(char*, ...);
45 void initkeyseed(void);
46 void mkkey(char*, Authkey*);
47 void mkticket(Ticketreq*, Ticket*);
48 void nthash(uchar hash[MShashlen], char *passwd);
49 void lmhash(uchar hash[MShashlen], char *passwd);
50 void ntv2hash(uchar hash[MShashlen], char *passwd, char *user, char *dom);
51 void mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]);
52 void desencrypt(uchar data[8], uchar key[7]);
53 void tickauthreply(Ticketreq*, Authkey*);
54 void tickauthreply2(Ticketreq*, Authkey*, uchar *, int, uchar *, int);
55 void safecpy(char*, char*, int);
56 void catch(void*, char*);
59 main(int argc, char *argv[])
71 strcpy(raddr, "unknown");
73 getraddr(argv[argc-1]);
75 alarm(10*60*1000); /* kill a connection after 10 minutes */
80 db = ndbopen("/lib/ndb/auth");
82 syslog(0, AUTHLOG, "no /lib/ndb/auth");
85 n = readn(0, buf, sizeof(buf));
86 if(n <= 0 || convM2TR(buf, n, &tr) <= 0)
105 mschap(&tr, MSchallen);
108 mschap(&tr, MSchallenv2);
123 syslog(0, AUTHLOG, "unknown ticket request type: %d", tr.type);
126 /* invalidate pak keys */
135 pak1(char *u, Keyslot *k)
140 safecpy(k->id, u, sizeof(k->id));
141 if(!findkey(KEYDB, k->id, k) || tsmemcmp(k->aes, zeros, AESKEYLEN) == 0) {
142 syslog(0, AUTHLOG, "pak-fail no AES key for id %s", k->id);
143 /* make one up so caller doesn't know it was wrong */
145 authpak_hash(k, k->id);
147 authpak_new(&p, k, y, 0);
148 if(write(1, y, PAKYLEN) != PAKYLEN)
150 if(readn(0, y, PAKYLEN) != PAKYLEN)
152 if(authpak_finish(&p, k, y))
159 static uchar ok[1] = {AuthOK};
161 if(write(1, ok, 1) != 1)
164 /* invalidate pak keys */
171 pak1(tr->authid, &akey);
172 pak1(tr->hostid, &hkey);
173 } else if(tr->uid[0]) {
174 pak1(tr->uid, &ukey);
181 getkey(char *u, Keyslot *k)
183 /* empty user id is an error */
187 if(k == &hkey && strcmp(u, k->id) == 0)
189 if(k == &akey && strcmp(u, k->id) == 0)
191 if(k == &ukey && strcmp(u, k->id) == 0)
195 syslog(0, AUTHLOG, "need DES key for %s, but DES is disabled", u);
196 replyerror("DES is disabled");
200 return findkey(KEYDB, u, k);
204 ticketrequest(Ticketreq *tr)
206 char tbuf[2*MAXTICKETLEN+1];
212 if(!getkey(tr->authid, &akey)){
213 /* make one up so caller doesn't know it was wrong */
214 mkkey(tr->authid, &akey);
215 syslog(0, AUTHLOG, "tr-fail authid %s", tr->authid);
217 if(!getkey(tr->hostid, &hkey)){
218 /* make one up so caller doesn't know it was wrong */
219 mkkey(tr->hostid, &hkey);
220 syslog(0, AUTHLOG, "tr-fail hostid %s(%s)", tr->hostid, raddr);
223 if(!speaksfor(tr->hostid, tr->uid)){
224 mkkey(tr->authid, &akey);
225 mkkey(tr->hostid, &hkey);
226 syslog(0, AUTHLOG, "tr-fail %s@%s(%s) -> %s@%s no speaks for",
227 tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
232 n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &hkey);
234 n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &akey);
235 if(write(1, tbuf, n) != n)
238 syslog(0, AUTHLOG, "tr-ok %s@%s(%s) -> %s@%s", tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
242 challengebox(Ticketreq *tr)
244 char kbuf[DESKEYLEN], nkbuf[DESKEYLEN], buf[NETCHLEN+1];
245 char *key, *netkey, *err;
250 key = finddeskey(KEYDB, tr->uid, kbuf);
251 netkey = finddeskey(NETKEYDB, tr->uid, nkbuf);
252 if(key == nil && netkey == nil){
253 /* make one up so caller doesn't know it was wrong */
254 genrandom((uchar*)nkbuf, DESKEYLEN);
256 syslog(0, AUTHLOG, "cr-fail uid %s@%s", tr->uid, raddr);
259 if(!getkey(tr->hostid, &hkey)){
260 /* make one up so caller doesn't know it was wrong */
261 mkkey(tr->hostid, &hkey);
262 syslog(0, AUTHLOG, "cr-fail hostid %s %s@%s", tr->hostid, tr->uid, raddr);
268 memset(buf, 0, sizeof(buf));
270 chal = nfastrand(MAXNETCHAL);
271 sprint(buf+1, "%lud", chal);
272 if(write(1, buf, NETCHLEN+1) != NETCHLEN+1)
274 if(readn(0, buf, NETCHLEN) != NETCHLEN)
276 if(!(key != nil && netcheck(key, chal, buf))
277 && !(netkey != nil && netcheck(netkey, chal, buf))
278 && (err = secureidcheck(tr->uid, buf)) != nil){
279 replyerror("cr-fail %s %s %s", err, tr->uid, raddr);
286 * reply with ticket & authenticator
288 tickauthreply(tr, &hkey);
290 syslog(0, AUTHLOG, "cr-ok %s@%s(%s)", tr->uid, tr->hostid, raddr);
294 changepasswd(Ticketreq *tr)
296 char tbuf[MAXTICKETLEN+1], prbuf[MAXPASSREQLEN], *err;
302 if(!getkey(tr->uid, &ukey)){
303 /* make one up so caller doesn't know it was wrong */
304 mkkey(tr->uid, &ukey);
305 syslog(0, AUTHLOG, "cp-fail uid %s@%s", tr->uid, raddr);
308 /* send back a ticket with a new key */
313 n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &ukey);
314 if(write(1, tbuf, n) != n)
317 /* loop trying passwords out */
319 for(n=0; (m = convM2PR(prbuf, n, &pr, &t)) <= 0; n += m){
321 if(m <= n || m > sizeof(prbuf))
324 if(readn(0, prbuf+n, m) != m)
327 if(pr.num != AuthPass){
328 replyerror("protocol botch1: %s", raddr);
331 passtokey(&nkey, pr.old);
332 if(tsmemcmp(ukey.des, nkey.des, DESKEYLEN) != 0){
333 replyerror("protocol botch2: %s", raddr);
336 if(tsmemcmp(ukey.aes, zeros, AESKEYLEN) != 0 && tsmemcmp(ukey.aes, nkey.aes, AESKEYLEN) != 0){
337 replyerror("protocol botch3: %s", raddr);
341 err = okpasswd(pr.new);
343 replyerror("%s %s", err, raddr);
346 passtokey(&nkey, pr.new);
348 if(pr.changesecret && setsecret(KEYDB, tr->uid, pr.secret) == 0){
349 replyerror("can't write secret %s", raddr);
352 if(*pr.new && setkey(KEYDB, tr->uid, &nkey) == 0){
353 replyerror("can't write key %s", raddr);
356 memmove(ukey.des, nkey.des, DESKEYLEN);
357 memmove(ukey.aes, nkey.aes, AESKEYLEN);
363 if(write(1, prbuf, 1) != 1)
370 static char sysname[Maxpath];
379 domain = csgetvalue(0, "sys", sysname, "dom", nil);
383 n = readfile("/dev/sysname", sysname, sizeof(sysname)-1);
385 strcpy(sysname, "kremvax");
396 if(c >= '0' && c <= '9')
398 if(c >= 'A' && c <= 'F')
400 if(c >= 'a' && c <= 'f')
406 apop(Ticketreq *tr, int type)
408 int challen, i, n, tries;
412 char sbuf[SECRETLEN];
413 char trbuf[TICKREQLEN];
415 uchar digest[MD5dlen], resp[MD5dlen];
422 * Create a challenge and send it.
424 genrandom((uchar*)rb, sizeof(rb));
426 p += snprint(p, sizeof(chal), "<%lux%lux.%lux%lux@%s>",
427 rb[0], rb[1], rb[2], rb[3], domainname());
429 print("%c%-5d%s", AuthOKvar, challen, chal);
439 n = readn(0, trbuf, sizeof(trbuf));
440 if(n <= 0 || convM2TR(trbuf, n, &treq) <= 0)
443 if(tr->type != type || tr->uid[0] == 0)
449 if(readn(0, buf, MD5dlen*2) != MD5dlen*2)
451 for(i = 0; i < MD5dlen; i++)
452 resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]);
457 secret = findsecret(KEYDB, tr->uid, sbuf);
458 if(!getkey(tr->hostid, &hkey) || secret == nil){
459 replyerror("apop-fail bad response %s", raddr);
466 if(type == AuthCram){
467 hmac_md5((uchar*)chal, challen,
468 (uchar*)secret, strlen(secret),
471 s = md5((uchar*)chal, challen, 0, 0);
472 md5((uchar*)secret, strlen(secret), digest, s);
474 if(tsmemcmp(digest, resp, MD5dlen) != 0){
475 replyerror("apop-fail bad response %s", raddr);
483 * reply with ticket & authenticator
485 tickauthreply(tr, &hkey);
488 syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr);
490 syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr);
497 /* VNC reverses the bits of each byte before using as a des key */
498 uchar swizzletab[256] = {
499 0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
500 0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
501 0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
502 0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
503 0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
504 0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
505 0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
506 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
507 0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
508 0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
509 0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
510 0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
511 0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
512 0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
513 0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
514 0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
520 uchar chal[VNCchallen+6];
521 uchar reply[VNCchallen];
522 char sbuf[SECRETLEN];
531 * Create a challenge and send it.
533 genrandom(chal+6, VNCchallen);
535 sprint((char*)chal+1, "%-5d", VNCchallen);
536 if(write(1, chal, sizeof(chal)) != sizeof(chal))
542 if(readn(0, reply, sizeof(reply)) != sizeof(reply))
546 * lookup keys (and swizzle bits)
548 memset(sbuf, 0, sizeof(sbuf));
549 secret = findsecret(KEYDB, tr->uid, sbuf);
550 if(!getkey(tr->hostid, &hkey) || secret == nil){
551 mkkey(tr->hostid, &hkey);
552 genrandom((uchar*)sbuf, sizeof(sbuf));
555 for(i = 0; i < 8; i++)
556 secret[i] = swizzletab[(uchar)secret[i]];
559 * decrypt response and compare
561 setupDESstate(&s, (uchar*)secret, nil);
562 desECBdecrypt(reply, sizeof(reply), &s);
563 if(tsmemcmp(reply, chal+6, VNCchallen) != 0){
564 replyerror("vnc-fail bad response %s", raddr);
571 * reply with ticket & authenticator
573 tickauthreply(tr, &hkey);
575 syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr);
583 char sbuf[SECRETLEN];
584 uchar digest[MD5dlen];
590 * Create a challenge and send it.
592 genrandom((uchar*)chal, sizeof(chal));
593 if(write(1, chal, sizeof(chal)) != sizeof(chal))
604 if(readn(0, &reply, OCHAPREPLYLEN) < 0)
607 safecpy(tr->uid, reply.uid, sizeof(tr->uid));
614 secret = findsecret(KEYDB, tr->uid, sbuf);
615 if(!getkey(tr->hostid, &hkey) || secret == nil){
616 replyerror("chap-fail bad response %s", raddr);
623 s = md5(&reply.id, 1, 0, 0);
624 md5((uchar*)secret, strlen(secret), 0, s);
625 md5((uchar*)chal, sizeof(chal), digest, s);
627 if(tsmemcmp(digest, reply.resp, MD5dlen) != 0){
628 replyerror("chap-fail bad response %s", raddr);
636 * reply with ticket & authenticator
638 tickauthreply(tr, &hkey);
640 syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr);
642 /* no secret after ticket */
646 static uchar ntblobsig[] = {0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
652 char sbuf[SECRETLEN], windom[DOMLEN];
653 uchar chal[MSchallen], ntblob[1024];
654 uchar hash[MShashlen];
655 uchar resp[MSresplen];
657 int dupe, lmok, ntok, ntbloblen;
662 * Create a challenge and send it.
664 genrandom(chal, sizeof(chal));
665 if(write(1, chal, MSchallen) != MSchallen)
676 if(readn(0, &reply, NTLMREPLYLEN) < 0)
680 if(memcmp(reply.NTresp+16, ntblobsig, sizeof(ntblobsig)) == 0){
681 ntbloblen = reply.len[0] | reply.len[1]<<8;
682 ntbloblen -= NTLMREPLYLEN;
683 if(ntbloblen < 0 || ntbloblen > sizeof(ntblob)-8)
685 if(readn(0, ntblob+8, ntbloblen) < 0)
687 memmove(ntblob, reply.NTresp+16, 8);
691 safecpy(tr->uid, reply.uid, sizeof(tr->uid));
695 safecpy(windom, reply.dom, sizeof(windom));
700 secret = findsecret(KEYDB, tr->uid, sbuf);
701 if(!getkey(tr->hostid, &hkey) || secret == nil){
702 replyerror("ntlm-fail bad response %s@%s/%s(%s)", tr->uid, windom, tr->hostid, raddr);
708 ntv2hash(hash, secret, tr->uid, windom);
711 * LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC)
713 s = hmac_md5(chal, MSchallen, hash, MShashlen, nil, nil);
714 hmac_md5((uchar*)reply.LMresp+16, MSchallen, hash, MShashlen, resp, s);
715 lmok = tsmemcmp(resp, reply.LMresp, 16) == 0;
718 * NtResponse = Cat(HMAC_MD5(NtHash, Cat(SC, NtBlob)), NtBlob)
720 s = hmac_md5(chal, MSchallen, hash, MShashlen, nil, nil);
721 hmac_md5(ntblob, ntbloblen, hash, MShashlen, resp, s);
722 ntok = tsmemcmp(resp, reply.NTresp, 16) == 0;
725 * LM response can be all zeros or signature key,
726 * so make it valid when the NT respone matches.
730 } else if(memcmp(reply.NTresp, zeros, MSresplen) == 0){
732 ntv2hash(hash, secret, tr->uid, windom);
735 * LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC)
737 s = hmac_md5(chal, MSchallen, hash, MShashlen, nil, nil);
738 hmac_md5((uchar*)reply.LMresp+16, MSchallen, hash, MShashlen, resp, s);
739 lmok = ntok = tsmemcmp(resp, reply.LMresp, 16) == 0;
743 lmhash(hash, secret);
744 mschalresp(resp, hash, chal);
745 lmok = tsmemcmp(resp, reply.LMresp, MSresplen) == 0;
747 nthash(hash, secret);
748 mschalresp(resp, hash, chal);
749 ntok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0;
750 dupe = tsmemcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
754 * It is valid to send the same response in both the LM and NTLM
755 * fields provided one of them is correct, if neither matches,
756 * or the two fields are different and either fails to match,
757 * the whole sha-bang fails.
759 * This is an improvement in security as it allows clients who
760 * wish to do NTLM auth (which is insecure) not to send
761 * LM tokens (which is very insecure).
763 * Windows servers supports clients doing this also though
764 * windows clients don't seem to use the feature.
766 if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){
767 replyerror("ntlm-fail bad response %s@%s/%s(%s)", tr->uid, windom, tr->hostid, raddr);
775 * reply with ticket & authenticator
777 tickauthreply(tr, &hkey);
779 syslog(0, AUTHLOG, "ntlm-ok %s@%s/%s(%s)", tr->uid, windom, tr->hostid, raddr);
785 mschap(Ticketreq *tr, int nchal)
788 char sbuf[SECRETLEN];
790 uchar hash[MShashlen];
791 uchar resp[MSresplen];
793 int dupe, lmok, ntok;
794 uchar phash[SHA1dlen], chash[SHA1dlen], ahash[SHA1dlen];
799 * Create a challenge and send it.
801 genrandom(chal, sizeof(chal));
802 if(write(1, chal, nchal) != nchal)
813 if(readn(0, &reply, OMSCHAPREPLYLEN) < 0)
816 safecpy(tr->uid, reply.uid, sizeof(tr->uid));
823 secret = findsecret(KEYDB, tr->uid, sbuf);
824 if(!getkey(tr->hostid, &hkey) || secret == nil){
825 replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
829 if(nchal == MSchallenv2){
831 s = sha1((uchar*)reply.LMresp, nchal, nil, nil);
832 s = sha1(chal, nchal, nil, s);
833 sha1((uchar*)tr->uid, strlen(tr->uid), chash, s);
835 nthash(hash, secret);
836 mschalresp(resp, hash, chash);
837 ntok = lmok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0;
840 /* MSCHAP (LM+NTLM) */
841 lmhash(hash, secret);
842 mschalresp(resp, hash, chal);
843 lmok = tsmemcmp(resp, reply.LMresp, MSresplen) == 0;
845 nthash(hash, secret);
846 mschalresp(resp, hash, chal);
847 ntok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0;
848 dupe = tsmemcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
852 * It is valid to send the same response in both the LM and NTLM
853 * fields provided one of them is correct, if neither matches,
854 * or the two fields are different and either fails to match,
855 * the whole sha-bang fails.
857 * This is an improvement in security as it allows clients who
858 * wish to do NTLM auth (which is insecure) not to send
859 * LM tokens (which is very insecure).
861 * Windows servers supports clients doing this also though
862 * windows clients don't seem to use the feature.
864 if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){
865 replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
872 nthash(hash, secret);
873 md4(hash, 16, hash, nil);
876 * reply with ticket & authenticator
878 if(nchal == MSchallenv2){
879 s = sha1(hash, 16, nil, nil);
880 sha1((uchar*)reply.NTresp, MSresplen, nil, s);
881 sha1((uchar*)"This is the MPPE Master Key", 27, phash, s);
883 s = sha1(hash, 16, nil, nil);
884 sha1((uchar*)reply.NTresp, MSresplen, nil, s);
885 sha1((uchar*)"Magic server to client signing constant", 39, ahash, s);
887 s = sha1(ahash, 20, nil, nil);
888 sha1(chash, 8, nil, s);
889 sha1((uchar*)"Pad to make it do more than one iteration", 41, ahash, s);
891 tickauthreply2(tr, &hkey, phash, 16, ahash, 20);
893 s = sha1(hash, 16, nil, nil);
894 sha1(hash, 16, nil, s);
895 sha1(chal, 8, phash, s);
897 tickauthreply2(tr, &hkey, phash, 16, nil, 0);
900 syslog(0, AUTHLOG, "mschap-ok %s/%s(%s)", tr->uid, tr->hostid, raddr);
906 nthash(uchar hash[MShashlen], char *passwd)
912 ds = md4(nil, 0, nil, nil);
914 passwd += chartorune(&r, passwd);
919 md4(nil, 0, hash, ds);
923 ntv2hash(uchar hash[MShashlen], char *passwd, char *user, char *dom)
925 uchar v1hash[MShashlen];
930 nthash(v1hash, passwd);
933 * Some documentation insists that the username must be forced to
934 * uppercase, but the domain name should not be. Other shows both
935 * being forced to uppercase. I am pretty sure this is irrevevant as the
936 * domain name passed from the remote server always seems to be in
939 ds = hmac_md5(nil, 0, v1hash, sizeof(v1hash), nil, nil);
941 user += chartorune(&r, user);
945 hmac_md5(b, 2, v1hash, sizeof(v1hash), nil, ds);
948 dom += chartorune(&r, dom);
951 hmac_md5(b, 2, v1hash, sizeof(v1hash), nil, ds);
953 hmac_md5(nil, 0, v1hash, sizeof(v1hash), hash, ds);
957 lmhash(uchar hash[MShashlen], char *passwd)
960 char *stdtext = "KGS!@#$%";
963 memset(buf, 0, sizeof(buf));
964 strncpy((char*)buf, passwd, sizeof(buf));
965 for(i=0; i<sizeof(buf); i++)
966 if(buf[i] >= 'a' && buf[i] <= 'z')
969 memcpy(hash, stdtext, 8);
970 memcpy(hash+8, stdtext, 8);
972 desencrypt(hash, buf);
973 desencrypt(hash+8, buf+7);
977 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
982 memset(buf, 0, sizeof(buf));
983 memcpy(buf, hash, MShashlen);
986 memmove(resp+i*MSchallen, chal, MSchallen);
987 desencrypt(resp+i*MSchallen, buf+i*7);
992 desencrypt(uchar data[8], uchar key[7])
996 key_setup(key, ekey);
997 block_cipher(ekey, data, 0);
1001 * return true of the speaker may speak for the user
1003 * a speaker may always speak for himself/herself
1006 speaksfor(char *speaker, char *user)
1011 char notuser[Maxpath];
1013 if(strcmp(speaker, user) == 0)
1019 tp = ndbsearch(db, &s, "hostid", speaker);
1024 snprint(notuser, sizeof notuser, "!%s", user);
1025 for(ntp = tp; ntp != nil; ntp = ntp->entry)
1026 if(strcmp(ntp->attr, "uid") == 0){
1027 if(strcmp(ntp->val, notuser) == 0){
1031 if(*ntp->val == '*' || strcmp(ntp->val, user) == 0)
1039 * return an error reply
1042 replyerror(char *fmt, ...)
1044 char buf[AERRLEN+1];
1047 memset(buf, 0, sizeof(buf));
1049 vseprint(buf + 1, buf + sizeof(buf), fmt, arg);
1053 write(1, buf, AERRLEN+1);
1054 syslog(0, AUTHLOG, buf+1);
1065 snprint(file, sizeof(file), "%s/remote", dir);
1066 n = readfile(file, raddr, sizeof(raddr)-1);
1071 cp = strchr(raddr, '\n');
1074 cp = strchr(raddr, '!');
1084 genrandom(keyseed, sizeof(keyseed));
1085 if((fd = open("/adm/keyseed", OREAD)) >= 0){
1086 werrstr("file truncated");
1087 if(read(fd, keyseed, sizeof(keyseed)) == sizeof(keyseed)){
1093 syslog(0, AUTHLOG, "initkeyseed: no keyseed: %r");
1094 if((fd = create("/adm/keyseed", OWRITE, 0600)) < 0){
1095 syslog(0, AUTHLOG, "initkeyseed: can't create: %r");
1098 write(fd, keyseed, sizeof(keyseed));
1103 mkkey(char *id, Authkey *a)
1105 uchar h[SHA2_256dlen];
1107 genrandom((uchar*)a, sizeof(Authkey));
1110 * the DES key has to be constant for a user in each response,
1111 * so we make one up pseudo randomly from a keyseed and user name.
1113 hmac_sha2_256((uchar*)id, strlen(id), keyseed, sizeof(keyseed), h, nil);
1114 memmove(a->des, h, DESKEYLEN);
1115 memset(h, 0, sizeof(h));
1119 mkticket(Ticketreq *tr, Ticket *t)
1121 memset(t, 0, sizeof(Ticket));
1122 memmove(t->chal, tr->chal, CHALLEN);
1123 safecpy(t->cuid, tr->uid, ANAMELEN);
1124 safecpy(t->suid, tr->uid, ANAMELEN);
1125 genrandom(t->key, NONCELEN);
1126 t->form = ticketform;
1130 * reply with ticket and authenticator
1133 * reply with ticket and authenticator
1136 tickauthreply(Ticketreq *tr, Authkey *key)
1138 tickauthreply2(tr, key, nil, 0, nil, 0);
1142 * reply with ticket and authenticator with
1143 * secret s[ns] and authenticator data a[na].
1146 tickauthreply2(Ticketreq *tr, Authkey *key, uchar *ps, int ns, uchar *pa, int na)
1150 char buf[MAXTICKETLEN+MAXAUTHENTLEN+1];
1154 if(t.form != 0 && ns > 0){
1155 assert(ns <= NONCELEN);
1156 memmove(t.key, ps, ns);
1161 n += convT2M(&t, buf+n, sizeof(buf)-n, key);
1162 memset(&a, 0, sizeof(a));
1163 memmove(a.chal, t.chal, CHALLEN);
1164 genrandom(a.rand, NONCELEN);
1165 if(t.form != 0 && na > 0){
1166 assert(na <= NONCELEN);
1167 memmove(a.rand, pa, na);
1170 n += convA2M(&a, buf+n, sizeof(buf)-n, &t);
1171 if(write(1, buf, n) != n)
1177 safecpy(char *to, char *from, int len)
1179 strncpy(to, from, len);
1184 catch(void*, char *msg)
1186 if(strstr(msg, "alarm") != nil)