cifsd: fix ntlmv2 authentication

in ntlmv2, the client will retry the challenge response trying a bunch
of different domain names assuming the same server challenge. so we have
to make retries work with factotum and the auth server.

also, windows 7 with compatlevel=4 sends all zeros LM response.
This commit is contained in:
cinap_lenrek 2018-05-19 16:40:01 +02:00
parent a59aa24a94
commit 58aba2a67f
3 changed files with 87 additions and 72 deletions

View file

@ -422,57 +422,53 @@ apop(Ticketreq *tr, int type)
challen = p - chal; challen = p - chal;
print("%c%-5d%s", AuthOKvar, challen, chal); print("%c%-5d%s", AuthOKvar, challen, chal);
/* give user a few attempts */ tries = 5;
for(tries = 0; ; tries++) { Retry:
/* if(--tries < 0)
* get ticket request exits(0);
*/
n = readn(0, trbuf, sizeof(trbuf));
if(n <= 0 || convM2TR(trbuf, n, &treq) <= 0)
exits(0);
tr = &treq;
if(tr->type != type || tr->uid[0] == 0)
exits(0);
/* /*
* read response * get ticket request
*/ */
if(readn(0, buf, MD5dlen*2) != MD5dlen*2) n = readn(0, trbuf, sizeof(trbuf));
exits(0); if(n <= 0 || convM2TR(trbuf, n, &treq) <= 0)
for(i = 0; i < MD5dlen; i++) exits(0);
resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]); tr = &treq;
if(tr->type != type || tr->uid[0] == 0)
exits(0);
/* /*
* lookup * read response
*/ */
secret = findsecret(KEYDB, tr->uid, sbuf); if(readn(0, buf, MD5dlen*2) != MD5dlen*2)
if(!getkey(tr->hostid, &hkey) || secret == nil){ exits(0);
replyerror("apop-fail bad response %s", raddr); for(i = 0; i < MD5dlen; i++)
logfail(tr->uid); resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]);
if(tries > 5)
exits(0);
continue;
}
/* /*
* check for match * lookup
*/ */
if(type == AuthCram){ secret = findsecret(KEYDB, tr->uid, sbuf);
hmac_md5((uchar*)chal, challen, if(!getkey(tr->hostid, &hkey) || secret == nil){
(uchar*)secret, strlen(secret), replyerror("apop-fail bad response %s", raddr);
digest, nil); goto Retry;
} else { }
s = md5((uchar*)chal, challen, 0, 0);
md5((uchar*)secret, strlen(secret), digest, s); /*
} * check for match
if(tsmemcmp(digest, resp, MD5dlen) != 0){ */
replyerror("apop-fail bad response %s", raddr); if(type == AuthCram){
logfail(tr->uid); hmac_md5((uchar*)chal, challen,
if(tries > 5) (uchar*)secret, strlen(secret),
exits(0); digest, nil);
continue; } else {
} s = md5((uchar*)chal, challen, 0, 0);
break; md5((uchar*)secret, strlen(secret), digest, s);
}
if(tsmemcmp(digest, resp, MD5dlen) != 0){
replyerror("apop-fail bad response %s", raddr);
logfail(tr->uid);
goto Retry;
} }
succeed(tr->uid); succeed(tr->uid);
@ -582,6 +578,7 @@ chap(Ticketreq *tr)
uchar digest[MD5dlen]; uchar digest[MD5dlen];
char chal[CHALLEN]; char chal[CHALLEN];
OChapreply reply; OChapreply reply;
int tries;
/* /*
* Create a challenge and send it. * Create a challenge and send it.
@ -590,6 +587,11 @@ chap(Ticketreq *tr)
if(write(1, chal, sizeof(chal)) != sizeof(chal)) if(write(1, chal, sizeof(chal)) != sizeof(chal))
exits(0); exits(0);
tries = 5;
Retry:
if(--tries < 0)
exits(0);
/* /*
* get chap reply * get chap reply
*/ */
@ -606,8 +608,7 @@ chap(Ticketreq *tr)
secret = findsecret(KEYDB, tr->uid, sbuf); secret = findsecret(KEYDB, tr->uid, sbuf);
if(!getkey(tr->hostid, &hkey) || secret == nil){ if(!getkey(tr->hostid, &hkey) || secret == nil){
replyerror("chap-fail bad response %s", raddr); replyerror("chap-fail bad response %s", raddr);
logfail(tr->uid); goto Retry;
return;
} }
/* /*
@ -620,7 +621,7 @@ chap(Ticketreq *tr)
if(tsmemcmp(digest, reply.resp, MD5dlen) != 0){ if(tsmemcmp(digest, reply.resp, MD5dlen) != 0){
replyerror("chap-fail bad response %s", raddr); replyerror("chap-fail bad response %s", raddr);
logfail(tr->uid); logfail(tr->uid);
return; goto Retry;
} }
succeed(tr->uid); succeed(tr->uid);
@ -690,6 +691,7 @@ mschap(Ticketreq *tr, int nchal)
int dupe, lmok, ntok, ntbloblen; int dupe, lmok, ntok, ntbloblen;
uchar phash[SHA1dlen], chash[SHA1dlen], ahash[SHA1dlen]; uchar phash[SHA1dlen], chash[SHA1dlen], ahash[SHA1dlen];
DigestState *s; DigestState *s;
int tries;
/* /*
* Create a challenge and send it. * Create a challenge and send it.
@ -698,6 +700,11 @@ mschap(Ticketreq *tr, int nchal)
if(write(1, chal, nchal) != nchal) if(write(1, chal, nchal) != nchal)
exits(0); exits(0);
tries = 5;
Retry:
if(--tries < 0)
exits(0);
/* /*
* get chap reply * get chap reply
*/ */
@ -758,39 +765,43 @@ mschap(Ticketreq *tr, int nchal)
secret = findsecret(KEYDB, tr->uid, sbuf); secret = findsecret(KEYDB, tr->uid, sbuf);
if(!getkey(tr->hostid, &hkey) || secret == nil){ if(!getkey(tr->hostid, &hkey) || secret == nil){
replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr); replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
logfail(tr->uid); goto Retry;
return;
} }
if(ntbloblen > 0){ if(ntbloblen > 0){
getname(MsvAvNbDomainName, ntblob, ntbloblen, windom, sizeof(windom)); getname(MsvAvNbDomainName, ntblob, ntbloblen, windom, sizeof(windom));
for(;;){ for(;;){
ntv2hash(hash, secret, tr->uid, windom); ntv2hash(hash, secret, tr->uid, windom);
/* /*
* LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC) * LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC)
*/ */
s = hmac_md5(chal, 8, hash, MShashlen, nil, nil); s = hmac_md5(chal, nchal, hash, MShashlen, nil, nil);
hmac_md5((uchar*)reply.LMresp+16, 8, hash, MShashlen, resp, s); hmac_md5((uchar*)reply.LMresp+16, nchal, hash, MShashlen, resp, s);
lmok = tsmemcmp(resp, reply.LMresp, 16) == 0; lmok = tsmemcmp(resp, reply.LMresp, 16) == 0;
/* /*
* NtResponse = Cat(HMAC_MD5(NtHash, Cat(SC, NtBlob)), NtBlob) * NtResponse = Cat(HMAC_MD5(NtHash, Cat(SC, NtBlob)), NtBlob)
*/ */
s = hmac_md5(chal, 8, hash, MShashlen, nil, nil); s = hmac_md5(chal, nchal, hash, MShashlen, nil, nil);
hmac_md5(ntblob, ntbloblen, hash, MShashlen, resp, s); hmac_md5(ntblob, ntbloblen, hash, MShashlen, resp, s);
ntok = tsmemcmp(resp, reply.NTresp, 16) == 0; ntok = tsmemcmp(resp, reply.NTresp, 16) == 0;
if(lmok || ntok || windom[0] == '\0') /*
* LM response can be all zeros or signature key,
* so make it valid when the NT respone matches.
*/
lmok |= ntok;
if(lmok || windom[0] == '\0')
break; break;
windom[0] = '\0'; /* try NIL domain */ windom[0] = '\0'; /* try NIL domain */
} }
dupe = 0; dupe = 0;
} else if(nchal == MSchallenv2){ } else if(nchal == MSchallenv2){
s = sha1((uchar*)reply.LMresp, MSchallenv2, nil, nil); s = sha1((uchar*)reply.LMresp, nchal, nil, nil);
s = sha1(chal, MSchallenv2, nil, s); s = sha1(chal, nchal, nil, s);
sha1((uchar*)tr->uid, strlen(tr->uid), chash, s); sha1((uchar*)tr->uid, strlen(tr->uid), chash, s);
nthash(hash, secret); nthash(hash, secret);
@ -805,7 +816,6 @@ mschap(Ticketreq *tr, int nchal)
nthash(hash, secret); nthash(hash, secret);
mschalresp(resp, hash, chal); mschalresp(resp, hash, chal);
ntok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0; ntok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0;
dupe = tsmemcmp(reply.LMresp, reply.NTresp, MSresplen) == 0; dupe = tsmemcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
} }
@ -825,7 +835,7 @@ mschap(Ticketreq *tr, int nchal)
if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){ if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){
replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr); replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
logfail(tr->uid); logfail(tr->uid);
return; goto Retry;
} }
succeed(tr->uid); succeed(tr->uid);

View file

@ -11,6 +11,7 @@
* read challenge: 8 bytes binary (or 16 bytes for mschapv2) * read challenge: 8 bytes binary (or 16 bytes for mschapv2)
* write user: utf8 * write user: utf8
* write response: Chapreply or MSchapreply structure * write response: Chapreply or MSchapreply structure
* ... retry another user
*/ */
#include <ctype.h> #include <ctype.h>
@ -285,8 +286,10 @@ chapwrite(Fsstate *fss, void *va, uint n)
memmove(omcr->NTresp, mcr->NTresp, n+sizeof(mcr->NTresp)-MSchapreplylen); memmove(omcr->NTresp, mcr->NTresp, n+sizeof(mcr->NTresp)-MSchapreplylen);
break; break;
} }
if(doreply(s, reply, nreply) < 0) if(doreply(s, reply, nreply) < 0){
fss->phase = SNeedUser;
return failure(fss, nil); return failure(fss, nil);
}
fss->phase = Established; fss->phase = Established;
fss->ai.cuid = s->t.cuid; fss->ai.cuid = s->t.cuid;
fss->ai.suid = s->t.suid; fss->ai.suid = s->t.suid;

View file

@ -44,7 +44,7 @@ err:
c = ce = nil; c = ce = nil;
mode = 0; mode = 0;
if(needauth){ if(needauth){
if(smbcs) if(smbcs != nil)
auth_freechal(smbcs); auth_freechal(smbcs);
if(smbcs = auth_challenge("proto=mschap role=server")){ if(smbcs = auth_challenge("proto=mschap role=server")){
c = (uchar*)smbcs->chal; c = (uchar*)smbcs->chal;
@ -107,22 +107,24 @@ smbsessionsetupandx(Req *r, uchar *h, uchar *p, uchar *e)
smbcs->nresp = (nte - nt)+sizeof(*mcr)-sizeof(mcr->NTresp); smbcs->nresp = (nte - nt)+sizeof(*mcr)-sizeof(mcr->NTresp);
if(smbcs->nresp < sizeof(*mcr)) if(smbcs->nresp < sizeof(*mcr))
smbcs->nresp = sizeof(*mcr); smbcs->nresp = sizeof(*mcr);
smbcs->resp = mallocz(smbcs->nresp, 1); mcr = mallocz(smbcs->nresp, 1);
mcr = (MSchapreply*)smbcs->resp;
if((lme - lm) <= sizeof(mcr->LMresp)) if((lme - lm) <= sizeof(mcr->LMresp))
memmove(mcr->LMresp, lm, lme - lm); memmove(mcr->LMresp, lm, lme - lm);
if((nte - nt) > 0) if((nte - nt) > 0)
memmove(mcr->NTresp, nt, nte - nt); memmove(mcr->NTresp, nt, nte - nt);
if((ai = auth_response(smbcs)) == nil) smbcs->resp = mcr;
ai = auth_response(smbcs);
if(ai == nil){
logit("auth_response: %r"); logit("auth_response: %r");
auth_freechal(smbcs); free(mcr);
smbcs = nil; break; /* allow retry with the same challenge */
free(mcr); }
if(ai == nil)
break;
if(auth_chuid(ai, nil) < 0) if(auth_chuid(ai, nil) < 0)
logit("auth_chuid: %r"); logit("auth_chuid: %r");
auth_freeAI(ai); auth_freeAI(ai);
auth_freechal(smbcs);
smbcs = nil;
free(mcr);
} }
remoteuser = getuser(); remoteuser = getuser();
logit("auth successfull"); logit("auth successfull");