samr: Add logging of password change success and failure
authorAndrew Bartlett <abartlet@samba.org>
Fri, 17 Mar 2017 00:26:13 +0000 (13:26 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 29 Mar 2017 00:37:29 +0000 (02:37 +0200)
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
selftest/knownfail
source4/rpc_server/samr/samr_password.c

index 6ccd7c99268a438d3b1d692631bb438aa342030b..432908b4c38ecf52ecbb5e2e8d164e8206e7a955 100644 (file)
 # rap password tests don't function in the ad_dc_ntvfs:local environment
 #
 ^samba.tests.auth_log_pass_change.samba.tests.auth_log_pass_change.AuthLogPassChangeTests.test_rap_change_password\(ad_dc_ntvfs:local\)
-^samba.tests.auth_log_pass_change.samba.tests.auth_log_pass_change.AuthLogPassChangeTests.test_admin_change_password\(
-^samba.tests.auth_log_pass_change.samba.tests.auth_log_pass_change.AuthLogPassChangeTests.test_admin_change_password_bad_original_password
-^samba.tests.auth_log_pass_change.samba.tests.auth_log_pass_change.AuthLogPassChangeTests.test_admin_change_password_new_password_fails_restriction
-^samba.tests.auth_log_pass_change.samba.tests.auth_log_pass_change.AuthLogPassChangeTests.test_admin_change_password_unknown_user
-^samba.tests.auth_log_pass_change.samba.tests.auth_log_pass_change.AuthLogPassChangeTests.test_rap_change_password\(ad_dc:local\)
index 5d4504f7bf01608f1dcb26f8d522eeeee08832b7..785633ddc259c987bab30a14937e7361a2d87b46 100644 (file)
 #include "rpc_server/samr/proto.h"
 #include "auth/auth_sam.h"
 
+static void log_password_change_event(struct imessaging_context *msg_ctx,
+                                     struct loadparm_context *lp_ctx,
+                                     const struct tsocket_address *remote_client_address,
+                                     const struct tsocket_address *local_server_address,
+                                     const char *auth_description,
+                                     const char *password_type,
+                                     const char *original_client_name,
+                                     const char *account_name_from_db,
+                                     NTSTATUS status,
+                                     struct dom_sid *sid)
+{
+       /*
+        * Forcing this via the NTLM auth structure is not ideal, but
+        * it is the most practical option right now, and ensures the
+        * logs are consistent, even if some elements are always NULL.
+        */
+       struct auth_usersupplied_info ui = {
+               .mapped_state = true,
+               .was_mapped = true,
+               .client = {
+                       .account_name = original_client_name,
+                       .domain_name = lpcfg_sam_name(lp_ctx),
+               },
+               .mapped = {
+                       .account_name = account_name_from_db,
+                       .domain_name = lpcfg_sam_name(lp_ctx),
+               },
+               .remote_host = remote_client_address,
+               .local_host = local_server_address,
+               .service_description = "SAMR Password Change",
+               .auth_description = auth_description,
+               .password_type = password_type,
+       };
+
+       log_authentication_event(msg_ctx,
+                                lp_ctx,
+                                &ui,
+                                status,
+                                ui.mapped.domain_name,
+                                ui.mapped.account_name,
+                                NULL,
+                                sid);
+}
 /*
   samr_ChangePasswordUser
 
@@ -54,7 +97,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                                            TALLOC_CTX *mem_ctx,
                                            struct samr_OemChangePasswordUser2 *r)
 {
-       NTSTATUS status;
+       NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
        DATA_BLOB new_password, new_unicode_password;
        char *new_pass;
        struct samr_CryptPassword *pwbuf = r->in.password;
@@ -66,6 +109,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                                       "userAccountControl",
                                       "msDS-User-Account-Control-Computed",
                                       "badPwdCount", "badPasswordTime",
+                                      "samAccountName",
                                       NULL };
        struct samr_Password *lm_pwd;
        DATA_BLOB lm_pwd_blob;
@@ -73,6 +117,8 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
        struct samr_Password lm_verifier;
        size_t unicode_pw_len;
        size_t converted_size = 0;
+       const char *user_samAccountName = NULL;
+       struct dom_sid *user_objectSid = NULL;
 
        if (pwbuf == NULL) {
                return NT_STATUS_INVALID_PARAMETER;
@@ -84,7 +130,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
 
        /* this call can only work with lanman auth */
        if (!lpcfg_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
-               return NT_STATUS_WRONG_PASSWORD;
+               return NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
        }
 
        /* Connect to a SAMDB with system privileges for fetching the old pw
@@ -104,18 +150,22 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                           "(&(sAMAccountName=%s)(objectclass=user))",
                           ldb_binary_encode_string(mem_ctx, r->in.account->string));
        if (ret != 1) {
-               /* Don't give the game away:  (don't allow anonymous users to prove the existence of usernames) */
-               return NT_STATUS_WRONG_PASSWORD;
+               status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
+               goto failed;
        }
 
        user_dn = res[0]->dn;
 
+       user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
+       user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
+
        status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
                                        res[0], &lm_pwd, NULL);
        if (!NT_STATUS_IS_OK(status)) {
-               return status;
+               goto failed;
        } else if (!lm_pwd) {
-               return NT_STATUS_WRONG_PASSWORD;
+               status = NT_STATUS_WRONG_PASSWORD;
+               goto failed;
        }
 
        /* decrypt the password we have been given */
@@ -126,7 +176,8 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
        if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
                DEBUG(3,("samr: failed to decode password buffer\n"));
                authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
-               return NT_STATUS_WRONG_PASSWORD;
+               status =  NT_STATUS_WRONG_PASSWORD;
+               goto failed;
        }
 
        if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
@@ -136,7 +187,8 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                                  (void **)&new_pass, &converted_size)) {
                DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
                authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
-               return NT_STATUS_WRONG_PASSWORD;
+               status =  NT_STATUS_WRONG_PASSWORD;
+               goto failed;
        }
 
        if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
@@ -146,7 +198,8 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                                               (void **)&new_unicode_password.data, &unicode_pw_len)) {
                DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
                authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
-               return NT_STATUS_WRONG_PASSWORD;
+               status =  NT_STATUS_WRONG_PASSWORD;
+               goto failed;
        }
        new_unicode_password.length = unicode_pw_len;
 
@@ -154,7 +207,8 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
        E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
        if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
                authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
-               return NT_STATUS_WRONG_PASSWORD;
+               status =  NT_STATUS_WRONG_PASSWORD;
+               goto failed;
        }
 
        /* Connect to a SAMDB with user privileges for the password change */
@@ -184,7 +238,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                                    NULL);
        if (!NT_STATUS_IS_OK(status)) {
                ldb_transaction_cancel(sam_ctx);
-               return status;
+               goto failed;
        }
 
        /* And this confirms it in a transaction commit */
@@ -193,10 +247,36 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
                         ldb_dn_get_linearized(user_dn),
                         ldb_errstring(sam_ctx)));
-               return NT_STATUS_TRANSACTION_ABORTED;
+               status = NT_STATUS_TRANSACTION_ABORTED;
+               goto failed;
        }
 
-       return NT_STATUS_OK;
+       status = NT_STATUS_OK;
+
+failed:
+
+       log_password_change_event(dce_call->conn->msg_ctx,
+                                 dce_call->conn->dce_ctx->lp_ctx,
+                                 dce_call->conn->remote_address,
+                                 dce_call->conn->local_address,
+                                 "OemChangePasswordUser2",
+                                 "RC4/DES using LanMan-hash",
+                                 r->in.account->string,
+                                 user_samAccountName,
+                                 status,
+                                 user_objectSid);
+       if (NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_OK;
+       }
+       /* Only update the badPwdCount if we found the user */
+       if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+               authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+               /* Don't give the game away:  (don't allow anonymous users to prove the existence of usernames) */
+               status = NT_STATUS_WRONG_PASSWORD;
+       }
+
+       return status;
 }
 
 
@@ -207,7 +287,7 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
                                         TALLOC_CTX *mem_ctx,
                                         struct samr_ChangePasswordUser3 *r)
 {
-       NTSTATUS status;
+       NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
        DATA_BLOB new_password;
        struct ldb_context *sam_ctx = NULL;
        struct ldb_dn *user_dn = NULL;
@@ -225,6 +305,8 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
        enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
        uint8_t new_nt_hash[16], new_lm_hash[16];
        struct samr_Password nt_verifier, lm_verifier;
+       const char *user_samAccountName = NULL;
+       struct dom_sid *user_objectSid = NULL;
 
        *r->out.dominfo = NULL;
        *r->out.reject = NULL;
@@ -251,12 +333,13 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
                           "(&(sAMAccountName=%s)(objectclass=user))",
                           ldb_binary_encode_string(mem_ctx, r->in.account->string));
        if (ret != 1) {
-               /* Don't give the game away:  (don't allow anonymous users to prove the existence of usernames) */
-               status = NT_STATUS_WRONG_PASSWORD;
+               status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
                goto failed;
        }
 
        user_dn = res[0]->dn;
+       user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
+       user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
 
        status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
                                        res[0], &lm_pwd, &nt_pwd);
@@ -355,12 +438,30 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
                goto failed;
        }
 
-       return NT_STATUS_OK;
+       status = NT_STATUS_OK;
 
 failed:
+
+       log_password_change_event(dce_call->conn->msg_ctx,
+                                 dce_call->conn->dce_ctx->lp_ctx,
+                                 dce_call->conn->remote_address,
+                                 dce_call->conn->local_address,
+                                 "samr_ChangePasswordUser3",
+                                 "RC4/DES using NTLM-hash",
+                                 r->in.account->string,
+                                 user_samAccountName,
+                                 status,
+                                 user_objectSid);
+       if (NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_OK;
+       }
+
        /* Only update the badPwdCount if we found the user */
-       if (user_dn != NULL && NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+       if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
                authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+               /* Don't give the game away:  (don't allow anonymous users to prove the existence of usernames) */
+               status = NT_STATUS_WRONG_PASSWORD;
        }
 
        reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
@@ -375,7 +476,6 @@ failed:
        return status;
 }
 
-
 /*
   samr_ChangePasswordUser2