shithub: riscv

Download patch

ref: c940e986302d16d6e09d61c908d45730b3873766
parent: d7f90a909637fcf12f564fa65b53a1416bef1f6c
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Dec 7 21:22:12 EST 2013

experimental ntlmv2 server authenticaion support for cifsd

extending factotums and the auth servers mschap implementation
to handle variable length NT response for NTLMv2.

fix some minor bugs.

only tested with cifs so far.

--- a/sys/src/cmd/auth/authsrv.c
+++ b/sys/src/cmd/auth/authsrv.c
@@ -34,6 +34,7 @@
 void	randombytes(uchar*, int);
 void	nthash(uchar hash[MShashlen], char *passwd);
 void	lmhash(uchar hash[MShashlen], char *passwd);
+void	ntv2hash(uchar hash[MShashlen], char *passwd, char *user, char *dom);
 void	mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]);
 void	desencrypt(uchar data[8], uchar key[7]);
 int	tickauthreply(Ticketreq*, char*);
@@ -629,19 +630,59 @@
 	syslog(0, AUTHLOG, "resp = %s", buf);
 }
 
+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)
 {
-
 	char *secret, *hkey;
-	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
-	uchar chal[CHALLEN];
+	char sbuf[SECRETLEN], hbuf[DESKEYLEN], windom[128];
+	uchar chal[CHALLEN], ntblob[1024];
 	uchar hash[MShashlen];
 	uchar hash2[MShashlen];
 	uchar resp[MSresplen];
 	OMSchapreply reply;
-	int dupe, lmok, ntok;
+	int dupe, lmok, ntok, ntbloblen;
 	DigestState *s;
 	uchar digest[SHA1dlen];
 
@@ -657,6 +698,43 @@
 	if(readn(0, &reply, sizeof(reply)) < 0)
 		exits(0);
 
+	/*
+	 * CIFS/NTLMv2 uses variable length NT response.
+	 */
+	ntbloblen = 0;
+	if(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)
+			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;
+		}
+	}
+
 	safecpy(tr->uid, reply.uid, sizeof(tr->uid));
 	/*
 	 * lookup
@@ -670,14 +748,34 @@
 		exits(0);
 	}
 
-	lmhash(hash, secret);
-	mschalresp(resp, hash, chal);
-	lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
-	nthash(hash, secret);
-	mschalresp(resp, hash, chal);
-	ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
-	dupe = memcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
+	if(ntbloblen > 0){
+		getname(MsvAvNbDomainName, ntblob, ntbloblen, windom, sizeof(windom));
+		ntv2hash(hash, secret, tr->uid, windom);
 
+		/*
+		 * LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC)
+		 */
+		s = hmac_md5(chal, 8, hash, MShashlen, nil, nil);
+		hmac_md5((uchar*)reply.LMresp+16, 8, hash, MShashlen, resp, s);
+		lmok = memcmp(resp, reply.LMresp, 16) == 0;
+
+		/*
+		 * NtResponse = Cat(HMAC_MD5(NtHash, Cat(SC, NtBlob)), NtBlob)
+		 */
+		s = hmac_md5(chal, 8, hash, MShashlen, nil, nil);
+		hmac_md5(ntblob, ntbloblen, hash, MShashlen, resp, s);
+		ntok = memcmp(resp, reply.NTresp, 16) == 0;
+		dupe = 0;
+	} else {
+		lmhash(hash, secret);
+		mschalresp(resp, hash, chal);
+		lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
+		nthash(hash, secret);
+		mschalresp(resp, hash, chal);
+		ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
+		dupe = memcmp(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,
@@ -707,8 +805,7 @@
 		exits(0);
 
 	if(debug)
-		replyerror("mschap-ok %s/%s(%s) %ux",
-			tr->uid, tr->hostid, raddr);
+		replyerror("mschap-ok %s/%s(%s)", tr->uid, tr->hostid, raddr);
 
 	nthash(hash, secret);
 	md4(hash, 16, hash2, 0);
@@ -723,19 +820,52 @@
 void
 nthash(uchar hash[MShashlen], char *passwd)
 {
-	uchar buf[512];
-	int i;
+	DigestState *ds;
+	uchar b[2];
+	Rune r;
 
-	for (i = 0; *passwd && i + 1 < sizeof(buf);) {
-		Rune r;
+	ds = md4(nil, 0, nil, nil);
+	while(*passwd){
 		passwd += chartorune(&r, passwd);
-		buf[i++] = r;
-		buf[i++] = r >> 8;
+		b[0] = r & 0xff;
+		b[1] = r >> 8;
+		md4(b, 2, nil, ds);
 	}
+	md4(nil, 0, hash, ds);
+}
 
-	memset(hash, 0, 16);
+void
+ntv2hash(uchar hash[MShashlen], char *passwd, char *user, char *dom)
+{
+	uchar v1hash[MShashlen];
+	DigestState *ds;
+	uchar b[2];
+	Rune r;
 
-	md4(buf, i, hash, 0);
+	nthash(v1hash, passwd);
+
+	/*
+	 * Some documentation insists that the username must be forced to
+	 * uppercase, but the domain name should not be. Other shows both
+	 * being forced to uppercase. I am pretty sure this is irrevevant as the
+	 * domain name passed from the remote server always seems to be in
+	 * uppercase already.
+	 */
+        ds = hmac_md5(nil, 0, v1hash, sizeof(v1hash), nil, nil);
+	while(*user){
+		user += chartorune(&r, user);
+		r = toupperrune(r);
+		b[0] = r & 0xff;
+		b[1] = r >> 8;
+        	hmac_md5(b, 2, v1hash, sizeof(v1hash), nil, ds);
+	}
+	while(*dom){
+		dom += chartorune(&r, dom);
+		b[0] = r & 0xff;
+		b[1] = r >> 8;
+        	hmac_md5(b, 2, v1hash, sizeof(v1hash), nil, ds);
+	}
+        hmac_md5(nil, 0, v1hash, sizeof(v1hash), hash, ds);
 }
 
 void
@@ -745,12 +875,12 @@
 	char *stdtext = "KGS!@#$%";
 	int i;
 
+	memset(buf, 0, sizeof(buf));
 	strncpy((char*)buf, passwd, sizeof(buf));
 	for(i=0; i<sizeof(buf); i++)
 		if(buf[i] >= 'a' && buf[i] <= 'z')
 			buf[i] += 'A' - 'a';
 
-	memset(hash, 0, 16);
 	memcpy(hash, stdtext, 8);
 	memcpy(hash+8, stdtext, 8);
 
--- a/sys/src/cmd/auth/factotum/chap.c
+++ b/sys/src/cmd/auth/factotum/chap.c
@@ -130,14 +130,14 @@
 {
 	int ret, nreply;
 	char *a, *v;
-	void *reply;
 	Key *k;
 	Keyinfo ki;
 	State *s;
-	Chapreply cr;
-	MSchapreply mcr;
-	OChapreply ocr;
-	OMSchapreply omcr;
+	Chapreply *cr;
+	MSchapreply *mcr;
+	OChapreply *ocr;
+	OMSchapreply *omcr;
+	uchar reply[4*1024];
 
 	s = fss->ps;
 	a = va;
@@ -150,17 +150,28 @@
 		if(ret != RpcOk)
 			return ret;
 		v = _strfindattr(k->privattr, "!password");
-		if(v == nil)
+		if(v == nil){
+			closekey(k);
 			return failure(fss, "key has no password");
+		}
 		setattrs(fss->attr, k->attr);
 		switch(s->astype){
 		default:
-			abort();
+			closekey(k);
+			return failure(fss, "chap internal botch");
 		case AuthMSchap:
+			if(n < ChapChallen){
+				closekey(k);
+				return failure(fss, "challenge too short");
+			}
 			doLMchap(v, (uchar *)a, (uchar *)s->mcr.LMresp);
 			doNTchap(v, (uchar *)a, (uchar *)s->mcr.NTresp);
 			break;
 		case AuthChap:
+			if(n < ChapChallen+1){
+				closekey(k);
+				return failure(fss, "challenge too short");
+			}
 			dochap(v, *a, a+1, (uchar *)s->cr);
 			break;
 		}
@@ -181,26 +192,28 @@
 		default:
 			return failure(fss, "chap internal botch");
 		case AuthChap:
-			if(n != sizeof(Chapreply))
+			if(n != sizeof(*cr))
 				return failure(fss, "did not get Chapreply");
-			memmove(&cr, va, sizeof cr);
-			ocr.id = cr.id;
-			memmove(ocr.resp, cr.resp, sizeof ocr.resp);
-			memset(omcr.uid, 0, sizeof(omcr.uid));
-			strecpy(ocr.uid, ocr.uid+sizeof ocr.uid, s->user);
-			reply = &ocr;
-			nreply = sizeof ocr;
+			cr = (Chapreply*)va;
+			nreply = sizeof(*ocr);
+			memset(reply, 0, nreply);
+			ocr = (OChapreply*)reply;
+			strecpy(ocr->uid, ocr->uid+sizeof(ocr->uid), s->user);
+			ocr->id = cr->id;
+			memmove(ocr->resp, cr->resp, sizeof(ocr->resp));
 			break;
 		case AuthMSchap:
-			if(n != sizeof(MSchapreply))
+			if(n < sizeof(*mcr))
 				return failure(fss, "did not get MSchapreply");
-			memmove(&mcr, va, sizeof mcr);
-			memmove(omcr.LMresp, mcr.LMresp, sizeof omcr.LMresp);
-			memmove(omcr.NTresp, mcr.NTresp, sizeof omcr.NTresp);
-			memset(omcr.uid, 0, sizeof(omcr.uid));
-			strecpy(omcr.uid, omcr.uid+sizeof omcr.uid, s->user);
-			reply = &omcr;
-			nreply = sizeof omcr;
+			if(n > sizeof(reply)+sizeof(*mcr)-sizeof(*omcr))
+				return failure(fss, "MSchapreply too long");
+			mcr = (MSchapreply*)va;
+			nreply = n+sizeof(*omcr)-sizeof(*mcr);
+			memset(reply, 0, nreply);
+			omcr = (OMSchapreply*)reply;
+			strecpy(omcr->uid, omcr->uid+sizeof(omcr->uid), s->user);
+			memmove(omcr->LMresp, mcr->LMresp, sizeof(omcr->LMresp));
+			memmove(omcr->NTresp, mcr->NTresp, n+sizeof(mcr->NTresp)-sizeof(*mcr));
 			break;
 		}
 		if(doreply(s, reply, nreply) < 0)
--- a/sys/src/cmd/ip/cifsd/smb.c
+++ b/sys/src/cmd/ip/cifsd/smb.c
@@ -99,22 +99,25 @@
 		logit("ignoring bad session key");
 	while(!remoteuser){
 		if(needauth){
-			MSchapreply mcr;
+			MSchapreply *mcr;
 
 			if(smbcs == nil || strlen(user) == 0)
 				break;
-			memset(&mcr, 0, sizeof(mcr));
-			if((lme - lm) == sizeof(mcr.LMresp))
-				memmove(mcr.LMresp, lm, lme - lm);
-			if((nte - nt) == sizeof(mcr.NTresp))
-				memmove(mcr.NTresp, nt, nte - nt);
 			smbcs->user = user;
-			smbcs->resp = &mcr;
-			smbcs->nresp = sizeof(mcr);
+			smbcs->nresp = (nte - nt)+sizeof(*mcr)-sizeof(mcr->NTresp);
+			if(smbcs->nresp < sizeof(*mcr))
+				smbcs->nresp = sizeof(*mcr);
+			smbcs->resp = mallocz(smbcs->nresp, 1);
+			mcr = (MSchapreply*)smbcs->resp;
+			if((lme - lm) <= sizeof(mcr->LMresp))
+				memmove(mcr->LMresp, lm, lme - lm);
+			if((nte - nt) > 0)
+				memmove(mcr->NTresp, nt, nte - nt);
 			if((ai = auth_response(smbcs)) == nil)
 				logit("auth_response: %r");
 			auth_freechal(smbcs);
 			smbcs = nil;
+			free(mcr);
 			if(ai == nil)
 				break;
 			if(auth_chuid(ai, nil) < 0)
--