allow ntlmv2 ntlmssp authentication

Shirish Pargaonkar shirishpargaonkar at gmail.com
Wed Jul 7 10:52:16 MDT 2010


On Wed, Jul 7, 2010 at 6:25 AM, Jeff Layton <jlayton at samba.org> wrote:
> On Fri,  2 Jul 2010 22:16:03 -0500
> shirishpargaonkar at gmail.com wrote:
>
>> Have kept ntlmv1 ntlmssp as a default, ntlmv2 ntlmssp can
>> be made default only by making code change.
>>
>>
>> From 3d8a8960a6d164e2bacd2a4fc96456453042e049 Mon Sep 17 00:00:00 2001
>> From: Shirish Pargaonkar <shirishpargaonkar at gmail.com>
>> Date: Fri, 2 Jul 2010 21:54:51 -0500
>> Subject: [PATCH] add ntlvm2 ntlmssp authentication
>>
>> Signed-off-by: Shirish Pargaonkar <shirishpargaonkar at gmail.com>
>> ---
>>  fs/cifs/cifsencrypt.c |   48 ++++++++++++++++++++++++++++++++++++++++++++----
>>  fs/cifs/cifsglob.h    |    2 ++
>>  fs/cifs/cifspdu.h     |    1 -
>>  fs/cifs/sess.c        |   39 +++++++++++++++++++++++++++++++++++++--
>>  4 files changed, 83 insertions(+), 7 deletions(-)
>>
>> diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
>> index 847628d..ebfafec 100644
>> --- a/fs/cifs/cifsencrypt.c
>> +++ b/fs/cifs/cifsencrypt.c
>> @@ -378,6 +378,44 @@ calc_exit_2:
>>       return rc;
>>  }
>>
>> +static void
>> +find_domain_name(struct cifsSesInfo *ses)
>> +{
>> +     unsigned int attrsize;
>> +     unsigned int type;
>> +     unsigned char *blobptr;
>> +     struct ntlmssp2_name *attrptr;
>> +
>> +     if (ses->server->tiblob) {
>> +             blobptr = ses->server->tiblob;
>> +             attrptr = (struct ntlmssp2_name *) blobptr;
>> +
>> +             while ((type = attrptr->type) != 0) {
>> +                     blobptr += 2; /* advance attr type */
>> +                     attrsize = attrptr->length;
>> +                     blobptr += 2; /* advance attr size */
>> +                     if (type == 0x2) {
>                                    ^^^
>                Magic numbers like this should be turned into names.

Yes, will make the change.

>
>> +                             if (!ses->domainName) {
>> +                                     ses->domainName =
>> +                                             kmalloc(attrptr->length + 1,
>> +                                                             GFP_KERNEL);
>> +                                     if (!ses->domainName)
>> +                                             return;
>> +                                     cifs_from_ucs2(ses->domainName,
>> +                                             (__le16 *)blobptr,
>> +                                             attrptr->length,
>> +                                             attrptr->length,
>> +                                             load_nls_default(), false);
>> +                             }
>> +                     }
>> +                     blobptr += attrsize; /* advance attr  value */
>> +                     attrptr = (struct ntlmssp2_name *) blobptr;
>> +             }
>> +     }
>> +
>> +     return;
>> +}
>> +
>
> Is it possible that you'll need to pick other UCS2 fields than the
> domain out of a NTLMSSP blob? If so, it might be better to turn the
> above function into a "copy the field of type X out of the blob"
> function.

I have not seen enough responses where a server does not
return a domain name field in the AV pairs it sends in type 2
message of NTLMSSP protocol.
NTLM v2 protocol is supposed to use server name in case of
servers of with local accounts.  I will have to do some code
changes and testing to determine whether authentication
succeeds or not using server name instead of domain name
returned in type 2 challenge packet.

>
>>  void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf,
>>                     const struct nls_table *nls_cp)
>>  {
>> @@ -390,10 +428,9 @@ void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf,
>>       buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
>>       get_random_bytes(&buf->client_chal, sizeof(buf->client_chal));
>>       buf->reserved2 = 0;
>> -     buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE);
>> -     buf->names[0].length = 0;
>> -     buf->names[1].type = 0;
>> -     buf->names[1].length = 0;
>> +
>> +     if (!ses->domainName)
>> +             find_domain_name(ses);
>>
>>       /* calculate buf->ntlmv2_hash */
>>       rc = calc_ntlmv2_hash(ses, nls_cp);
>> @@ -421,6 +458,9 @@ void CalcNTLMv2_response(const struct cifsSesInfo *ses,
>>
>>       hmac_md5_update(v2_session_response+8,
>>                       sizeof(struct ntlmv2_resp) - 8, &context);
>> +     if (ses->server->tilen)
>> +             hmac_md5_update(ses->server->tiblob,
>> +                     ses->server->tilen, &context);
>>
>>       hmac_md5_final(v2_session_response, &context);
>>  /*   cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */
>> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
>> index 3c55e10..db6c5da 100644
>> --- a/fs/cifs/cifsglob.h
>> +++ b/fs/cifs/cifsglob.h
>> @@ -188,6 +188,8 @@ struct TCP_Server_Info {
>>       unsigned long lstrp; /* when we got last response from this server */
>>       u16 dialect; /* dialect index that server chose */
>>       /* extended security flavors that server supports */
>> +     unsigned int tilen; /* length of the target info blob */
>> +     unsigned char *tiblob; /* target info blob in challenge response */
>
> All of this is for NTLMSSP session setup, correct? If so, is the
> TCP_Server_Info really the right place for this? Will this break if I
> have more than one session on the same socket?

I think it will not break authentication but will break signing (and
perhaps sealing).
What would be a good place to hang is per session, per user information?

>
>>       bool    sec_kerberos;           /* supports plain Kerberos */
>>       bool    sec_mskerberos;         /* supports legacy MS Kerberos */
>>       bool    sec_kerberosu2u;        /* supports U2U Kerberos */
>> diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
>> index 14d036d..b0f4b56 100644
>> --- a/fs/cifs/cifspdu.h
>> +++ b/fs/cifs/cifspdu.h
>> @@ -663,7 +663,6 @@ struct ntlmv2_resp {
>>       __le64  time;
>>       __u64  client_chal; /* random */
>>       __u32  reserved2;
>> -     struct ntlmssp2_name names[2];
>>       /* array of name entries could follow ending in minimum 4 byte struct */
>>  } __attribute__((packed));
>>
>> diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
>> index 0a57cb7..d1ec053 100644
>> --- a/fs/cifs/sess.c
>> +++ b/fs/cifs/sess.c
>> @@ -383,6 +383,9 @@ static int decode_ascii_ssetup(char **pbcc_area, int bleft,
>>  static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
>>                                   struct cifsSesInfo *ses)
>>  {
>> +     unsigned int tioffset; /* challeng message target info area */
>> +     unsigned int tilen; /* challeng message target info area length  */
>> +
>>       CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr;
>>
>>       if (blob_len < sizeof(CHALLENGE_MESSAGE)) {
>> @@ -405,6 +408,18 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
>>       /* BB spec says that if AvId field of MsvAvTimestamp is populated then
>>               we must set the MIC field of the AUTHENTICATE_MESSAGE */
>>
>> +     tioffset = cpu_to_le16(pblob->TargetInfoArray.BufferOffset);
>> +     tilen = cpu_to_le16(pblob->TargetInfoArray.Length);
>> +     ses->server->tilen = tilen;
>> +     if (tilen) {
>> +             ses->server->tiblob = kmalloc(tilen, GFP_KERNEL);
>> +             if (!ses->server->tiblob) {
>> +                     cERROR(1, "Challenge target info allocation failure");
>> +                     return -ENOMEM;
>> +             }
>> +             memcpy(ses->server->tiblob,  bcc_ptr + tioffset, tilen);
>> +     }
>> +
>>       return 0;
>>  }
>>
>> @@ -451,10 +466,12 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
>>                                  struct cifsSesInfo *ses,
>>                                  const struct nls_table *nls_cp, bool first)
>>  {
>> +     unsigned int size;
>>       AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer;
>>       __u32 flags;
>>       unsigned char *tmp;
>>       char ntlm_session_key[CIFS_SESS_KEY_SIZE];
>> +     struct ntlmv2_resp ntlmv2_response = {};
>>
>>       memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
>>       sec_blob->MessageType = NtLmAuthenticate;
>> @@ -477,6 +494,7 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
>>       sec_blob->LmChallengeResponse.Length = 0;
>>       sec_blob->LmChallengeResponse.MaximumLength = 0;
>>
>> +#if 1
>  ^^^^^^^
> Why are you adding code that's always compiled out?

Right now we are using ntlmv1 as a default authentication mechanism in NTLMSSP.
I am not sure whether we can just switch to ntlmv2 as an
authentication mechanism.
I think in case of Windows servers, an LmCompatibilityLevel registry
setting of 0 through 5
will accept NTLMv2 response but not sure how it is handled by other
servers i.e. are
there servers out there who will/can't handle ntlmv2 authentication
mechanism within
NTLMSSP.

>
>>       /* calculate session key,  BB what about adding similar ntlmv2 path? */
>>       SMBNTencrypt(ses->password, ses->server->cryptKey, ntlm_session_key);
>>       if (first)
>> @@ -491,6 +509,25 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
>>
>>       tmp += CIFS_SESS_KEY_SIZE;
>>
>> +#else
>> +     sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer);
>> +     setup_ntlmv2_rsp(ses, (char *)&ntlmv2_response, nls_cp);
>> +     size =  sizeof(struct ntlmv2_resp);
>> +     memcpy(tmp, (char *)&ntlmv2_response, size);
>> +     tmp += size;
>> +     if (ses->server->tilen > 0) {
>> +             memcpy(tmp, ses->server->tiblob, ses->server->tilen);
>> +             kfree(ses->server->tiblob);
>> +             tmp += ses->server->tilen;
>> +     } else
>> +             ses->server->tilen = 0;
>> +
>> +     sec_blob->NtChallengeResponse.Length = cpu_to_le16(size +
>> +                             ses->server->tilen);
>> +     sec_blob->NtChallengeResponse.MaximumLength =
>> +             cpu_to_le16(size + ses->server->tilen);
>> +#endif
>> +
>>       if (ses->domainName == NULL) {
>>               sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
>>               sec_blob->DomainName.Length = 0;
>> @@ -501,7 +538,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
>>               len = cifs_strtoUCS((__le16 *)tmp, ses->domainName,
>>                                   MAX_USERNAME_SIZE, nls_cp);
>>               len *= 2; /* unicode is 2 bytes each */
>> -             len += 2; /* trailing null */
>>               sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
>>               sec_blob->DomainName.Length = cpu_to_le16(len);
>>               sec_blob->DomainName.MaximumLength = cpu_to_le16(len);
>> @@ -518,7 +554,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
>>               len = cifs_strtoUCS((__le16 *)tmp, ses->userName,
>>                                   MAX_USERNAME_SIZE, nls_cp);
>>               len *= 2; /* unicode is 2 bytes each */
>> -             len += 2; /* trailing null */
>>               sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
>>               sec_blob->UserName.Length = cpu_to_le16(len);
>>               sec_blob->UserName.MaximumLength = cpu_to_le16(len);
>
>
> --
> Jeff Layton <jlayton at samba.org>
>


More information about the samba-technical mailing list