ref: e69cde312084f383a346f98f9d7edd42b27e2aef
parent: 87617ea25eecdb3a0e0834786ac5b9dc20f1d59d
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun May 20 18:59:24 EDT 2018
authsrv: implement AuthNTLM
--- a/sys/src/cmd/auth/authsrv.c
+++ b/sys/src/cmd/auth/authsrv.c
@@ -9,7 +9,7 @@
Ndb *db;
char raddr[128];
-uchar zeros[16];
+uchar zeros[32];
typedef struct Keyslot Keyslot;
struct Keyslot
@@ -36,6 +36,7 @@
void changepasswd(Ticketreq*);
void apop(Ticketreq*, int);
void chap(Ticketreq*);
+void ntlm(Ticketreq*);
void mschap(Ticketreq*, int);
void vnc(Ticketreq*);
int speaksfor(char*, char*);
@@ -106,6 +107,9 @@
case AuthMSchapv2:
mschap(&tr, MSchallenv2);
break;
+ case AuthNTLM:
+ ntlm(&tr);
+ break;
case AuthCram:
apop(&tr, AuthCram);
break;
@@ -638,61 +642,19 @@
exits(0);
}
-enum {
- MsvAvEOL = 0,
- MsvAvNbComputerName,
- MsvAvNbDomainName,
- MsvAvDnsComputerName,
- MsvAvDnsomainName,
-};
-
-char*
-getname(int id, uchar *ntblob, int ntbloblen, char *buf, int nbuf)
-{
- int aid, alen, i;
- uchar *p, *e;
- char *d;
- Rune r;
-
- d = buf;
- p = ntblob+8+8+8+4; /* AvPair offset */
- e = ntblob+ntbloblen;
- while(p+4 <= e){
- aid = *p++;
- aid |= *p++ << 8;
- alen = *p++;
- alen |= *p++ << 8;
-
- if(p+alen > e)
- break;
- if(aid == id){
- for(i=0; i+1 < alen && d-buf < nbuf-(UTFmax+1); i+=2){
- r = p[i] | p[i+1]<<8;
- d += runetochar(d, &r);
- }
- break;
- }
- p += alen;
- }
- *d = '\0';
- return buf;
-}
-
static uchar ntblobsig[] = {0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
void
-mschap(Ticketreq *tr, int nchal)
+ntlm(Ticketreq *tr)
{
char *secret;
- char sbuf[SECRETLEN], windom[128];
- uchar chal[16], ntblob[1024];
+ char sbuf[SECRETLEN], windom[DOMLEN];
+ uchar chal[MSchallen], ntblob[1024];
uchar hash[MShashlen];
uchar resp[MSresplen];
- OMSchapreply reply;
+ NTLMreply reply;
int dupe, lmok, ntok, ntbloblen;
- uchar phash[SHA1dlen], chash[SHA1dlen], ahash[SHA1dlen];
DigestState *s;
- long timeout;
int tries;
/*
@@ -699,7 +661,7 @@
* Create a challenge and send it.
*/
genrandom(chal, sizeof(chal));
- if(write(1, chal, nchal) != nchal)
+ if(write(1, chal, MSchallen) != MSchallen)
exits(0);
tries = 5;
@@ -708,58 +670,21 @@
exits(0);
/*
- * get chap reply
+ * get NTLM reply
*/
- if(readn(0, &reply, OMSCHAPREPLYLEN) < 0)
+ if(readn(0, &reply, NTLMREPLYLEN) < 0)
exits(0);
- /*
- * CIFS/NTLMv2 uses variable length NT response.
- */
ntbloblen = 0;
- if(nchal == MSchallen && memcmp(reply.NTresp+16, ntblobsig, sizeof(ntblobsig)) == 0){
- /* Version[1], HiVision[1], Z[6] */
- ntbloblen += 1+1+6;
- memmove(ntblob, reply.NTresp+16, ntbloblen);
-
- /* Time[8], CC[8], Z[4] */
- if(readn(0, ntblob+ntbloblen, 8+8+4) < 0)
+ if(memcmp(reply.NTresp+16, ntblobsig, sizeof(ntblobsig)) == 0){
+ ntbloblen = reply.len[0] | reply.len[1]<<8;
+ ntbloblen -= NTLMREPLYLEN;
+ if(ntbloblen < 0 || ntbloblen > sizeof(ntblob)-8)
exits(0);
- ntbloblen += 8+8+4;
-
- /* variable AvPairs */
- for(;;){
- int len, id;
-
- if(ntbloblen > sizeof(ntblob)-4)
- exits(0);
- /* AvId[2], AvLen[2], Vairable[AvLen] */
- if(readn(0, ntblob+ntbloblen, 4) < 0)
- exits(0);
- id = ntblob[ntbloblen+0] | ntblob[ntbloblen+1]<<8;
- len = ntblob[ntbloblen+2] | ntblob[ntbloblen+3]<<8;
- ntbloblen += 4;
-
- if(ntbloblen+len > sizeof(ntblob))
- exits(0);
- if(readn(0, ntblob+ntbloblen, len) < 0)
- exits(0);
- ntbloblen += len;
- if(id == MsvAvEOL)
- break;
- }
-
- /* Z[4] */
- if(ntbloblen > sizeof(ntblob)-4)
+ if(readn(0, ntblob+8, ntbloblen) < 0)
exits(0);
-
- /* LINUX omits the final Z(4), so read with short timeout */
- notify(catch);
- timeout = alarm(50);
- if(readn(0, ntblob+ntbloblen, 4) == 4)
- ntbloblen += 4;
- alarm(timeout);
- notify(nil);
+ memmove(ntblob, reply.NTresp+16, 8);
+ ntbloblen += 8;
}
safecpy(tr->uid, reply.uid, sizeof(tr->uid));
@@ -766,47 +691,142 @@
if(tr->uid[0] == 0)
exits(0);
+ safecpy(windom, reply.dom, sizeof(windom));
+
/*
* lookup
*/
secret = findsecret(KEYDB, tr->uid, sbuf);
if(!getkey(tr->hostid, &hkey) || secret == nil){
- replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
+ replyerror("ntlm-fail bad response %s@%s/%s(%s)", tr->uid, windom, tr->hostid, raddr);
goto Retry;
}
if(ntbloblen > 0){
- getname(MsvAvNbDomainName, ntblob, ntbloblen, windom, sizeof(windom));
- for(;;){
- ntv2hash(hash, secret, tr->uid, windom);
+ /* NTLMv2 */
+ ntv2hash(hash, secret, tr->uid, windom);
- /*
- * LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC)
- */
- s = hmac_md5(chal, nchal, hash, MShashlen, nil, nil);
- hmac_md5((uchar*)reply.LMresp+16, nchal, hash, MShashlen, resp, s);
- lmok = tsmemcmp(resp, reply.LMresp, 16) == 0;
+ /*
+ * LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC)
+ */
+ s = hmac_md5(chal, MSchallen, hash, MShashlen, nil, nil);
+ hmac_md5((uchar*)reply.LMresp+16, MSchallen, hash, MShashlen, resp, s);
+ lmok = tsmemcmp(resp, reply.LMresp, 16) == 0;
- /*
- * NtResponse = Cat(HMAC_MD5(NtHash, Cat(SC, NtBlob)), NtBlob)
- */
- s = hmac_md5(chal, nchal, hash, MShashlen, nil, nil);
- hmac_md5(ntblob, ntbloblen, hash, MShashlen, resp, s);
- ntok = tsmemcmp(resp, reply.NTresp, 16) == 0;
+ /*
+ * NtResponse = Cat(HMAC_MD5(NtHash, Cat(SC, NtBlob)), NtBlob)
+ */
+ s = hmac_md5(chal, MSchallen, hash, MShashlen, nil, nil);
+ hmac_md5(ntblob, ntbloblen, hash, MShashlen, resp, s);
+ ntok = tsmemcmp(resp, reply.NTresp, 16) == 0;
- /*
- * LM response can be all zeros or signature key,
- * so make it valid when the NT respone matches.
- */
- lmok |= ntok;
+ /*
+ * LM response can be all zeros or signature key,
+ * so make it valid when the NT respone matches.
+ */
+ lmok |= ntok;
+ dupe = 0;
+ } else if(memcmp(reply.NTresp, zeros, MSresplen) == 0){
+ /* LMv2 */
+ ntv2hash(hash, secret, tr->uid, windom);
- if(lmok || windom[0] == '\0')
- break;
-
- windom[0] = '\0'; /* try NIL domain */
- }
+ /*
+ * LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC)
+ */
+ s = hmac_md5(chal, MSchallen, hash, MShashlen, nil, nil);
+ hmac_md5((uchar*)reply.LMresp+16, MSchallen, hash, MShashlen, resp, s);
+ lmok = ntok = tsmemcmp(resp, reply.LMresp, 16) == 0;
dupe = 0;
- } else if(nchal == MSchallenv2){
+ } else {
+ /* LM+NTLM */
+ lmhash(hash, secret);
+ mschalresp(resp, hash, chal);
+ lmok = tsmemcmp(resp, reply.LMresp, MSresplen) == 0;
+
+ nthash(hash, secret);
+ mschalresp(resp, hash, chal);
+ ntok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0;
+ dupe = tsmemcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
+ }
+
+ /*
+ * It is valid to send the same response in both the LM and NTLM
+ * fields provided one of them is correct, if neither matches,
+ * or the two fields are different and either fails to match,
+ * the whole sha-bang fails.
+ *
+ * This is an improvement in security as it allows clients who
+ * wish to do NTLM auth (which is insecure) not to send
+ * LM tokens (which is very insecure).
+ *
+ * Windows servers supports clients doing this also though
+ * windows clients don't seem to use the feature.
+ */
+ if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){
+ replyerror("ntlm-fail bad response %s@%s/%s(%s)", tr->uid, windom, tr->hostid, raddr);
+ logfail(tr->uid);
+ goto Retry;
+ }
+
+ succeed(tr->uid);
+
+ /*
+ * reply with ticket & authenticator
+ */
+ tickauthreply(tr, &hkey);
+
+ syslog(0, AUTHLOG, "ntlm-ok %s@%s/%s(%s)", tr->uid, windom, tr->hostid, raddr);
+
+ exits(0);
+}
+
+void
+mschap(Ticketreq *tr, int nchal)
+{
+ char *secret;
+ char sbuf[SECRETLEN];
+ uchar chal[16];
+ uchar hash[MShashlen];
+ uchar resp[MSresplen];
+ OMSchapreply reply;
+ int dupe, lmok, ntok;
+ uchar phash[SHA1dlen], chash[SHA1dlen], ahash[SHA1dlen];
+ DigestState *s;
+ int tries;
+
+ /*
+ * Create a challenge and send it.
+ */
+ genrandom(chal, sizeof(chal));
+ if(write(1, chal, nchal) != nchal)
+ exits(0);
+
+ tries = 5;
+Retry:
+ if(--tries < 0)
+ exits(0);
+
+ /*
+ * get chap reply
+ */
+ if(readn(0, &reply, OMSCHAPREPLYLEN) < 0)
+ exits(0);
+
+ safecpy(tr->uid, reply.uid, sizeof(tr->uid));
+ if(tr->uid[0] == 0)
+ exits(0);
+
+ /*
+ * lookup
+ */
+ secret = findsecret(KEYDB, tr->uid, sbuf);
+ if(!getkey(tr->hostid, &hkey) || secret == nil){
+ replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
+ goto Retry;
+ }
+
+ if(nchal == MSchallenv2){
+ /* MSCHAPv2 */
s = sha1((uchar*)reply.LMresp, nchal, nil, nil);
s = sha1(chal, nchal, nil, s);
sha1((uchar*)tr->uid, strlen(tr->uid), chash, s);
@@ -816,6 +836,7 @@
ntok = lmok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0;
dupe = 0;
} else {
+ /* MSCHAP (LM+NTLM) */
lmhash(hash, secret);
mschalresp(resp, hash, chal);
lmok = tsmemcmp(resp, reply.LMresp, MSresplen) == 0;