From 6e19d19285dddcd3c8f2a6b031fc0f0324b3e48b Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sun, 20 May 2018 22:49:24 +0200 Subject: [PATCH] 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. --- sys/include/auth.h | 3 +- sys/include/authsrv.h | 12 +++++ sys/src/cmd/auth/factotum/chap.c | 81 ++++++++++++++++++++++++-------- sys/src/cmd/auth/factotum/dat.h | 2 +- sys/src/cmd/auth/factotum/fs.c | 3 +- sys/src/cmd/cifs/auth.c | 6 +-- sys/src/cmd/ip/cifsd/smb.c | 3 +- sys/src/libauth/auth_challenge.c | 9 ++++ 8 files changed, 92 insertions(+), 27 deletions(-) diff --git a/sys/include/auth.h b/sys/include/auth.h index d7f58035e..8b350a64c 100644 --- a/sys/include/auth.h +++ b/sys/include/auth.h @@ -53,6 +53,7 @@ struct AuthInfo struct Chalstate { char *user; + char *dom; char chal[MAXCHLEN]; int nchal; void *resp; @@ -71,7 +72,7 @@ struct Chapreply /* for protocol "chap" */ char resp[MD5LEN]; }; -struct MSchapreply /* for protocol "mschap" */ +struct MSchapreply /* for protocol "mschap" and "ntlm" */ { char LMresp[24]; /* Lan Manager response */ char NTresp[24]; /* NT response */ diff --git a/sys/include/authsrv.h b/sys/include/authsrv.h index 5c0b368e7..8fb2d08df 100644 --- a/sys/include/authsrv.h +++ b/sys/include/authsrv.h @@ -11,6 +11,7 @@ typedef struct Nvrsafe Nvrsafe; typedef struct Passwordreq Passwordreq; typedef struct OChapreply OChapreply; typedef struct OMSchapreply OMSchapreply; +typedef struct NTLMreply NTLMreply; typedef struct Authkey Authkey; @@ -62,6 +63,7 @@ enum AuthVNC=14, /* VNC server login (deprecated) */ AuthPAK=19, /* authenticated diffie hellman key agreement */ AuthMSchapv2=21,/* MS chap v2 authentication for ppp */ + AuthNTLM=22, /* NTLM authentication for cifs */ AuthTs=64, /* ticket encrypted with server's key */ AuthTc, /* ticket encrypted with client's key */ AuthAs, /* server generated authenticator */ @@ -127,6 +129,16 @@ struct OMSchapreply }; #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 { char des[DESKEYLEN]; /* DES key from password */ diff --git a/sys/src/cmd/auth/factotum/chap.c b/sys/src/cmd/auth/factotum/chap.c index 56ad476c3..ae8cd6097 100644 --- a/sys/src/cmd/auth/factotum/chap.c +++ b/sys/src/cmd/auth/factotum/chap.c @@ -10,6 +10,7 @@ * Server protocol: * read challenge: 8 bytes binary (or 16 bytes for mschapv2) * write user: utf8 + * write dom: utf8 (ntlm) * write response: Chapreply or MSchapreply structure * ... retry another user */ @@ -36,7 +37,7 @@ static int dochal(State *s); 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 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); struct State @@ -53,6 +54,7 @@ struct State uchar resp[4096]; char err[ERRMAX]; char user[64]; + char dom[DOMLEN]; uchar secret[16+20]; /* for mschap: MPPE Master secret + authenticator (v2) */ int nsecret; }; @@ -64,6 +66,7 @@ enum SHaveChal, SNeedUser, + SNeedDom, SNeedResp, Maxphase @@ -76,6 +79,7 @@ static char *phasenames[Maxphase] = [SHaveChal] "SHaveChal", [SNeedUser] "SNeedUser", +[SNeedDom] "SNeedDom", [SNeedResp] "SNeedResp", }; @@ -98,14 +102,15 @@ chapinit(Proto *p, Fsstate *fss) if(p == &mschapv2) { s->nchal = MSchallenv2; s->astype = AuthMSchapv2; + } else if(p == &mschap){ + s->nchal = MSchallen; + s->astype = AuthMSchap; + } else if(p == &ntlm || p == &ntlmv2){ + s->nchal = MSchallen; + s->astype = AuthNTLM; } else { - if(p == &mschap || p == &mschap2){ - s->nchal = MSchallen; - s->astype = AuthMSchap; - }else { - s->nchal = ChapChallen; - s->astype = AuthChap; - } + s->nchal = ChapChallen; + s->astype = AuthChap; } if(iscli) fss->phase = CNeedChal; @@ -150,6 +155,7 @@ chapwrite(Fsstate *fss, void *va, uint n) MSchapreply *mcr; OChapreply *ocr; OMSchapreply *omcr; + NTLMreply *ntcr; uchar pchal[MSchallenv2]; uchar digest[SHA1dlen]; uchar reply[4096]; @@ -177,20 +183,25 @@ chapwrite(Fsstate *fss, void *va, uint n) memset(s->resp, 0, sizeof(s->resp)); setattrs(fss->attr, k->attr); switch(s->astype){ - case AuthMSchap: + case AuthNTLM: if(n < MSchallen) break; - if(fss->proto == &mschap2){ + if(fss->proto == &ntlmv2){ user = _strfindattr(fss->attr, "user"); if(user == nil) break; dom = _strfindattr(fss->attr, "windom"); if(dom == nil) 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 { 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); md4(digest, 16, digest, 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"); memmove(s->user, va, n); 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; return RpcOk; @@ -275,15 +294,29 @@ chapwrite(Fsstate *fss, void *va, uint n) case AuthMSchapv2: if(n < MSchapreplylen) return failure(fss, "did not get MSchapreply"); - if(n > sizeof(reply)+MSchapreplylen-OMSCHAPREPLYLEN) - return failure(fss, "MSchapreply too long"); mcr = (MSchapreply*)va; - nreply = n+OMSCHAPREPLYLEN-MSchapreplylen; + nreply = OMSCHAPREPLYLEN; memset(reply, 0, nreply); omcr = (OMSchapreply*)reply; 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)-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; } if(doreply(s, reply, nreply) < 0){ @@ -402,12 +435,12 @@ doreply(State *s, uchar *reply, int nreply) werrstr(Easproto); return -1; } - s->key->successes++; if(a.num != AuthAc || tsmemcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0){ werrstr(Easproto); return -1; } + s->key->successes++; s->nsecret = 0; if(s->t.form != 0){ if(s->astype == AuthMSchap || s->astype == AuthMSchapv2){ @@ -457,8 +490,18 @@ Proto mschapv2 = { .keyprompt= "user? !password?" }; -Proto mschap2 = { -.name= "mschap2", /* really NTLMv2 */ +Proto ntlm = { +.name= "ntlm", +.init= chapinit, +.write= chapwrite, +.read= chapread, +.close= chapclose, +.addkey= replacekey, +.keyprompt= "user? !password?" +}; + +Proto ntlmv2 = { +.name= "ntlmv2", .init= chapinit, .write= chapwrite, .read= chapread, @@ -582,7 +625,7 @@ 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) +dontlmv2(char *passwd, char *user, char *dom, uchar chal[MSchallen], uchar *resp, int resplen) { uchar hash[MShashlen], *p, *e; MSchapreply *r; diff --git a/sys/src/cmd/auth/factotum/dat.h b/sys/src/cmd/auth/factotum/dat.h index 19893aa6e..d51bf919c 100644 --- a/sys/src/cmd/auth/factotum/dat.h +++ b/sys/src/cmd/auth/factotum/dat.h @@ -221,7 +221,7 @@ void writehostowner(char*); /* protocols */ extern Proto apop, cram; /* apop.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 pass; /* pass.c */ extern Proto rsa; /* rsa.c */ diff --git a/sys/src/cmd/auth/factotum/fs.c b/sys/src/cmd/auth/factotum/fs.c index 13e04ce22..3756f9a31 100644 --- a/sys/src/cmd/auth/factotum/fs.c +++ b/sys/src/cmd/auth/factotum/fs.c @@ -32,7 +32,8 @@ prototab[] = &httpdigest, &mschap, &mschapv2, - &mschap2, + &ntlm, + &ntlmv2, &p9any, &p9cr, &p9sk1, diff --git a/sys/src/cmd/cifs/auth.c b/sys/src/cmd/cifs/auth.c index cc334889a..ce501b808 100644 --- a/sys/src/cmd/cifs/auth.c +++ b/sys/src/cmd/cifs/auth.c @@ -78,8 +78,6 @@ auth_proto(char *proto, char *windom, char *keyp, uchar *chal, int len) mcr = (MSchapreply*)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, auth_getkey, "proto=%s role=client service=cifs windom=%s %s", proto, windom, keyp); @@ -109,7 +107,7 @@ auth_proto(char *proto, char *windom, char *keyp, uchar *chal, int len) static Auth * 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 * 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 { diff --git a/sys/src/cmd/ip/cifsd/smb.c b/sys/src/cmd/ip/cifsd/smb.c index af2182097..45a9aff55 100644 --- a/sys/src/cmd/ip/cifsd/smb.c +++ b/sys/src/cmd/ip/cifsd/smb.c @@ -46,7 +46,7 @@ err: if(needauth){ if(smbcs != nil) auth_freechal(smbcs); - if(smbcs = auth_challenge("proto=mschap role=server")){ + if(smbcs = auth_challenge("proto=ntlm role=server")){ c = (uchar*)smbcs->chal; ce = c + smbcs->nchal; 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) break; smbcs->user = user; + smbcs->dom = dom; smbcs->nresp = (nte - nt)+sizeof(*mcr)-sizeof(mcr->NTresp); if(smbcs->nresp < sizeof(*mcr)) smbcs->nresp = sizeof(*mcr); diff --git a/sys/src/libauth/auth_challenge.c b/sys/src/libauth/auth_challenge.c index d21841a05..5ab0ee6a1 100644 --- a/sys/src/libauth/auth_challenge.c +++ b/sys/src/libauth/auth_challenge.c @@ -74,6 +74,15 @@ auth_response(Chalstate *c) 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){ /*