s3:libsmb: let trust_pw_change() verify the new password at the end.
[nivanova/samba-autobuild/.git] / source3 / libsmb / trusts_util.c
index b38aec648beddaed7c0858880fea1fff4bb853d2..4b784c1babefe926354b9cbbae1199ef076d3f1c 100644 (file)
@@ -54,19 +54,26 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
                         bool force)
 {
        TALLOC_CTX *frame = talloc_stackframe();
+       const char *context_name = NULL;
        struct trust_pw_change_state *state;
-       struct samr_Password current_nt_hash;
+       struct cli_credentials *creds = NULL;
+       const struct samr_Password *current_nt_hash = NULL;
        const struct samr_Password *previous_nt_hash = NULL;
        enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
-       const char *account_name;
-       char *new_trust_passwd;
-       char *pwd;
-       struct dom_sid sid;
        time_t pass_last_set_time;
+       uint32_t old_version = 0;
+       struct pdb_trusted_domain *td = NULL;
        struct timeval g_timeout = { 0, };
        int timeout = 0;
        struct timeval tv = { 0, };
+       size_t new_len = DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH;
+       uint8_t new_password_buffer[256 * 2] = { 0, };
+       char *new_trust_passwd = NULL;
+       size_t len = 0;
+       uint32_t new_version = 0;
+       uint32_t *new_trust_version = NULL;
        NTSTATUS status;
+       bool ok;
 
        state = talloc_zero(frame, struct trust_pw_change_state);
        if (state == NULL) {
@@ -101,29 +108,53 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
 
        talloc_set_destructor(state, trust_pw_change_state_destructor);
 
-       if (!get_trust_pw_hash(domain, current_nt_hash.hash,
-                              &account_name,
-                              &sec_channel_type)) {
-               DEBUG(0, ("could not fetch domain secrets for domain %s!\n", domain));
+       status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
+                         domain, nt_errstr(status)));
                TALLOC_FREE(frame);
                return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
        }
 
+       current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
+       if (current_nt_hash == NULL) {
+               DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
+                         domain));
+               TALLOC_FREE(frame);
+               return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+       }
+
+       old_version = cli_credentials_get_kvno(creds);
+       pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
+       sec_channel_type = cli_credentials_get_secure_channel_type(creds);
+
+       new_version = old_version + 1;
+
        switch (sec_channel_type) {
        case SEC_CHAN_WKSTA:
-               pwd = secrets_fetch_machine_password(domain,
-                                                    &pass_last_set_time,
-                                                    NULL);
-               if (pwd == NULL) {
-                       TALLOC_FREE(frame);
-                       return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
-               }
+       case SEC_CHAN_BDC:
                break;
+       case SEC_CHAN_DNS_DOMAIN:
+               /*
+                * new_len * 2 = 498 bytes is the largest possible length
+                * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
+                * and a confounder with at least 2 bytes is required.
+                *
+                * Windows uses new_len = 120 => 240 bytes.
+                */
+               new_len = 120;
+
+               /* fall through */
        case SEC_CHAN_DOMAIN:
-               if (!pdb_get_trusteddom_pw(domain, &pwd, &sid, &pass_last_set_time)) {
+               status = pdb_get_trusted_domain(frame, domain, &td);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
+                                 domain, nt_errstr(status)));
                        TALLOC_FREE(frame);
-                       return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+                       return status;
                }
+
+               new_trust_version = &new_version;
                break;
        default:
                TALLOC_FREE(frame);
@@ -151,33 +182,55 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
                return NT_STATUS_OK;
        }
 
-       /* Create a random machine account password */
-       new_trust_passwd = generate_random_password(frame,
-                               DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH,
-                               DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
-       if (new_trust_passwd == NULL) {
-               DEBUG(0, ("generate_random_password failed\n"));
+       context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
+       if (context_name == NULL) {
                TALLOC_FREE(frame);
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = netlogon_creds_cli_auth(context, b,
-                                        current_nt_hash,
-                                        previous_nt_hash);
-       if (!NT_STATUS_IS_OK(status)) {
+       /*
+        * Create a random machine account password
+        * We create a random buffer and convert that to utf8.
+        * This is similar to what windows is doing.
+        */
+       generate_secret_buffer(new_password_buffer, new_len * 2);
+       ok = convert_string_talloc(frame,
+                                  CH_UTF16MUNGED, CH_UTF8,
+                                  new_password_buffer, new_len * 2,
+                                  (void *)&new_trust_passwd, &len);
+       ZERO_STRUCT(new_password_buffer);
+       if (!ok) {
+               DEBUG(0, ("convert_string_talloc failed\n"));
                TALLOC_FREE(frame);
-               return status;
+               return NT_STATUS_NO_MEMORY;
        }
 
-       status = netlogon_creds_cli_ServerPasswordSet(context, b,
-                                                     new_trust_passwd, NULL);
+       /*
+        * We could use cli_credentials_get_old_nt_hash(creds, frame) to
+        * set previous_nt_hash.
+        *
+        * But we want to check if the dc has our current password and only do
+        * a change if that's the case. So we keep previous_nt_hash = NULL.
+        *
+        * TODO:
+        * If the previous password is the only password in common with the dc,
+        * we better skip the password change, or use something like
+        * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
+        * local secrets before doing the change.
+        */
+       status = netlogon_creds_cli_auth(context, b,
+                                        *current_nt_hash,
+                                        previous_nt_hash);
        if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old password - %s!\n",
+                         context_name, nt_errstr(status)));
                TALLOC_FREE(frame);
                return status;
        }
 
-       DEBUG(3,("%s : trust_pw_change_and_store_it: Changed password.\n",
-                current_timestring(talloc_tos(), False)));
+       DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
+                current_timestring(talloc_tos(), false),
+                __func__, domain, context_name));
 
        /*
         * Return the result of trying to write the new password
@@ -187,27 +240,89 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
        switch (sec_channel_type) {
 
        case SEC_CHAN_WKSTA:
-               if (!secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type)) {
+       case SEC_CHAN_BDC:
+               ok = secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type);
+               if (!ok) {
+                       DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n",
+                                 domain));
                        TALLOC_FREE(frame);
                        return NT_STATUS_INTERNAL_DB_CORRUPTION;
                }
                break;
 
+       case SEC_CHAN_DNS_DOMAIN:
        case SEC_CHAN_DOMAIN:
                /*
                 * we need to get the sid first for the
                 * pdb_set_trusteddom_pw call
                 */
-               if (!pdb_set_trusteddom_pw(domain, new_trust_passwd, &sid)) {
+               ok = pdb_set_trusteddom_pw(domain, new_trust_passwd,
+                                          &td->security_identifier);
+               if (!ok) {
+                       DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
+                                 domain));
                        TALLOC_FREE(frame);
                        return NT_STATUS_INTERNAL_DB_CORRUPTION;
                }
                break;
 
        default:
+               smb_panic("Unsupported secure channel type");
                break;
        }
 
+       DEBUG(0,("%s : %s(%s): Changed password locally\n",
+                current_timestring(talloc_tos(), false), __func__, domain));
+
+       status = netlogon_creds_cli_ServerPasswordSet(context, b,
+                                                     new_trust_passwd,
+                                                     new_trust_version);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("%s : %s(%s) remote password change set with %s failed - %s\n",
+                        current_timestring(talloc_tos(), false),
+                        __func__, domain, context_name,
+                        nt_errstr(status)));
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
+                current_timestring(talloc_tos(), false),
+                __func__, domain, context_name));
+
+       ok = cli_credentials_set_password(creds, new_trust_passwd, CRED_SPECIFIED);
+       if (!ok) {
+               DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
+                         domain));
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
+       if (current_nt_hash == NULL) {
+               DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
+                         domain));
+               TALLOC_FREE(frame);
+               return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+       }
+
+       /*
+        * Now we verify the new password.
+        */
+       status = netlogon_creds_cli_auth(context, b,
+                                        *current_nt_hash,
+                                        NULL); /* previous_nt_hash */
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
+                         context_name, nt_errstr(status)));
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
+                current_timestring(talloc_tos(), false),
+                __func__, domain, context_name));
+
        TALLOC_FREE(frame);
        return NT_STATUS_OK;
 }