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.
This commit is contained in:
parent
d7f90a9096
commit
c940e98630
3 changed files with 199 additions and 53 deletions
|
@ -34,6 +34,7 @@ void mkkey(char*);
|
||||||
void randombytes(uchar*, int);
|
void randombytes(uchar*, int);
|
||||||
void nthash(uchar hash[MShashlen], char *passwd);
|
void nthash(uchar hash[MShashlen], char *passwd);
|
||||||
void lmhash(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 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]);
|
||||||
void desencrypt(uchar data[8], uchar key[7]);
|
void desencrypt(uchar data[8], uchar key[7]);
|
||||||
int tickauthreply(Ticketreq*, char*);
|
int tickauthreply(Ticketreq*, char*);
|
||||||
|
@ -629,19 +630,59 @@ printresp(uchar resp[MSresplen])
|
||||||
syslog(0, AUTHLOG, "resp = %s", buf);
|
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
|
void
|
||||||
mschap(Ticketreq *tr)
|
mschap(Ticketreq *tr)
|
||||||
{
|
{
|
||||||
|
|
||||||
char *secret, *hkey;
|
char *secret, *hkey;
|
||||||
char sbuf[SECRETLEN], hbuf[DESKEYLEN];
|
char sbuf[SECRETLEN], hbuf[DESKEYLEN], windom[128];
|
||||||
uchar chal[CHALLEN];
|
uchar chal[CHALLEN], ntblob[1024];
|
||||||
uchar hash[MShashlen];
|
uchar hash[MShashlen];
|
||||||
uchar hash2[MShashlen];
|
uchar hash2[MShashlen];
|
||||||
uchar resp[MSresplen];
|
uchar resp[MSresplen];
|
||||||
OMSchapreply reply;
|
OMSchapreply reply;
|
||||||
int dupe, lmok, ntok;
|
int dupe, lmok, ntok, ntbloblen;
|
||||||
DigestState *s;
|
DigestState *s;
|
||||||
uchar digest[SHA1dlen];
|
uchar digest[SHA1dlen];
|
||||||
|
|
||||||
|
@ -657,6 +698,43 @@ mschap(Ticketreq *tr)
|
||||||
if(readn(0, &reply, sizeof(reply)) < 0)
|
if(readn(0, &reply, sizeof(reply)) < 0)
|
||||||
exits(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));
|
safecpy(tr->uid, reply.uid, sizeof(tr->uid));
|
||||||
/*
|
/*
|
||||||
* lookup
|
* lookup
|
||||||
|
@ -670,13 +748,33 @@ mschap(Ticketreq *tr)
|
||||||
exits(0);
|
exits(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
lmhash(hash, secret);
|
if(ntbloblen > 0){
|
||||||
mschalresp(resp, hash, chal);
|
getname(MsvAvNbDomainName, ntblob, ntbloblen, windom, sizeof(windom));
|
||||||
lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
|
ntv2hash(hash, secret, tr->uid, windom);
|
||||||
nthash(hash, secret);
|
|
||||||
mschalresp(resp, hash, chal);
|
/*
|
||||||
ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
|
* LmResponse = Cat(HMAC_MD5(LmHash, Cat(SC, CC)), CC)
|
||||||
dupe = memcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
|
*/
|
||||||
|
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
|
* It is valid to send the same response in both the LM and NTLM
|
||||||
|
@ -707,8 +805,7 @@ mschap(Ticketreq *tr)
|
||||||
exits(0);
|
exits(0);
|
||||||
|
|
||||||
if(debug)
|
if(debug)
|
||||||
replyerror("mschap-ok %s/%s(%s) %ux",
|
replyerror("mschap-ok %s/%s(%s)", tr->uid, tr->hostid, raddr);
|
||||||
tr->uid, tr->hostid, raddr);
|
|
||||||
|
|
||||||
nthash(hash, secret);
|
nthash(hash, secret);
|
||||||
md4(hash, 16, hash2, 0);
|
md4(hash, 16, hash2, 0);
|
||||||
|
@ -723,19 +820,52 @@ mschap(Ticketreq *tr)
|
||||||
void
|
void
|
||||||
nthash(uchar hash[MShashlen], char *passwd)
|
nthash(uchar hash[MShashlen], char *passwd)
|
||||||
{
|
{
|
||||||
uchar buf[512];
|
DigestState *ds;
|
||||||
int i;
|
uchar b[2];
|
||||||
|
Rune r;
|
||||||
|
|
||||||
for (i = 0; *passwd && i + 1 < sizeof(buf);) {
|
ds = md4(nil, 0, nil, nil);
|
||||||
Rune r;
|
while(*passwd){
|
||||||
passwd += chartorune(&r, passwd);
|
passwd += chartorune(&r, passwd);
|
||||||
buf[i++] = r;
|
b[0] = r & 0xff;
|
||||||
buf[i++] = r >> 8;
|
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
|
void
|
||||||
|
@ -745,12 +875,12 @@ lmhash(uchar hash[MShashlen], char *passwd)
|
||||||
char *stdtext = "KGS!@#$%";
|
char *stdtext = "KGS!@#$%";
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
strncpy((char*)buf, passwd, sizeof(buf));
|
strncpy((char*)buf, passwd, sizeof(buf));
|
||||||
for(i=0; i<sizeof(buf); i++)
|
for(i=0; i<sizeof(buf); i++)
|
||||||
if(buf[i] >= 'a' && buf[i] <= 'z')
|
if(buf[i] >= 'a' && buf[i] <= 'z')
|
||||||
buf[i] += 'A' - 'a';
|
buf[i] += 'A' - 'a';
|
||||||
|
|
||||||
memset(hash, 0, 16);
|
|
||||||
memcpy(hash, stdtext, 8);
|
memcpy(hash, stdtext, 8);
|
||||||
memcpy(hash+8, stdtext, 8);
|
memcpy(hash+8, stdtext, 8);
|
||||||
|
|
||||||
|
|
|
@ -130,14 +130,14 @@ chapwrite(Fsstate *fss, void *va, uint n)
|
||||||
{
|
{
|
||||||
int ret, nreply;
|
int ret, nreply;
|
||||||
char *a, *v;
|
char *a, *v;
|
||||||
void *reply;
|
|
||||||
Key *k;
|
Key *k;
|
||||||
Keyinfo ki;
|
Keyinfo ki;
|
||||||
State *s;
|
State *s;
|
||||||
Chapreply cr;
|
Chapreply *cr;
|
||||||
MSchapreply mcr;
|
MSchapreply *mcr;
|
||||||
OChapreply ocr;
|
OChapreply *ocr;
|
||||||
OMSchapreply omcr;
|
OMSchapreply *omcr;
|
||||||
|
uchar reply[4*1024];
|
||||||
|
|
||||||
s = fss->ps;
|
s = fss->ps;
|
||||||
a = va;
|
a = va;
|
||||||
|
@ -150,17 +150,28 @@ chapwrite(Fsstate *fss, void *va, uint n)
|
||||||
if(ret != RpcOk)
|
if(ret != RpcOk)
|
||||||
return ret;
|
return ret;
|
||||||
v = _strfindattr(k->privattr, "!password");
|
v = _strfindattr(k->privattr, "!password");
|
||||||
if(v == nil)
|
if(v == nil){
|
||||||
|
closekey(k);
|
||||||
return failure(fss, "key has no password");
|
return failure(fss, "key has no password");
|
||||||
|
}
|
||||||
setattrs(fss->attr, k->attr);
|
setattrs(fss->attr, k->attr);
|
||||||
switch(s->astype){
|
switch(s->astype){
|
||||||
default:
|
default:
|
||||||
abort();
|
closekey(k);
|
||||||
|
return failure(fss, "chap internal botch");
|
||||||
case AuthMSchap:
|
case AuthMSchap:
|
||||||
|
if(n < ChapChallen){
|
||||||
|
closekey(k);
|
||||||
|
return failure(fss, "challenge too short");
|
||||||
|
}
|
||||||
doLMchap(v, (uchar *)a, (uchar *)s->mcr.LMresp);
|
doLMchap(v, (uchar *)a, (uchar *)s->mcr.LMresp);
|
||||||
doNTchap(v, (uchar *)a, (uchar *)s->mcr.NTresp);
|
doNTchap(v, (uchar *)a, (uchar *)s->mcr.NTresp);
|
||||||
break;
|
break;
|
||||||
case AuthChap:
|
case AuthChap:
|
||||||
|
if(n < ChapChallen+1){
|
||||||
|
closekey(k);
|
||||||
|
return failure(fss, "challenge too short");
|
||||||
|
}
|
||||||
dochap(v, *a, a+1, (uchar *)s->cr);
|
dochap(v, *a, a+1, (uchar *)s->cr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -181,26 +192,28 @@ chapwrite(Fsstate *fss, void *va, uint n)
|
||||||
default:
|
default:
|
||||||
return failure(fss, "chap internal botch");
|
return failure(fss, "chap internal botch");
|
||||||
case AuthChap:
|
case AuthChap:
|
||||||
if(n != sizeof(Chapreply))
|
if(n != sizeof(*cr))
|
||||||
return failure(fss, "did not get Chapreply");
|
return failure(fss, "did not get Chapreply");
|
||||||
memmove(&cr, va, sizeof cr);
|
cr = (Chapreply*)va;
|
||||||
ocr.id = cr.id;
|
nreply = sizeof(*ocr);
|
||||||
memmove(ocr.resp, cr.resp, sizeof ocr.resp);
|
memset(reply, 0, nreply);
|
||||||
memset(omcr.uid, 0, sizeof(omcr.uid));
|
ocr = (OChapreply*)reply;
|
||||||
strecpy(ocr.uid, ocr.uid+sizeof ocr.uid, s->user);
|
strecpy(ocr->uid, ocr->uid+sizeof(ocr->uid), s->user);
|
||||||
reply = &ocr;
|
ocr->id = cr->id;
|
||||||
nreply = sizeof ocr;
|
memmove(ocr->resp, cr->resp, sizeof(ocr->resp));
|
||||||
break;
|
break;
|
||||||
case AuthMSchap:
|
case AuthMSchap:
|
||||||
if(n != sizeof(MSchapreply))
|
if(n < sizeof(*mcr))
|
||||||
return failure(fss, "did not get MSchapreply");
|
return failure(fss, "did not get MSchapreply");
|
||||||
memmove(&mcr, va, sizeof mcr);
|
if(n > sizeof(reply)+sizeof(*mcr)-sizeof(*omcr))
|
||||||
memmove(omcr.LMresp, mcr.LMresp, sizeof omcr.LMresp);
|
return failure(fss, "MSchapreply too long");
|
||||||
memmove(omcr.NTresp, mcr.NTresp, sizeof omcr.NTresp);
|
mcr = (MSchapreply*)va;
|
||||||
memset(omcr.uid, 0, sizeof(omcr.uid));
|
nreply = n+sizeof(*omcr)-sizeof(*mcr);
|
||||||
strecpy(omcr.uid, omcr.uid+sizeof omcr.uid, s->user);
|
memset(reply, 0, nreply);
|
||||||
reply = &omcr;
|
omcr = (OMSchapreply*)reply;
|
||||||
nreply = sizeof omcr;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
if(doreply(s, reply, nreply) < 0)
|
if(doreply(s, reply, nreply) < 0)
|
||||||
|
|
|
@ -99,22 +99,25 @@ smbsessionsetupandx(Req *r, uchar *h, uchar *p, uchar *e)
|
||||||
logit("ignoring bad session key");
|
logit("ignoring bad session key");
|
||||||
while(!remoteuser){
|
while(!remoteuser){
|
||||||
if(needauth){
|
if(needauth){
|
||||||
MSchapreply mcr;
|
MSchapreply *mcr;
|
||||||
|
|
||||||
if(smbcs == nil || strlen(user) == 0)
|
if(smbcs == nil || strlen(user) == 0)
|
||||||
break;
|
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->user = user;
|
||||||
smbcs->resp = &mcr;
|
smbcs->nresp = (nte - nt)+sizeof(*mcr)-sizeof(mcr->NTresp);
|
||||||
smbcs->nresp = sizeof(mcr);
|
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)
|
if((ai = auth_response(smbcs)) == nil)
|
||||||
logit("auth_response: %r");
|
logit("auth_response: %r");
|
||||||
auth_freechal(smbcs);
|
auth_freechal(smbcs);
|
||||||
smbcs = nil;
|
smbcs = nil;
|
||||||
|
free(mcr);
|
||||||
if(ai == nil)
|
if(ai == nil)
|
||||||
break;
|
break;
|
||||||
if(auth_chuid(ai, nil) < 0)
|
if(auth_chuid(ai, nil) < 0)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue