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:
parent
a59aa24a94
commit
58aba2a67f
3 changed files with 87 additions and 72 deletions
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue