sync 3.0 into HEAD for the last time
[tprouty/samba.git] / source / libsmb / ntlmssp.c
index 0cd1ac33ec2ec4297c5f22920ca3b9bfec075fb7..42bf18d1d26516f85d9eba897ecded27b1224a7a 100644 (file)
@@ -140,7 +140,7 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
                                 &cliname,
                                 &domname)) {
                        DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP:\n"));
-                       dump_data(2, request.data, request.length);
+                       dump_data(2, (const char *)request.data, request.length);
                        return NT_STATUS_INVALID_PARAMETER;
                }
                
@@ -172,14 +172,17 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
        target_name = ntlmssp_target_name(ntlmssp_state, 
                                          neg_flags, &chal_flags); 
 
+       if (target_name == NULL)
+               return NT_STATUS_INVALID_PARAMETER;
+
        /* This should be a 'netbios domain -> DNS domain' mapping */
        dnsdomname[0] = '\0';
        get_mydomname(dnsdomname);
-       strlower(dnsdomname);
+       strlower_m(dnsdomname);
        
        dnsname[0] = '\0';
        get_myfullname(dnsname);
-       strlower(dnsname);
+       strlower_m(dnsname);
        
        if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) 
        {
@@ -190,7 +193,6 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
                        target_name_dns = dnsname;
                }
 
-               /* the numbers here are the string type flags */
                msrpc_gen(&struct_blob, "aaaaa",
                          ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_DOMAIN, target_name,
                          ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->get_global_myname(),
@@ -274,14 +276,14 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
                         &sess_key,
                         &neg_flags)) {
                DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
-               dump_data(2, request.data, request.length);
+               dump_data(2, (const char *)request.data, request.length);
                return NT_STATUS_INVALID_PARAMETER;
        }
 
        data_blob_free(&sess_key);
        
-       DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%d len2=%d\n",
-                ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, ntlmssp_state->lm_resp.length, ntlmssp_state->nt_resp.length));
+       DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
+                ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length));
 
 #if 0
        file_save("nthash1.dat",  &ntlmssp_state->nt_resp.data,  &ntlmssp_state->nt_resp.length);
@@ -385,7 +387,7 @@ NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state,
        } else if (ntlmssp_command == NTLMSSP_AUTH) {
                return ntlmssp_server_auth(ntlmssp_state, request, reply);
        } else {
-               DEBUG(1, ("unknown NTLMSSP command %u\n", ntlmssp_command, ntlmssp_state->expected_state));
+               DEBUG(1, ("unknown NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state));
                return NT_STATUS_INVALID_PARAMETER;
        }
 }
@@ -410,6 +412,12 @@ static NTSTATUS ntlmssp_client_initial(struct ntlmssp_client_state *ntlmssp_stat
                ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
        }
        
+       if (ntlmssp_state->use_ntlmv2) {
+               ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
+       }
+
+       ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
+       
        /* generate the ntlmssp negotiate packet */
        msrpc_gen(next_request, "CddAA",
                  "NTLMSSP",
@@ -436,37 +444,50 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
        uint32 chal_flags, ntlmssp_command, unkn1, unkn2;
        DATA_BLOB server_domain_blob;
        DATA_BLOB challenge_blob;
-       DATA_BLOB struct_blob;
+       DATA_BLOB struct_blob = data_blob(NULL, 0);
        char *server_domain;
        const char *chal_parse_string;
        const char *auth_gen_string;
        DATA_BLOB lm_response = data_blob(NULL, 0);
        DATA_BLOB nt_response = data_blob(NULL, 0);
        DATA_BLOB session_key = data_blob(NULL, 0);
-       uint8 datagram_sess_key[16];
-
-       generate_random_buffer(datagram_sess_key, sizeof(datagram_sess_key), False);    
+       DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
 
        if (!msrpc_parse(&reply, "CdBd",
                         "NTLMSSP",
                         &ntlmssp_command, 
                         &server_domain_blob,
                         &chal_flags)) {
-               DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n"));
+               DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
+               dump_data(2, (const char *)reply.data, reply.length);
+
                return NT_STATUS_INVALID_PARAMETER;
        }
        
        data_blob_free(&server_domain_blob);
 
+       DEBUG(3, ("Got challenge flags:\n"));
+       debug_ntlmssp_flags(chal_flags);
+
        if (chal_flags & NTLMSSP_NEGOTIATE_UNICODE) {
-               chal_parse_string = "CdUdbddB";
+               if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
+                       chal_parse_string = "CdUdbddB";
+               } else {
+                       chal_parse_string = "CdUdbdd";
+               }
                auth_gen_string = "CdBBUUUBd";
                ntlmssp_state->unicode = True;
                ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
                ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
        } else if (chal_flags & NTLMSSP_NEGOTIATE_OEM) {
-               chal_parse_string = "CdAdbddB";
+               if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
+                       chal_parse_string = "CdAdbddB";
+               } else {
+                       chal_parse_string = "CdAdbdd";
+               }
+
                auth_gen_string = "CdBBAAABd";
+
                ntlmssp_state->unicode = False;
                ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
                ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
@@ -474,6 +495,29 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       if (chal_flags & NTLMSSP_NEGOTIATE_LM_KEY && lp_client_lanman_auth()) {
+               /* server forcing us to use LM */
+               ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
+               ntlmssp_state->use_ntlmv2 = False;
+       } else {
+               ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+       }
+
+       if (!(chal_flags & NTLMSSP_NEGOTIATE_NTLM2)) {
+               ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+       }
+
+       if (!(chal_flags & NTLMSSP_NEGOTIATE_128)) {
+               ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128;
+       }
+
+       if (!(chal_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) {
+               ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH;
+       }
+
+       DEBUG(3, ("NTLMSSP: Set final flags:\n"));
+       debug_ntlmssp_flags(ntlmssp_state->neg_flags);
+
        if (!msrpc_parse(&reply, chal_parse_string,
                         "NTLMSSP",
                         &ntlmssp_command, 
@@ -482,45 +526,110 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
                         &challenge_blob, 8,
                         &unkn1, &unkn2,
                         &struct_blob)) {
-               DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n"));
+               DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
+               dump_data(2, (const char *)reply.data, reply.length);
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       ntlmssp_state->server_domain = talloc_strdup(ntlmssp_state->mem_ctx,
+                                                    server_domain);
+
        SAFE_FREE(server_domain);
-       data_blob_free(&struct_blob);
-       
        if (challenge_blob.length != 8) {
+               data_blob_free(&struct_blob);
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       if (ntlmssp_state->use_ntlmv2) {
+       if (!ntlmssp_state->password) {
+               /* do nothing - blobs are zero length */
+       } else if (ntlmssp_state->use_ntlmv2) {
+
+               if (!struct_blob.length) {
+                       /* be lazy, match win2k - we can't do NTLMv2 without it */
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
 
                /* TODO: if the remote server is standalone, then we should replace 'domain'
                   with the server name as supplied above */
                
                if (!SMBNTLMv2encrypt(ntlmssp_state->user, 
                                      ntlmssp_state->domain, 
-                                     ntlmssp_state->password, challenge_blob, 
+                                     ntlmssp_state->password, &challenge_blob, 
+                                     &struct_blob, 
                                      &lm_response, &nt_response, &session_key)) {
                        data_blob_free(&challenge_blob);
+                       data_blob_free(&struct_blob);
                        return NT_STATUS_NO_MEMORY;
                }
+       } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+               struct MD5Context md5_session_nonce_ctx;
+               uchar nt_hash[16];
+               uchar session_nonce[16];
+               uchar session_nonce_hash[16];
+               uchar nt_session_key[16];
+               E_md4hash(ntlmssp_state->password, nt_hash);
+               
+               lm_response = data_blob(NULL, 24);
+               generate_random_buffer(lm_response.data, 8, False);
+               memset(lm_response.data+8, 0, 16);
+
+               memcpy(session_nonce, challenge_blob.data, 8);
+               memcpy(&session_nonce[8], lm_response.data, 8);
+       
+               MD5Init(&md5_session_nonce_ctx);
+               MD5Update(&md5_session_nonce_ctx, challenge_blob.data, 8);
+               MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
+               MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+               
+               nt_response = data_blob(NULL, 24);
+               SMBNTencrypt(ntlmssp_state->password,
+                            session_nonce_hash,
+                            nt_response.data);
+
+               session_key = data_blob(NULL, 16);
+
+               SMBsesskeygen_ntv1(nt_hash, NULL, nt_session_key);
+               hmac_md5(nt_session_key, session_nonce, sizeof(session_nonce), session_key.data);
        } else {
+               
+               
+               uchar lm_hash[16];
                uchar nt_hash[16];
+               E_deshash(ntlmssp_state->password, lm_hash);
                E_md4hash(ntlmssp_state->password, nt_hash);
                
-               /* non encrypted password supplied. Ignore ntpass. */
+               /* lanman auth is insecure, it may be disabled */
                if (lp_client_lanman_auth()) {
                        lm_response = data_blob(NULL, 24);
                        SMBencrypt(ntlmssp_state->password,challenge_blob.data,
                                   lm_response.data);
-               }
+                       }
                
                nt_response = data_blob(NULL, 24);
                SMBNTencrypt(ntlmssp_state->password,challenge_blob.data,
                             nt_response.data);
+               
                session_key = data_blob(NULL, 16);
-               SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+               if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) 
+                   && lp_client_lanman_auth()) {
+                       SMBsesskeygen_lmv1(lm_hash, lm_response.data, 
+                                          session_key.data);
+               } else {
+                       SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+               }
+       }
+       data_blob_free(&struct_blob);
+
+       /* Key exchange encryptes a new client-generated session key with
+          the password-derived key */
+       if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+               uint8 client_session_key[16];
+               
+               generate_random_buffer(client_session_key, sizeof(client_session_key), False);  
+               encrypted_session_key = data_blob(client_session_key, sizeof(client_session_key));
+               SamOEMhash(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
+               data_blob_free(&session_key);
+               session_key = data_blob(client_session_key, sizeof(client_session_key));
        }
 
        /* this generates the actual auth packet */
@@ -532,7 +641,7 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
                       ntlmssp_state->domain, 
                       ntlmssp_state->user, 
                       ntlmssp_state->get_global_myname(), 
-                      datagram_sess_key, 16,
+                      encrypted_session_key.data, encrypted_session_key.length,
                       ntlmssp_state->neg_flags)) {
                
                data_blob_free(&lm_response);
@@ -541,6 +650,8 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_st
                return NT_STATUS_NO_MEMORY;
        }
 
+       data_blob_free(&encrypted_session_key);
+
        data_blob_free(&ntlmssp_state->chal);
        data_blob_free(&ntlmssp_state->lm_resp);
        data_blob_free(&ntlmssp_state->nt_resp);
@@ -574,9 +685,13 @@ NTSTATUS ntlmssp_client_start(NTLMSSP_CLIENT_STATE **ntlmssp_state)
 
        (*ntlmssp_state)->unicode = True;
 
+       (*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth();
+
        (*ntlmssp_state)->neg_flags = 
                NTLMSSP_NEGOTIATE_128 |
                NTLMSSP_NEGOTIATE_NTLM |
+               NTLMSSP_NEGOTIATE_NTLM2 |
+               NTLMSSP_NEGOTIATE_KEY_EXCH |
                NTLMSSP_REQUEST_TARGET;
 
        (*ntlmssp_state)->ref_count = 1;
@@ -595,6 +710,7 @@ NTSTATUS ntlmssp_client_end(NTLMSSP_CLIENT_STATE **ntlmssp_state)
                data_blob_free(&(*ntlmssp_state)->lm_resp);
                data_blob_free(&(*ntlmssp_state)->nt_resp);
                data_blob_free(&(*ntlmssp_state)->session_key);
+               data_blob_free(&(*ntlmssp_state)->stored_response);
                talloc_destroy(mem_ctx);
        }
 
@@ -605,12 +721,18 @@ NTSTATUS ntlmssp_client_end(NTLMSSP_CLIENT_STATE **ntlmssp_state)
 NTSTATUS ntlmssp_client_update(NTLMSSP_CLIENT_STATE *ntlmssp_state, 
                               DATA_BLOB reply, DATA_BLOB *next_request) 
 {
+       NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
        uint32 ntlmssp_command;
        *next_request = data_blob(NULL, 0);
 
        if (!reply.length) {
-               return ntlmssp_client_initial(ntlmssp_state, reply, next_request);
-       }               
+               /* If there is a cached reply, use it - otherwise this is the first packet */
+               if (!ntlmssp_state->stored_response.length) {
+                       return ntlmssp_client_initial(ntlmssp_state, reply, next_request);
+               }
+               
+               reply = ntlmssp_state->stored_response;
+       }
 
        if (!msrpc_parse(&reply, "Cd",
                         "NTLMSSP",
@@ -619,9 +741,12 @@ NTSTATUS ntlmssp_client_update(NTLMSSP_CLIENT_STATE *ntlmssp_state,
        }
 
        if (ntlmssp_command == NTLMSSP_CHALLENGE) {
-               return ntlmssp_client_challenge(ntlmssp_state, reply, next_request);
+               nt_status = ntlmssp_client_challenge(ntlmssp_state, reply, next_request);
+       }
+       if (ntlmssp_state->stored_response.length) {
+               data_blob_free(&ntlmssp_state->stored_response);
        }
-       return NT_STATUS_INVALID_PARAMETER;
+       return nt_status;
 }
 
 NTSTATUS ntlmssp_set_username(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *user) 
@@ -635,9 +760,13 @@ NTSTATUS ntlmssp_set_username(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *u
 
 NTSTATUS ntlmssp_set_password(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *password) 
 {
-       ntlmssp_state->password = talloc_strdup(ntlmssp_state->mem_ctx, password);
-       if (!ntlmssp_state->password) {
-               return NT_STATUS_NO_MEMORY;
+       if (!password) {
+               ntlmssp_state->password = NULL;
+       } else {
+               ntlmssp_state->password = talloc_strdup(ntlmssp_state->mem_ctx, password);
+               if (!ntlmssp_state->password) {
+                       return NT_STATUS_NO_MEMORY;
+               }
        }
        return NT_STATUS_OK;
 }
@@ -650,3 +779,16 @@ NTSTATUS ntlmssp_set_domain(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *dom
        }
        return NT_STATUS_OK;
 }
+
+/**
+ *  Store a DATA_BLOB containing an NTLMSSP response, for use later.
+ *  This 'keeps' the data blob - the caller must *not* free it.
+ */
+
+NTSTATUS ntlmssp_client_store_response(NTLMSSP_CLIENT_STATE *ntlmssp_state,
+                                      DATA_BLOB response) 
+{
+       data_blob_free(&ntlmssp_state->stored_response);
+       ntlmssp_state->stored_response = response;
+       return NT_STATUS_OK;
+}