diff --git a/librpc/idl/windows_event_ids.idl b/librpc/idl/windows_event_ids.idl index 289415724af..c711db1b30f 100644 --- a/librpc/idl/windows_event_ids.idl +++ b/librpc/idl/windows_event_ids.idl @@ -10,7 +10,9 @@ interface windows_events typedef [v1_enum,public] enum { EVT_ID_SUCCESSFUL_LOGON = 4624, - EVT_ID_UNSUCCESSFUL_LOGON = 4625 + EVT_ID_UNSUCCESSFUL_LOGON = 4625, + EVT_ID_PASSWORD_CHANGE = 4723, + EVT_ID_PASSWORD_RESET = 4724 } event_id_type; typedef [v1_enum,public] enum { diff --git a/python/samba/tests/audit_log_pass_change.py b/python/samba/tests/audit_log_pass_change.py index dc554acd68a..8ece235f558 100644 --- a/python/samba/tests/audit_log_pass_change.py +++ b/python/samba/tests/audit_log_pass_change.py @@ -28,6 +28,11 @@ from samba.tests.audit_log_base import AuditLogTestBase from samba.tests import delete_force from samba.net import Net from ldb import ERR_INSUFFICIENT_ACCESS_RIGHTS +from samba.dcerpc.windows_event_ids import ( + EVT_ID_PASSWORD_CHANGE, + EVT_ID_PASSWORD_RESET +) + USER_NAME = "auditlogtestuser" USER_PASS = samba.generate_random_password(32, 32) @@ -119,6 +124,7 @@ class AuditLogPassChangeTests(AuditLogTestBase): len(messages), "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] + self.assertEquals(EVT_ID_PASSWORD_CHANGE, audit["eventId"]) self.assertEquals("Change", audit["action"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], @@ -147,6 +153,7 @@ class AuditLogPassChangeTests(AuditLogTestBase): "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] + self.assertEquals(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEquals("Reset", audit["action"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], @@ -187,6 +194,7 @@ class AuditLogPassChangeTests(AuditLogTestBase): "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] + self.assertEquals(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEquals("Reset", audit["action"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], @@ -223,6 +231,7 @@ class AuditLogPassChangeTests(AuditLogTestBase): "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] + self.assertEquals(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEquals("Reset", audit["action"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], @@ -256,6 +265,7 @@ class AuditLogPassChangeTests(AuditLogTestBase): "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] + self.assertEquals(EVT_ID_PASSWORD_CHANGE, audit["eventId"]) self.assertEquals("Change", audit["action"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], @@ -286,6 +296,7 @@ class AuditLogPassChangeTests(AuditLogTestBase): "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] + self.assertEquals(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEquals("Reset", audit["action"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], @@ -312,6 +323,7 @@ class AuditLogPassChangeTests(AuditLogTestBase): # The first message should be the reset from the Setup code. # audit = messages[0]["passwordChange"] + self.assertEquals(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEquals("Reset", audit["action"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], diff --git a/source4/dsdb/samdb/ldb_modules/audit_log.c b/source4/dsdb/samdb/ldb_modules/audit_log.c index 86a02f2c406..5d6ebc1e165 100644 --- a/source4/dsdb/samdb/ldb_modules/audit_log.c +++ b/source4/dsdb/samdb/ldb_modules/audit_log.c @@ -33,6 +33,7 @@ #include "libcli/security/dom_sid.h" #include "auth/common_auth.h" #include "param/param.h" +#include "librpc/gen_ndr/windows_event_ids.h" #define OPERATION_JSON_TYPE "dsdbChange" #define OPERATION_HR_TAG "DSDB Change" @@ -43,7 +44,7 @@ #define PASSWORD_JSON_TYPE "passwordChange" #define PASSWORD_HR_TAG "Password Change" #define PASSWORD_MAJOR 1 -#define PASSWORD_MINOR 0 +#define PASSWORD_MINOR 1 #define PASSWORD_LOG_LVL 5 #define TRANSACTION_JSON_TYPE "dsdbTransaction" @@ -121,6 +122,47 @@ static bool has_password_changed(const struct ldb_message *message) return false; } +/* + * @brief get the password change windows event id + * + * Get the Windows Event Id for the action being performed on the user password. + * + * This routine assumes that the request contains password attributes and that the + * password ACL checks have been performed by acl.c + * + * @param request the ldb_request to inspect + * @param reply the ldb_reply, will contain the password controls + * + * @return The windows event code. + */ +static enum event_id_type get_password_windows_event_id( + const struct ldb_request *request, + const struct ldb_reply *reply) +{ + if(request->operation == LDB_ADD) { + return EVT_ID_PASSWORD_RESET; + } else { + struct ldb_control *pav_ctrl = NULL; + struct dsdb_control_password_acl_validation *pav = NULL; + + pav_ctrl = ldb_reply_get_control( + discard_const(reply), + DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID); + if (pav_ctrl == NULL) { + return EVT_ID_PASSWORD_RESET; + } + + pav = talloc_get_type_abort( + pav_ctrl->data, + struct dsdb_control_password_acl_validation); + + if (pav->pwd_reset) { + return EVT_ID_PASSWORD_RESET; + } else { + return EVT_ID_PASSWORD_CHANGE; + } + } +} /* * @brief Is the request a password "Change" or a "Reset" * @@ -455,6 +497,7 @@ static struct json_object password_change_json( = talloc_get_type_abort(ldb_module_get_private(module), struct audit_private); int rc = 0; + enum event_id_type event_id; ldb = ldb_module_get_ctx(module); @@ -463,6 +506,7 @@ static struct json_object password_change_json( dn = dsdb_audit_get_primary_dn(request); action = get_password_action(request, reply); unique_session_token = dsdb_audit_get_unique_session_token(module); + event_id = get_password_windows_event_id(request, reply); audit = json_new_object(); if (json_is_invalid(&audit)) { @@ -472,6 +516,10 @@ static struct json_object password_change_json( if (rc != 0) { goto failure; } + rc = json_add_int(&audit, "eventId", event_id); + if (rc != 0) { + goto failure; + } rc = json_add_int(&audit, "statusCode", reply->error); if (rc != 0) { goto failure; diff --git a/source4/dsdb/samdb/ldb_modules/tests/test_audit_log.c b/source4/dsdb/samdb/ldb_modules/tests/test_audit_log.c index fdce2d48d48..2fba2406b64 100644 --- a/source4/dsdb/samdb/ldb_modules/tests/test_audit_log.c +++ b/source4/dsdb/samdb/ldb_modules/tests/test_audit_log.c @@ -828,11 +828,14 @@ static void test_password_change_json_empty(void **state) audit = json_object_get(json.root, "passwordChange"); assert_non_null(audit); assert_true(json_is_object(audit)); - assert_int_equal(9, json_object_size(audit)); + assert_int_equal(10, json_object_size(audit)); o = json_object_get(audit, "version"); assert_non_null(o); + v = json_object_get(audit, "eventId"); + assert_non_null(v); + v = json_object_get(audit, "statusCode"); assert_non_null(v); @@ -950,12 +953,17 @@ static void test_password_change_json(void **state) audit = json_object_get(json.root, "passwordChange"); assert_non_null(audit); assert_true(json_is_object(audit)); - assert_int_equal(9, json_object_size(audit)); + assert_int_equal(10, json_object_size(audit)); o = json_object_get(audit, "version"); assert_non_null(o); check_version(o, PASSWORD_MAJOR,PASSWORD_MINOR); + v = json_object_get(audit, "eventId"); + assert_non_null(v); + assert_true(json_is_integer(v)); + assert_int_equal(EVT_ID_PASSWORD_RESET, json_integer_value(v)); + v = json_object_get(audit, "statusCode"); assert_non_null(v); assert_true(json_is_integer(v));