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,8 +422,11 @@ apop(Ticketreq *tr, int type)
challen = p - chal;
print("%c%-5d%s", AuthOKvar, challen, chal);
/* give user a few attempts */
for(tries = 0; ; tries++) {
tries = 5;
Retry:
if(--tries < 0)
exits(0);
/*
* get ticket request
*/
@ -448,10 +451,7 @@ apop(Ticketreq *tr, int type)
secret = findsecret(KEYDB, tr->uid, sbuf);
if(!getkey(tr->hostid, &hkey) || secret == nil){
replyerror("apop-fail bad response %s", raddr);
logfail(tr->uid);
if(tries > 5)
exits(0);
continue;
goto Retry;
}
/*
@ -468,11 +468,7 @@ apop(Ticketreq *tr, int type)
if(tsmemcmp(digest, resp, MD5dlen) != 0){
replyerror("apop-fail bad response %s", raddr);
logfail(tr->uid);
if(tries > 5)
exits(0);
continue;
}
break;
goto Retry;
}
succeed(tr->uid);
@ -582,6 +578,7 @@ chap(Ticketreq *tr)
uchar digest[MD5dlen];
char chal[CHALLEN];
OChapreply reply;
int tries;
/*
* Create a challenge and send it.
@ -590,6 +587,11 @@ chap(Ticketreq *tr)
if(write(1, chal, sizeof(chal)) != sizeof(chal))
exits(0);
tries = 5;
Retry:
if(--tries < 0)
exits(0);
/*
* get chap reply
*/
@ -606,8 +608,7 @@ chap(Ticketreq *tr)
secret = findsecret(KEYDB, tr->uid, sbuf);
if(!getkey(tr->hostid, &hkey) || secret == nil){
replyerror("chap-fail bad response %s", raddr);
logfail(tr->uid);
return;
goto Retry;
}
/*
@ -620,7 +621,7 @@ chap(Ticketreq *tr)
if(tsmemcmp(digest, reply.resp, MD5dlen) != 0){
replyerror("chap-fail bad response %s", raddr);
logfail(tr->uid);
return;
goto Retry;
}
succeed(tr->uid);
@ -690,6 +691,7 @@ mschap(Ticketreq *tr, int nchal)
int dupe, lmok, ntok, ntbloblen;
uchar phash[SHA1dlen], chash[SHA1dlen], ahash[SHA1dlen];
DigestState *s;
int tries;
/*
* Create a challenge and send it.
@ -698,6 +700,11 @@ mschap(Ticketreq *tr, int nchal)
if(write(1, chal, nchal) != nchal)
exits(0);
tries = 5;
Retry:
if(--tries < 0)
exits(0);
/*
* get chap reply
*/
@ -758,39 +765,43 @@ mschap(Ticketreq *tr, int nchal)
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);
logfail(tr->uid);
return;
goto Retry;
}
if(ntbloblen > 0){
getname(MsvAvNbDomainName, ntblob, ntbloblen, windom, sizeof(windom));
for(;;){
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);
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;
/*
* 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);
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;
windom[0] = '\0'; /* try NIL domain */
}
dupe = 0;
} else if(nchal == MSchallenv2){
s = sha1((uchar*)reply.LMresp, MSchallenv2, nil, nil);
s = sha1(chal, MSchallenv2, nil, s);
s = sha1((uchar*)reply.LMresp, nchal, nil, nil);
s = sha1(chal, nchal, nil, s);
sha1((uchar*)tr->uid, strlen(tr->uid), chash, s);
nthash(hash, secret);
@ -805,7 +816,6 @@ mschap(Ticketreq *tr, int nchal)
nthash(hash, secret);
mschalresp(resp, hash, chal);
ntok = tsmemcmp(resp, 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)){
replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
logfail(tr->uid);
return;
goto Retry;
}
succeed(tr->uid);

View file

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

View file

@ -44,7 +44,7 @@ err:
c = ce = nil;
mode = 0;
if(needauth){
if(smbcs)
if(smbcs != nil)
auth_freechal(smbcs);
if(smbcs = auth_challenge("proto=mschap role=server")){
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);
if(smbcs->nresp < sizeof(*mcr))
smbcs->nresp = sizeof(*mcr);
smbcs->resp = mallocz(smbcs->nresp, 1);
mcr = (MSchapreply*)smbcs->resp;
mcr = mallocz(smbcs->nresp, 1);
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)
smbcs->resp = mcr;
ai = auth_response(smbcs);
if(ai == nil){
logit("auth_response: %r");
auth_freechal(smbcs);
smbcs = nil;
free(mcr);
if(ai == nil)
break;
break; /* allow retry with the same challenge */
}
if(auth_chuid(ai, nil) < 0)
logit("auth_chuid: %r");
auth_freeAI(ai);
auth_freechal(smbcs);
smbcs = nil;
free(mcr);
}
remoteuser = getuser();
logit("auth successfull");