shithub: riscv

Download patch

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;