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:
parent
40f6e00b9c
commit
6e19d19285
8 changed files with 92 additions and 27 deletions
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -32,7 +32,8 @@ prototab[] =
|
||||||
&httpdigest,
|
&httpdigest,
|
||||||
&mschap,
|
&mschap,
|
||||||
&mschapv2,
|
&mschapv2,
|
||||||
&mschap2,
|
&ntlm,
|
||||||
|
&ntlmv2,
|
||||||
&p9any,
|
&p9any,
|
||||||
&p9cr,
|
&p9cr,
|
||||||
&p9sk1,
|
&p9sk1,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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){
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue