8 #include "authcmdlib.h"
14 typedef struct Keyslot Keyslot;
20 Keyslot hkey, akey, ukey;
21 uchar keyseed[SHA2_256dlen];
25 /* Microsoft auth constants */
33 void ticketrequest(Ticketreq*);
34 void challengebox(Ticketreq*);
35 void changepasswd(Ticketreq*);
36 void apop(Ticketreq*, int);
37 void chap(Ticketreq*);
38 void mschap(Ticketreq*);
40 int speaksfor(char*, char*);
41 void replyerror(char*, ...);
43 void initkeyseed(void);
45 void mkticket(Ticketreq*, Ticket*);
46 void nthash(uchar hash[MShashlen], char *passwd);
47 void lmhash(uchar hash[MShashlen], char *passwd);
48 void ntv2hash(uchar hash[MShashlen], char *passwd, char *user, char *dom);
49 void mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]);
50 void desencrypt(uchar data[8], uchar key[7]);
51 void tickauthreply(Ticketreq*, Authkey*);
52 void safecpy(char*, char*, int);
55 main(int argc, char *argv[])
67 strcpy(raddr, "unknown");
69 getraddr(argv[argc-1]);
71 alarm(10*60*1000); /* kill a connection after 10 minutes */
76 db = ndbopen("/lib/ndb/auth");
78 syslog(0, AUTHLOG, "no /lib/ndb/auth");
81 n = readn(0, buf, sizeof(buf));
82 if(n <= 0 || convM2TR(buf, n, &tr) <= 0)
113 syslog(0, AUTHLOG, "unknown ticket request type: %d", tr.type);
116 /* invalidate pak keys */
125 pak1(char *u, Keyslot *k)
130 safecpy(k->id, u, sizeof(k->id));
131 if(!findkey(KEYDB, k->id, k) || tsmemcmp(k->aes, zeros, AESKEYLEN) == 0) {
132 /* make one up so caller doesn't know it was wrong */
134 authpak_hash(k, k->id);
136 authpak_new(&p, k, y, 0);
137 if(write(1, y, PAKYLEN) != PAKYLEN)
139 if(readn(0, y, PAKYLEN) != PAKYLEN)
141 if(authpak_finish(&p, k, y))
148 static uchar ok[1] = {AuthOK};
150 if(write(1, ok, 1) != 1)
153 /* invalidate pak keys */
160 pak1(tr->authid, &akey);
161 pak1(tr->hostid, &hkey);
162 } else if(tr->uid[0]) {
163 pak1(tr->uid, &ukey);
170 getkey(char *u, Keyslot *k)
172 /* empty user id is an error */
176 if(k == &hkey && strcmp(u, k->id) == 0)
178 if(k == &akey && strcmp(u, k->id) == 0)
180 if(k == &ukey && strcmp(u, k->id) == 0)
186 return findkey(KEYDB, u, k);
190 ticketrequest(Ticketreq *tr)
192 char tbuf[2*MAXTICKETLEN+1];
198 if(!getkey(tr->authid, &akey)){
199 /* make one up so caller doesn't know it was wrong */
201 syslog(0, AUTHLOG, "tr-fail authid %s", tr->authid);
203 if(!getkey(tr->hostid, &hkey)){
204 /* make one up so caller doesn't know it was wrong */
206 syslog(0, AUTHLOG, "tr-fail hostid %s(%s)", tr->hostid, raddr);
209 if(!speaksfor(tr->hostid, tr->uid)){
212 syslog(0, AUTHLOG, "tr-fail %s@%s(%s) -> %s@%s no speaks for",
213 tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
218 n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &hkey);
220 n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &akey);
221 if(write(1, tbuf, n) != n)
224 syslog(0, AUTHLOG, "tr-ok %s@%s(%s) -> %s@%s", tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
228 challengebox(Ticketreq *tr)
230 char kbuf[DESKEYLEN], nkbuf[DESKEYLEN], buf[NETCHLEN+1];
231 char *key, *netkey, *err;
236 key = finddeskey(KEYDB, tr->uid, kbuf);
237 netkey = finddeskey(NETKEYDB, tr->uid, nkbuf);
238 if(key == nil && netkey == nil){
239 /* make one up so caller doesn't know it was wrong */
240 genrandom((uchar*)nkbuf, DESKEYLEN);
242 syslog(0, AUTHLOG, "cr-fail uid %s@%s", tr->uid, raddr);
245 if(!getkey(tr->hostid, &hkey)){
246 /* make one up so caller doesn't know it was wrong */
248 syslog(0, AUTHLOG, "cr-fail hostid %s %s@%s", tr->hostid, tr->uid, raddr);
254 memset(buf, 0, sizeof(buf));
256 chal = nfastrand(MAXNETCHAL);
257 sprint(buf+1, "%lud", chal);
258 if(write(1, buf, NETCHLEN+1) != NETCHLEN+1)
260 if(readn(0, buf, NETCHLEN) != NETCHLEN)
262 if(!(key != nil && netcheck(key, chal, buf))
263 && !(netkey != nil && netcheck(netkey, chal, buf))
264 && (err = secureidcheck(tr->uid, buf)) != nil){
265 replyerror("cr-fail %s %s %s", err, tr->uid, raddr);
272 * reply with ticket & authenticator
274 tickauthreply(tr, &hkey);
276 syslog(0, AUTHLOG, "cr-ok %s@%s(%s)", tr->uid, tr->hostid, raddr);
280 changepasswd(Ticketreq *tr)
282 char tbuf[MAXTICKETLEN+1], prbuf[MAXPASSREQLEN], *err;
288 if(!getkey(tr->uid, &ukey)){
289 /* make one up so caller doesn't know it was wrong */
291 syslog(0, AUTHLOG, "cp-fail uid %s@%s", tr->uid, raddr);
294 /* send back a ticket with a new key */
299 n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &ukey);
300 if(write(1, tbuf, n) != n)
303 /* loop trying passwords out */
305 for(n=0; (m = convM2PR(prbuf, n, &pr, &t)) <= 0; n += m){
307 if(m <= n || m > sizeof(prbuf))
310 if(readn(0, prbuf+n, m) != m)
313 if(pr.num != AuthPass){
314 replyerror("protocol botch1: %s", raddr);
317 passtokey(&nkey, pr.old);
318 if(tsmemcmp(ukey.des, nkey.des, DESKEYLEN) != 0){
319 replyerror("protocol botch2: %s", raddr);
322 if(tsmemcmp(ukey.aes, zeros, AESKEYLEN) != 0 && tsmemcmp(ukey.aes, nkey.aes, AESKEYLEN) != 0){
323 replyerror("protocol botch3: %s", raddr);
327 err = okpasswd(pr.new);
329 replyerror("%s %s", err, raddr);
332 passtokey(&nkey, pr.new);
334 if(pr.changesecret && setsecret(KEYDB, tr->uid, pr.secret) == 0){
335 replyerror("can't write secret %s", raddr);
338 if(*pr.new && setkey(KEYDB, tr->uid, &nkey) == 0){
339 replyerror("can't write key %s", raddr);
342 memmove(ukey.des, nkey.des, DESKEYLEN);
343 memmove(ukey.aes, nkey.aes, AESKEYLEN);
349 if(write(1, prbuf, 1) != 1)
356 static char sysname[Maxpath];
365 domain = csgetvalue(0, "sys", sysname, "dom", nil);
369 n = readfile("/dev/sysname", sysname, sizeof(sysname)-1);
371 strcpy(sysname, "kremvax");
382 if(c >= '0' && c <= '9')
384 if(c >= 'A' && c <= 'F')
386 if(c >= 'a' && c <= 'f')
392 apop(Ticketreq *tr, int type)
394 int challen, i, n, tries;
398 char sbuf[SECRETLEN];
399 char trbuf[TICKREQLEN];
401 uchar digest[MD5dlen], resp[MD5dlen];
408 * Create a challenge and send it.
410 genrandom((uchar*)rb, sizeof(rb));
412 p += snprint(p, sizeof(chal), "<%lux%lux.%lux%lux@%s>",
413 rb[0], rb[1], rb[2], rb[3], domainname());
415 print("%c%-5d%s", AuthOKvar, challen, chal);
417 /* give user a few attempts */
418 for(tries = 0; ; tries++) {
422 n = readn(0, trbuf, sizeof(trbuf));
423 if(n <= 0 || convM2TR(trbuf, n, &treq) <= 0)
426 if(tr->type != type || tr->uid[0] == 0)
432 if(readn(0, buf, MD5dlen*2) != MD5dlen*2)
434 for(i = 0; i < MD5dlen; i++)
435 resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]);
440 secret = findsecret(KEYDB, tr->uid, sbuf);
441 if(!getkey(tr->hostid, &hkey) || secret == nil){
442 replyerror("apop-fail bad response %s", raddr);
452 if(type == AuthCram){
453 hmac_md5((uchar*)chal, challen,
454 (uchar*)secret, strlen(secret),
457 s = md5((uchar*)chal, challen, 0, 0);
458 md5((uchar*)secret, strlen(secret), digest, s);
460 if(tsmemcmp(digest, resp, MD5dlen) != 0){
461 replyerror("apop-fail bad response %s", raddr);
473 * reply with ticket & authenticator
475 tickauthreply(tr, &hkey);
478 syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr);
480 syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr);
487 /* VNC reverses the bits of each byte before using as a des key */
488 uchar swizzletab[256] = {
489 0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
490 0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
491 0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
492 0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
493 0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
494 0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
495 0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
496 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
497 0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
498 0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
499 0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
500 0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
501 0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
502 0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
503 0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
504 0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
510 uchar chal[VNCchallen+6];
511 uchar reply[VNCchallen];
512 char sbuf[SECRETLEN];
521 * Create a challenge and send it.
523 genrandom(chal+6, VNCchallen);
525 sprint((char*)chal+1, "%-5d", VNCchallen);
526 if(write(1, chal, sizeof(chal)) != sizeof(chal))
530 * lookup keys (and swizzle bits)
532 memset(sbuf, 0, sizeof(sbuf));
533 secret = findsecret(KEYDB, tr->uid, sbuf);
534 if(!getkey(tr->hostid, &hkey) || secret == nil){
536 genrandom((uchar*)sbuf, sizeof(sbuf));
539 for(i = 0; i < 8; i++)
540 secret[i] = swizzletab[(uchar)secret[i]];
545 if(readn(0, reply, sizeof(reply)) != sizeof(reply))
549 * decrypt response and compare
551 setupDESstate(&s, (uchar*)secret, nil);
552 desECBdecrypt(reply, sizeof(reply), &s);
553 if(tsmemcmp(reply, chal+6, VNCchallen) != 0){
554 replyerror("vnc-fail bad response %s", raddr);
561 * reply with ticket & authenticator
563 tickauthreply(tr, &hkey);
565 syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr);
573 char sbuf[SECRETLEN];
574 uchar digest[MD5dlen];
579 * Create a challenge and send it.
581 genrandom((uchar*)chal, sizeof(chal));
582 if(write(1, chal, sizeof(chal)) != sizeof(chal))
588 if(readn(0, &reply, sizeof(reply)) < 0)
590 safecpy(tr->uid, reply.uid, sizeof(tr->uid));
597 secret = findsecret(KEYDB, tr->uid, sbuf);
598 if(!getkey(tr->hostid, &hkey) || secret == nil){
599 replyerror("chap-fail bad response %s", raddr);
607 s = md5(&reply.id, 1, 0, 0);
608 md5((uchar*)secret, strlen(secret), 0, s);
609 md5((uchar*)chal, sizeof(chal), digest, s);
611 if(tsmemcmp(digest, reply.resp, MD5dlen) != 0){
612 replyerror("chap-fail bad response %s", raddr);
620 * reply with ticket & authenticator
622 tickauthreply(tr, &hkey);
624 syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr);
631 MsvAvDnsComputerName,
636 getname(int id, uchar *ntblob, int ntbloblen, char *buf, int nbuf)
644 p = ntblob+8+8+8+4; /* AvPair offset */
645 e = ntblob+ntbloblen;
655 for(i=0; i+1 < alen && d-buf < nbuf-(UTFmax+1); i+=2){
656 r = p[i] | p[i+1]<<8;
657 d += runetochar(d, &r);
667 static uchar ntblobsig[] = {0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
670 mschap(Ticketreq *tr)
673 char sbuf[SECRETLEN], windom[128];
674 uchar chal[CHALLEN], ntblob[1024];
675 uchar hash[MShashlen];
676 uchar hash2[MShashlen];
677 uchar resp[MSresplen];
679 int dupe, lmok, ntok, ntbloblen;
681 uchar digest[SHA1dlen];
684 * Create a challenge and send it.
686 genrandom(chal, sizeof(chal));
687 if(write(1, chal, sizeof(chal)) != sizeof(chal))
693 if(readn(0, &reply, OMSCHAPREPLYLEN) < 0)
697 * CIFS/NTLMv2 uses variable length NT response.
700 if(memcmp(reply.NTresp+16, ntblobsig, sizeof(ntblobsig)) == 0){
701 /* Version[1], HiVision[1], Z[6] */
703 memmove(ntblob, reply.NTresp+16, ntbloblen);
705 /* Time[8], CC[8], Z[4] */
706 if(readn(0, ntblob+ntbloblen, 8+8+4) < 0)
710 /* variable AvPairs */
714 if(ntbloblen > sizeof(ntblob)-4)
716 /* AvId[2], AvLen[2], Vairable[AvLen] */
717 if(readn(0, ntblob+ntbloblen, 4) < 0)
719 id = ntblob[ntbloblen+0] | ntblob[ntbloblen+1]<<8;
720 len = ntblob[ntbloblen+2] | ntblob[ntbloblen+3]<<8;
723 if(ntbloblen+len > sizeof(ntblob))
725 if(readn(0, ntblob+ntbloblen, len) < 0)
733 if(ntbloblen > sizeof(ntblob)-4)
735 if(readn(0, ntblob+ntbloblen, 4) < 0)
740 safecpy(tr->uid, reply.uid, sizeof(tr->uid));
747 secret = findsecret(KEYDB, tr->uid, sbuf);
748 if(!getkey(tr->hostid, &hkey) || secret == nil){
749 replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
755 getname(MsvAvNbDomainName, ntblob, ntbloblen, windom, sizeof(windom));
758 ntv2hash(hash, secret, tr->uid, windom);
761 * LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC)
763 s = hmac_md5(chal, 8, hash, MShashlen, nil, nil);
764 hmac_md5((uchar*)reply.LMresp+16, 8, hash, MShashlen, resp, s);
765 lmok = tsmemcmp(resp, reply.LMresp, 16) == 0;
768 * NtResponse = Cat(HMAC_MD5(NtHash, Cat(SC, NtBlob)), NtBlob)
770 s = hmac_md5(chal, 8, hash, MShashlen, nil, nil);
771 hmac_md5(ntblob, ntbloblen, hash, MShashlen, resp, s);
772 ntok = tsmemcmp(resp, reply.NTresp, 16) == 0;
774 if(lmok || ntok || windom[0] == '\0')
777 windom[0] = '\0'; /* try NIL domain */
781 lmhash(hash, secret);
782 mschalresp(resp, hash, chal);
783 lmok = tsmemcmp(resp, reply.LMresp, MSresplen) == 0;
784 nthash(hash, secret);
785 mschalresp(resp, hash, chal);
786 ntok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0;
787 dupe = tsmemcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
791 * It is valid to send the same response in both the LM and NTLM
792 * fields provided one of them is correct, if neither matches,
793 * or the two fields are different and either fails to match,
794 * the whole sha-bang fails.
796 * This is an improvement in security as it allows clients who
797 * wish to do NTLM auth (which is insecure) not to send
798 * LM tokens (which is very insecure).
800 * Windows servers supports clients doing this also though
801 * windows clients don't seem to use the feature.
803 if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){
804 replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
812 * reply with ticket & authenticator
814 tickauthreply(tr, &hkey);
816 syslog(0, AUTHLOG, "mschap-ok %s/%s(%s)", tr->uid, tr->hostid, raddr);
818 nthash(hash, secret);
819 md4(hash, 16, hash2, 0);
820 s = sha1(hash2, 16, 0, 0);
821 sha1(hash2, 16, 0, s);
822 sha1(chal, 8, digest, s);
824 if(write(1, digest, 16) != 16)
829 nthash(uchar hash[MShashlen], char *passwd)
835 ds = md4(nil, 0, nil, nil);
837 passwd += chartorune(&r, passwd);
842 md4(nil, 0, hash, ds);
846 ntv2hash(uchar hash[MShashlen], char *passwd, char *user, char *dom)
848 uchar v1hash[MShashlen];
853 nthash(v1hash, passwd);
856 * Some documentation insists that the username must be forced to
857 * uppercase, but the domain name should not be. Other shows both
858 * being forced to uppercase. I am pretty sure this is irrevevant as the
859 * domain name passed from the remote server always seems to be in
862 ds = hmac_md5(nil, 0, v1hash, sizeof(v1hash), nil, nil);
864 user += chartorune(&r, user);
868 hmac_md5(b, 2, v1hash, sizeof(v1hash), nil, ds);
871 dom += chartorune(&r, dom);
874 hmac_md5(b, 2, v1hash, sizeof(v1hash), nil, ds);
876 hmac_md5(nil, 0, v1hash, sizeof(v1hash), hash, ds);
880 lmhash(uchar hash[MShashlen], char *passwd)
883 char *stdtext = "KGS!@#$%";
886 memset(buf, 0, sizeof(buf));
887 strncpy((char*)buf, passwd, sizeof(buf));
888 for(i=0; i<sizeof(buf); i++)
889 if(buf[i] >= 'a' && buf[i] <= 'z')
892 memcpy(hash, stdtext, 8);
893 memcpy(hash+8, stdtext, 8);
895 desencrypt(hash, buf);
896 desencrypt(hash+8, buf+7);
900 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
905 memset(buf, 0, sizeof(buf));
906 memcpy(buf, hash, MShashlen);
909 memmove(resp+i*MSchallen, chal, MSchallen);
910 desencrypt(resp+i*MSchallen, buf+i*7);
915 desencrypt(uchar data[8], uchar key[7])
919 key_setup(key, ekey);
920 block_cipher(ekey, data, 0);
924 * return true of the speaker may speak for the user
926 * a speaker may always speak for himself/herself
929 speaksfor(char *speaker, char *user)
934 char notuser[Maxpath];
936 if(strcmp(speaker, user) == 0)
942 tp = ndbsearch(db, &s, "hostid", speaker);
947 snprint(notuser, sizeof notuser, "!%s", user);
948 for(ntp = tp; ntp != nil; ntp = ntp->entry)
949 if(strcmp(ntp->attr, "uid") == 0){
950 if(strcmp(ntp->val, notuser) == 0){
954 if(*ntp->val == '*' || strcmp(ntp->val, user) == 0)
962 * return an error reply
965 replyerror(char *fmt, ...)
970 memset(buf, 0, sizeof(buf));
972 vseprint(buf + 1, buf + sizeof(buf), fmt, arg);
976 write(1, buf, AERRLEN+1);
977 syslog(0, AUTHLOG, buf+1);
988 snprint(file, sizeof(file), "%s/remote", dir);
989 n = readfile(file, raddr, sizeof(raddr)-1);
994 cp = strchr(raddr, '\n');
997 cp = strchr(raddr, '!');
1005 static char info[] = "PRF key for generation of dummy user keys";
1006 char k[DESKEYLEN], *u;
1009 if(!finddeskey(KEYDB, u, k)){
1010 syslog(0, AUTHLOG, "user %s not in keydb", u);
1013 hmac_sha2_256((uchar*)info, sizeof(info)-1, (uchar*)k, sizeof(k), keyseed, nil);
1014 memset(k, 0, sizeof(k));
1020 uchar h[SHA2_256dlen];
1023 genrandom((uchar*)a, sizeof(Authkey));
1026 * the DES key has to be constant for a user in each response,
1027 * so we make one up pseudo randomly from a keyseed and user name.
1029 hmac_sha2_256((uchar*)k->id, strlen(k->id), keyseed, sizeof(keyseed), h, nil);
1030 memmove(a->des, h, DESKEYLEN);
1031 memset(h, 0, sizeof(h));
1035 mkticket(Ticketreq *tr, Ticket *t)
1037 memset(t, 0, sizeof(Ticket));
1038 memmove(t->chal, tr->chal, CHALLEN);
1039 safecpy(t->cuid, tr->uid, ANAMELEN);
1040 safecpy(t->suid, tr->uid, ANAMELEN);
1041 genrandom(t->key, NONCELEN);
1042 t->form = ticketform;
1046 * reply with ticket and authenticator
1049 tickauthreply(Ticketreq *tr, Authkey *key)
1053 char buf[MAXTICKETLEN+MAXAUTHENTLEN+1];
1060 n += convT2M(&t, buf+n, sizeof(buf)-n, key);
1061 memset(&a, 0, sizeof(a));
1062 memmove(a.chal, t.chal, CHALLEN);
1063 genrandom(a.rand, NONCELEN);
1065 n += convA2M(&a, buf+n, sizeof(buf)-n, &t);
1066 if(write(1, buf, n) != n)
1071 safecpy(char *to, char *from, int len)
1073 strncpy(to, from, len);