dsdb audit_log: Add windows event codes to password changes
authorGary Lockyer <gary@catalyst.net.nz>
Thu, 13 Dec 2018 22:09:20 +0000 (11:09 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 14 Dec 2018 17:57:52 +0000 (18:57 +0100)
Add a new "eventId" element to the PasswordChange JSON log messages.
This contains a Windows Event Code Id either:
4723 Password changed
4724 Password reset

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
librpc/idl/windows_event_ids.idl
python/samba/tests/audit_log_pass_change.py
source4/dsdb/samdb/ldb_modules/audit_log.c
source4/dsdb/samdb/ldb_modules/tests/test_audit_log.c

index 289415724afe07d3c2074429ce58f02f171059e9..c711db1b30fc4221d5367ef0157006c20aca5d9e 100644 (file)
@@ -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 {
index dc554acd68a4a1fa18bae034159f7887bbf21e32..8ece235f55855f790af05a043a293f810ec97a6a 100644 (file)
@@ -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"],
index 86a02f2c406975e4a7c4de7f2d9d5f0ed8761978..5d6ebc1e16539cbb8c2dc48997f97a88a0526995 100644 (file)
@@ -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;
index fdce2d48d48afc6aa7d7e304e12fe649db725986..2fba2406b64b9e24410da6a99ecdd7b4996568c0 100644 (file)
@@ -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));