separate MSCHAP(v2) and NTLM(v2) authentication

due to linux omiting the final Z(4) in the NTLMv2 reply, and
the need for the windom for LMv2 authentication, here is a new
AuthNTLM ticket request now with length and dom fields.
This commit is contained in:
cinap_lenrek 2018-05-20 22:49:24 +02:00
parent 40f6e00b9c
commit 6e19d19285
8 changed files with 92 additions and 27 deletions

View file

@ -53,6 +53,7 @@ struct AuthInfo
struct Chalstate struct Chalstate
{ {
char *user; char *user;
char *dom;
char chal[MAXCHLEN]; char chal[MAXCHLEN];
int nchal; int nchal;
void *resp; void *resp;
@ -71,7 +72,7 @@ struct Chapreply /* for protocol "chap" */
char resp[MD5LEN]; char resp[MD5LEN];
}; };
struct MSchapreply /* for protocol "mschap" */ struct MSchapreply /* for protocol "mschap" and "ntlm" */
{ {
char LMresp[24]; /* Lan Manager response */ char LMresp[24]; /* Lan Manager response */
char NTresp[24]; /* NT response */ char NTresp[24]; /* NT response */

View file

@ -11,6 +11,7 @@ typedef struct Nvrsafe Nvrsafe;
typedef struct Passwordreq Passwordreq; typedef struct Passwordreq Passwordreq;
typedef struct OChapreply OChapreply; typedef struct OChapreply OChapreply;
typedef struct OMSchapreply OMSchapreply; typedef struct OMSchapreply OMSchapreply;
typedef struct NTLMreply NTLMreply;
typedef struct Authkey Authkey; typedef struct Authkey Authkey;
@ -62,6 +63,7 @@ enum
AuthVNC=14, /* VNC server login (deprecated) */ AuthVNC=14, /* VNC server login (deprecated) */
AuthPAK=19, /* authenticated diffie hellman key agreement */ AuthPAK=19, /* authenticated diffie hellman key agreement */
AuthMSchapv2=21,/* MS chap v2 authentication for ppp */ AuthMSchapv2=21,/* MS chap v2 authentication for ppp */
AuthNTLM=22, /* NTLM authentication for cifs */
AuthTs=64, /* ticket encrypted with server's key */ AuthTs=64, /* ticket encrypted with server's key */
AuthTc, /* ticket encrypted with client's key */ AuthTc, /* ticket encrypted with client's key */
AuthAs, /* server generated authenticator */ AuthAs, /* server generated authenticator */
@ -127,6 +129,16 @@ struct OMSchapreply
}; };
#define OMSCHAPREPLYLEN (ANAMELEN+24+24) #define OMSCHAPREPLYLEN (ANAMELEN+24+24)
struct NTLMreply
{
uchar len[2]; /* size of structure (lsb first) */
char uid[ANAMELEN];
char dom[DOMLEN];
char LMresp[24]; /* Lan Manager response */
char NTresp[24]; /* NT response (variable length) */
};
#define NTLMREPLYLEN (2+ANAMELEN+DOMLEN+24+24)
struct Authkey struct Authkey
{ {
char des[DESKEYLEN]; /* DES key from password */ char des[DESKEYLEN]; /* DES key from password */

View file

@ -10,6 +10,7 @@
* Server protocol: * Server protocol:
* 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 dom: utf8 (ntlm)
* write response: Chapreply or MSchapreply structure * write response: Chapreply or MSchapreply structure
* ... retry another user * ... retry another user
*/ */
@ -36,7 +37,7 @@ static int dochal(State *s);
static int doreply(State *s, uchar *reply, int nreply); static int doreply(State *s, uchar *reply, int nreply);
static int dochap(char *passwd, int id, char chal[ChapChallen], uchar *resp, int resplen); static int dochap(char *passwd, int id, char chal[ChapChallen], uchar *resp, int resplen);
static int domschap(char *passwd, uchar chal[MSchallen], uchar *resp, int resplen); static int domschap(char *passwd, uchar chal[MSchallen], uchar *resp, int resplen);
static int domschap2(char *passwd, char *user, char *dom, uchar chal[MSchallen], uchar *resp, int resplen); static int dontlmv2(char *passwd, char *user, char *dom, uchar chal[MSchallen], uchar *resp, int resplen);
static void nthash(uchar hash[MShashlen], char *passwd); static void nthash(uchar hash[MShashlen], char *passwd);
struct State struct State
@ -53,6 +54,7 @@ struct State
uchar resp[4096]; uchar resp[4096];
char err[ERRMAX]; char err[ERRMAX];
char user[64]; char user[64];
char dom[DOMLEN];
uchar secret[16+20]; /* for mschap: MPPE Master secret + authenticator (v2) */ uchar secret[16+20]; /* for mschap: MPPE Master secret + authenticator (v2) */
int nsecret; int nsecret;
}; };
@ -64,6 +66,7 @@ enum
SHaveChal, SHaveChal,
SNeedUser, SNeedUser,
SNeedDom,
SNeedResp, SNeedResp,
Maxphase Maxphase
@ -76,6 +79,7 @@ static char *phasenames[Maxphase] =
[SHaveChal] "SHaveChal", [SHaveChal] "SHaveChal",
[SNeedUser] "SNeedUser", [SNeedUser] "SNeedUser",
[SNeedDom] "SNeedDom",
[SNeedResp] "SNeedResp", [SNeedResp] "SNeedResp",
}; };
@ -98,15 +102,16 @@ chapinit(Proto *p, Fsstate *fss)
if(p == &mschapv2) { if(p == &mschapv2) {
s->nchal = MSchallenv2; s->nchal = MSchallenv2;
s->astype = AuthMSchapv2; s->astype = AuthMSchapv2;
} else { } else if(p == &mschap){
if(p == &mschap || p == &mschap2){
s->nchal = MSchallen; s->nchal = MSchallen;
s->astype = AuthMSchap; s->astype = AuthMSchap;
} else if(p == &ntlm || p == &ntlmv2){
s->nchal = MSchallen;
s->astype = AuthNTLM;
} else { } else {
s->nchal = ChapChallen; s->nchal = ChapChallen;
s->astype = AuthChap; s->astype = AuthChap;
} }
}
if(iscli) if(iscli)
fss->phase = CNeedChal; fss->phase = CNeedChal;
else{ else{
@ -150,6 +155,7 @@ chapwrite(Fsstate *fss, void *va, uint n)
MSchapreply *mcr; MSchapreply *mcr;
OChapreply *ocr; OChapreply *ocr;
OMSchapreply *omcr; OMSchapreply *omcr;
NTLMreply *ntcr;
uchar pchal[MSchallenv2]; uchar pchal[MSchallenv2];
uchar digest[SHA1dlen]; uchar digest[SHA1dlen];
uchar reply[4096]; uchar reply[4096];
@ -177,20 +183,25 @@ chapwrite(Fsstate *fss, void *va, uint n)
memset(s->resp, 0, sizeof(s->resp)); memset(s->resp, 0, sizeof(s->resp));
setattrs(fss->attr, k->attr); setattrs(fss->attr, k->attr);
switch(s->astype){ switch(s->astype){
case AuthMSchap: case AuthNTLM:
if(n < MSchallen) if(n < MSchallen)
break; break;
if(fss->proto == &mschap2){ if(fss->proto == &ntlmv2){
user = _strfindattr(fss->attr, "user"); user = _strfindattr(fss->attr, "user");
if(user == nil) if(user == nil)
break; break;
dom = _strfindattr(fss->attr, "windom"); dom = _strfindattr(fss->attr, "windom");
if(dom == nil) if(dom == nil)
dom = ""; dom = "";
s->nresp = domschap2(pass, user, dom, (uchar*)a, s->resp, sizeof(s->resp)); s->nresp = dontlmv2(pass, user, dom, (uchar*)a, s->resp, sizeof(s->resp));
} else { } else {
s->nresp = domschap(pass, (uchar*)a, s->resp, sizeof(s->resp)); s->nresp = domschap(pass, (uchar*)a, s->resp, sizeof(s->resp));
} }
break;
case AuthMSchap:
if(n < MSchallen)
break;
s->nresp = domschap(pass, (uchar*)a, s->resp, sizeof(s->resp));
nthash(digest, pass); nthash(digest, pass);
md4(digest, 16, digest, nil); md4(digest, 16, digest, nil);
ds = sha1(digest, 16, nil, nil); ds = sha1(digest, 16, nil, nil);
@ -253,6 +264,14 @@ chapwrite(Fsstate *fss, void *va, uint n)
return failure(fss, "user name too long"); return failure(fss, "user name too long");
memmove(s->user, va, n); memmove(s->user, va, n);
s->user[n] = '\0'; s->user[n] = '\0';
fss->phase = (s->astype == AuthNTLM)? SNeedDom: SNeedResp;
return RpcOk;
case SNeedDom:
if(n >= sizeof s->dom)
return failure(fss, "domain name too long");
memmove(s->dom, va, n);
s->dom[n] = '\0';
fss->phase = SNeedResp; fss->phase = SNeedResp;
return RpcOk; return RpcOk;
@ -275,15 +294,29 @@ chapwrite(Fsstate *fss, void *va, uint n)
case AuthMSchapv2: case AuthMSchapv2:
if(n < MSchapreplylen) if(n < MSchapreplylen)
return failure(fss, "did not get MSchapreply"); return failure(fss, "did not get MSchapreply");
if(n > sizeof(reply)+MSchapreplylen-OMSCHAPREPLYLEN)
return failure(fss, "MSchapreply too long");
mcr = (MSchapreply*)va; mcr = (MSchapreply*)va;
nreply = n+OMSCHAPREPLYLEN-MSchapreplylen; nreply = OMSCHAPREPLYLEN;
memset(reply, 0, nreply); memset(reply, 0, nreply);
omcr = (OMSchapreply*)reply; omcr = (OMSchapreply*)reply;
strecpy(omcr->uid, omcr->uid+sizeof(omcr->uid), s->user); strecpy(omcr->uid, omcr->uid+sizeof(omcr->uid), s->user);
memmove(omcr->LMresp, mcr->LMresp, sizeof(omcr->LMresp)); memmove(omcr->LMresp, mcr->LMresp, sizeof(omcr->LMresp));
memmove(omcr->NTresp, mcr->NTresp, n+sizeof(mcr->NTresp)-MSchapreplylen); memmove(omcr->NTresp, mcr->NTresp, sizeof(mcr->NTresp));
break;
case AuthNTLM:
if(n < MSchapreplylen)
return failure(fss, "did not get MSchapreply");
if(n > sizeof(reply)+MSchapreplylen-NTLMREPLYLEN)
return failure(fss, "MSchapreply too long");
mcr = (MSchapreply*)va;
nreply = n+NTLMREPLYLEN-MSchapreplylen;
memset(reply, 0, nreply);
ntcr = (NTLMreply*)reply;
ntcr->len[0] = nreply;
ntcr->len[1] = nreply>>8;
strecpy(ntcr->uid, ntcr->uid+sizeof(ntcr->uid), s->user);
strecpy(ntcr->dom, ntcr->dom+sizeof(ntcr->dom), s->dom);
memmove(ntcr->LMresp, mcr->LMresp, sizeof(ntcr->LMresp));
memmove(ntcr->NTresp, mcr->NTresp, n+sizeof(mcr->NTresp)-MSchapreplylen);
break; break;
} }
if(doreply(s, reply, nreply) < 0){ if(doreply(s, reply, nreply) < 0){
@ -402,12 +435,12 @@ doreply(State *s, uchar *reply, int nreply)
werrstr(Easproto); werrstr(Easproto);
return -1; return -1;
} }
s->key->successes++;
if(a.num != AuthAc if(a.num != AuthAc
|| tsmemcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0){ || tsmemcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0){
werrstr(Easproto); werrstr(Easproto);
return -1; return -1;
} }
s->key->successes++;
s->nsecret = 0; s->nsecret = 0;
if(s->t.form != 0){ if(s->t.form != 0){
if(s->astype == AuthMSchap || s->astype == AuthMSchapv2){ if(s->astype == AuthMSchap || s->astype == AuthMSchapv2){
@ -457,8 +490,18 @@ Proto mschapv2 = {
.keyprompt= "user? !password?" .keyprompt= "user? !password?"
}; };
Proto mschap2 = { Proto ntlm = {
.name= "mschap2", /* really NTLMv2 */ .name= "ntlm",
.init= chapinit,
.write= chapwrite,
.read= chapread,
.close= chapclose,
.addkey= replacekey,
.keyprompt= "user? !password?"
};
Proto ntlmv2 = {
.name= "ntlmv2",
.init= chapinit, .init= chapinit,
.write= chapwrite, .write= chapwrite,
.read= chapread, .read= chapread,
@ -582,7 +625,7 @@ domschap(char *passwd, uchar chal[MSchallen], uchar *resp, int resplen)
} }
static int static int
domschap2(char *passwd, char *user, char *dom, uchar chal[MSchallen], uchar *resp, int resplen) dontlmv2(char *passwd, char *user, char *dom, uchar chal[MSchallen], uchar *resp, int resplen)
{ {
uchar hash[MShashlen], *p, *e; uchar hash[MShashlen], *p, *e;
MSchapreply *r; MSchapreply *r;

View file

@ -221,7 +221,7 @@ void writehostowner(char*);
/* protocols */ /* protocols */
extern Proto apop, cram; /* apop.c */ extern Proto apop, cram; /* apop.c */
extern Proto p9any, p9sk1, dp9ik; /* p9sk.c */ extern Proto p9any, p9sk1, dp9ik; /* p9sk.c */
extern Proto chap, mschap, mschapv2, mschap2; /* chap.c */ extern Proto chap, mschap, mschapv2, ntlm, ntlmv2; /* chap.c */
extern Proto p9cr, vnc; /* p9cr.c */ extern Proto p9cr, vnc; /* p9cr.c */
extern Proto pass; /* pass.c */ extern Proto pass; /* pass.c */
extern Proto rsa; /* rsa.c */ extern Proto rsa; /* rsa.c */

View file

@ -32,7 +32,8 @@ prototab[] =
&httpdigest, &httpdigest,
&mschap, &mschap,
&mschapv2, &mschapv2,
&mschap2, &ntlm,
&ntlmv2,
&p9any, &p9any,
&p9cr, &p9cr,
&p9sk1, &p9sk1,

View file

@ -78,8 +78,6 @@ auth_proto(char *proto, char *windom, char *keyp, uchar *chal, int len)
mcr = (MSchapreply*)resp; mcr = (MSchapreply*)resp;
nresp = sizeof(resp); nresp = sizeof(resp);
if(strcmp(proto, "mschap") == 0)
nresp = sizeof(*mcr); /* backwards compatibility with old factotum */
nresp = auth_respond(chal, len, user, sizeof user, resp, nresp, nresp = auth_respond(chal, len, user, sizeof user, resp, nresp,
auth_getkey, "proto=%s role=client service=cifs windom=%s %s", auth_getkey, "proto=%s role=client service=cifs windom=%s %s",
proto, windom, keyp); proto, windom, keyp);
@ -109,7 +107,7 @@ auth_proto(char *proto, char *windom, char *keyp, uchar *chal, int len)
static Auth * static Auth *
auth_lm_and_ntlm(char *windom, char *keyp, uchar *chal, int len) auth_lm_and_ntlm(char *windom, char *keyp, uchar *chal, int len)
{ {
return auth_proto("mschap", windom, keyp, chal, len); return auth_proto("ntlm", windom, keyp, chal, len);
} }
/* /*
@ -136,7 +134,7 @@ auth_ntlm(char *windom, char *keyp, uchar *chal, int len)
static Auth * static Auth *
auth_ntlmv2(char *windom, char *keyp, uchar *chal, int len) auth_ntlmv2(char *windom, char *keyp, uchar *chal, int len)
{ {
return auth_proto("mschap2", windom, keyp, chal, len); return auth_proto("ntlmv2", windom, keyp, chal, len);
} }
struct { struct {

View file

@ -46,7 +46,7 @@ err:
if(needauth){ if(needauth){
if(smbcs != nil) if(smbcs != nil)
auth_freechal(smbcs); auth_freechal(smbcs);
if(smbcs = auth_challenge("proto=mschap role=server")){ if(smbcs = auth_challenge("proto=ntlm role=server")){
c = (uchar*)smbcs->chal; c = (uchar*)smbcs->chal;
ce = c + smbcs->nchal; ce = c + smbcs->nchal;
mode = NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS; mode = NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS;
@ -104,6 +104,7 @@ smbsessionsetupandx(Req *r, uchar *h, uchar *p, uchar *e)
if(smbcs == nil || strlen(user) == 0) if(smbcs == nil || strlen(user) == 0)
break; break;
smbcs->user = user; smbcs->user = user;
smbcs->dom = dom;
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);

View file

@ -74,6 +74,15 @@ auth_response(Chalstate *c)
goto Out; goto Out;
} }
} }
if(c->dom){
if(auth_rpc(c->rpc, "write", c->dom, strlen(c->dom)) != ARok){
/*
* if this fails we're out of phase with factotum.
* give up.
*/
goto Out;
}
}
if(auth_rpc(c->rpc, "write", c->resp, c->nresp) != ARok){ if(auth_rpc(c->rpc, "write", c->resp, c->nresp) != ARok){
/* /*