s4 group_audit: Add Windows Event Id's to Group membership changes
authorGary Lockyer <gary@catalyst.net.nz>
Tue, 18 Dec 2018 20:08:22 +0000 (09:08 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 21 Dec 2018 21:16:09 +0000 (22:16 +0100)
Generate a GroupChange event when a user is created with a PrimaryGroup
membership.  Log the windows event id in the JSON GroupChange message.

Event Id's supported are:
4728 A member was added to a security enabled global group
4729 A member was removed from a security enabled global
group
4732 A member was added to a security enabled local group
4733 A member was removed from a security enabled local group
4746 A member was added to a security disabled local group
4747 A member was removed from a security disabled local group
4751 A member was added to a security disabled global group
4752 A member was removed from a security disabled global
group
4756 A member was added to a security enabled universal
group
4757 A member was removed from a security enabled universal
group
4761 A member was added to a security disabled universal
group
4762 A member was removed from a security disabled universal
group

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

index 6698b09..5f23771 100644 (file)
@@ -118,17 +118,39 @@ type "logonType". The supported event codes and logon types are:
     2  Interactive
     3  Network
     8  NetworkCleartext
+
 The version number for Authentication messages is now 1.1, changed from 1.0
 
 Password change messages now contain the Windows Event Id "eventId", the
 supported event Id's are:
   4723 Password changed
   4724 Password reset
+
 The version number for PasswordChange messages is now 1.1, changed from 1.0
 
+Group membership change messages now contain the Windows Event Id "eventId",
+the supported event Id's are:
+  4728 A member was added to a security enabled global group
+  4729 A member was removed from a security enabled global group
+  4732 A member was added to a security enabled local group
+  4733 A member was removed from a security enabled local group
+  4746 A member was added to a security disabled local group
+  4747 A member was removed from a security disabled local group
+  4751 A member was added to a security disabled global group
+  4752 A member was removed from a security disabled global group
+  4756 A member was added to a security enabled universal group
+  4757 A member was removed from a security enabled universal group
+  4761 A member was added to a security disabled universal group
+  4762 A member was removed from a security disabled universal group
+
+
+The version number for GroupChange messages is now 1.1, changed from 1.0. Also
+A GroupChange message is generated when a new user is created to log that the
+user has been added to their primary group.
+
 The leading "JSON <message type>:" and source file  prefix of the JSON formatted
 log entries has been removed to make the parsing of the JSON log messages
-easier. JSON log entries now start with 2 spaces folowed by an opening brace
+easier. JSON log entries now start with 2 spaces followed by an opening brace
 i.e. "  {"
 
 
index c711db1..240ad9e 100644 (file)
@@ -9,10 +9,23 @@ interface windows_events
 {
 
        typedef [v1_enum,public] enum {
-               EVT_ID_SUCCESSFUL_LOGON         = 4624,
-               EVT_ID_UNSUCCESSFUL_LOGON       = 4625,
-               EVT_ID_PASSWORD_CHANGE          = 4723,
-               EVT_ID_PASSWORD_RESET           = 4724
+               EVT_ID_NONE                                             =    0,
+               EVT_ID_SUCCESSFUL_LOGON                                 = 4624,
+               EVT_ID_UNSUCCESSFUL_LOGON                               = 4625,
+               EVT_ID_PASSWORD_CHANGE                                  = 4723,
+               EVT_ID_PASSWORD_RESET                                   = 4724,
+               EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP                   = 4728,
+               EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP               = 4729,
+               EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP                    = 4732,
+               EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP                = 4733,
+               EVT_ID_USER_ADDED_TO_LOCAL_GROUP                        = 4746,
+               EVT_ID_USER_REMOVED_FROM_LOCAL_GROUP                    = 4747,
+               EVT_ID_USER_ADDED_TO_GLOBAL_GROUP                       = 4751,
+               EVT_ID_USER_REMOVED_FROM_GLOBAL_GROUP                   = 4752,
+               EVT_ID_USER_ADDED_TO_UNIVERSAL_SEC_GROUP                = 4756,
+               EVT_ID_USER_REMOVED_FROM_UNIVERSAL_SEC_GROUP            = 4757,
+               EVT_ID_USER_ADDED_TO_UNIVERSAL_GROUP                    = 4761,
+               EVT_ID_USER_REMOVED_FROM_UNIVERSAL_GROUP                = 4762
        } event_id_type;
 
        typedef [v1_enum,public] enum {
index 53a8bf6..9fe69bd 100644 (file)
@@ -21,6 +21,10 @@ from __future__ import print_function
 
 import samba.tests
 from samba.dcerpc.messaging import MSG_GROUP_LOG, DSDB_GROUP_EVENT_NAME
+from samba.dcerpc.windows_event_ids import (
+    EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP,
+    EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP
+)
 from samba.samdb import SamDB
 from samba.auth import system_session
 import os
@@ -100,9 +104,9 @@ class GroupAuditTests(AuditLogTestBase):
         #
         # Wait for the primary group change for the created user.
         #
-        messages = self.waitForMessages(1)
+        messages = self.waitForMessages(2)
         print("Received %d messages" % len(messages))
-        self.assertEquals(1,
+        self.assertEquals(2,
                           len(messages),
                           "Did not receive the expected number of messages")
         audit = messages[0]["groupChange"]
@@ -120,6 +124,21 @@ class GroupAuditTests(AuditLogTestBase):
         service_description = self.get_service_description()
         self.assertEquals(service_description, "LDAP")
 
+        # Check the Add message for the new users primary group
+        audit = messages[1]["groupChange"]
+
+        self.assertEqual("Added", audit["action"])
+        user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn
+        group_dn = "cn=domain users,cn=users," + self.base_dn
+        self.assertTrue(user_dn.lower(), audit["user"].lower())
+        self.assertTrue(group_dn.lower(), audit["group"].lower())
+        self.assertRegexpMatches(audit["remoteAddress"],
+                                 self.remoteAddress)
+        self.assertTrue(self.is_guid(audit["sessionId"]))
+        session_id = self.get_session()
+        self.assertEquals(session_id, audit["sessionId"])
+        self.assertEquals(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP,
+                          audit["eventId"])
         #
         # Add the user to a group
         #
@@ -231,11 +250,13 @@ class GroupAuditTests(AuditLogTestBase):
         #
         # Wait for the primary group change for the created user.
         #
-        messages = self.waitForMessages(1)
+        messages = self.waitForMessages(2)
         print("Received %d messages" % len(messages))
-        self.assertEquals(1,
+        self.assertEquals(2,
                           len(messages),
                           "Did not receive the expected number of messages")
+
+        # Check the PrimaryGroup message
         audit = messages[0]["groupChange"]
 
         self.assertEqual("PrimaryGroup", audit["action"])
@@ -251,6 +272,22 @@ class GroupAuditTests(AuditLogTestBase):
         service_description = self.get_service_description()
         self.assertEquals(service_description, "LDAP")
 
+        # Check the Add message for the new users primary group
+        audit = messages[1]["groupChange"]
+
+        self.assertEqual("Added", audit["action"])
+        user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn
+        group_dn = "cn=domain users,cn=users," + self.base_dn
+        self.assertTrue(user_dn.lower(), audit["user"].lower())
+        self.assertTrue(group_dn.lower(), audit["group"].lower())
+        self.assertRegexpMatches(audit["remoteAddress"],
+                                 self.remoteAddress)
+        self.assertTrue(self.is_guid(audit["sessionId"]))
+        session_id = self.get_session()
+        self.assertEquals(session_id, audit["sessionId"])
+        self.assertEquals(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP,
+                          audit["eventId"])
+
         #
         # Add the user to a group, the user needs to be a member of a group
         # before there primary group can be set to that group.
@@ -277,6 +314,8 @@ class GroupAuditTests(AuditLogTestBase):
         self.assertEquals(session_id, audit["sessionId"])
         service_description = self.get_service_description()
         self.assertEquals(service_description, "LDAP")
+        self.assertEquals(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP,
+                          audit["eventId"])
 
         #
         # Change the primary group of a user
@@ -323,6 +362,8 @@ class GroupAuditTests(AuditLogTestBase):
         self.assertEquals(session_id, audit["sessionId"])
         service_description = self.get_service_description()
         self.assertEquals(service_description, "LDAP")
+        self.assertEquals(EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP,
+                          audit["eventId"])
 
         audit = messages[1]["groupChange"]
 
@@ -338,6 +379,8 @@ class GroupAuditTests(AuditLogTestBase):
         self.assertEquals(session_id, audit["sessionId"])
         service_description = self.get_service_description()
         self.assertEquals(service_description, "LDAP")
+        self.assertEquals(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP,
+                          audit["eventId"])
 
         audit = messages[2]["groupChange"]
 
index 7e6e16d..9aba865 100644 (file)
@@ -25,6 +25,7 @@
 #include "includes.h"
 #include "ldb_module.h"
 #include "lib/audit_logging/audit_logging.h"
+#include "librpc/gen_ndr/windows_event_ids.h"
 
 #include "dsdb/samdb/samdb.h"
 #include "dsdb/samdb/ldb_modules/util.h"
 #define AUDIT_JSON_TYPE "groupChange"
 #define AUDIT_HR_TAG "Group Change"
 #define AUDIT_MAJOR 1
-#define AUDIT_MINOR 0
+#define AUDIT_MINOR 1
 #define GROUP_LOG_LVL 5
 
-static const char * const member_attr[] = {"member", NULL};
+static const char *const group_attrs[] = {"member", "groupType", NULL};
+static const char *const group_type_attr[] = {"groupType", NULL};
 static const char * const primary_group_attr[] = {
        "primaryGroupID",
        "objectSID",
@@ -105,13 +107,13 @@ static struct GUID *get_transaction_id(
  * @return A json object containing the details.
  *        NULL if an error was detected
  */
-static struct json_object audit_group_json(
-       const struct ldb_module *module,
-       const struct ldb_request *request,
-       const char *action,
-       const char *user,
-       const char *group,
-       const int status)
+static struct json_object audit_group_json(const struct ldb_module *module,
+                                          const struct ldb_request *request,
+                                          const char *action,
+                                          const char *user,
+                                          const char *group,
+                                          const enum event_id_type event_id,
+                                          const int status)
 {
        struct ldb_context *ldb = NULL;
        const struct dom_sid *sid = NULL;
@@ -137,6 +139,12 @@ static struct json_object audit_group_json(
        if (rc != 0) {
                goto failure;
        }
+       if (event_id != EVT_ID_NONE) {
+               rc = json_add_int(&audit, "eventId", event_id);
+               if (rc != 0) {
+                       goto failure;
+               }
+       }
        rc = json_add_int(&audit, "statusCode", status);
        if (rc != 0) {
                goto failure;
@@ -449,9 +457,11 @@ static const char *get_primary_group_dn(
  * @brief Log details of a change to a users primary group.
  *
  * Log details of a change to a users primary group.
+ * There is no windows event id associated with a Primary Group change.
+ * However for a new user we generate an added to group event.
  *
  * @param module The ldb module.
- * @param request The request deing logged.
+ * @param request The request being logged.
  * @param action Description of the action being performed.
  * @param group The linearized for of the group DN
  * @param status the LDB status code for the processing of the request.
@@ -497,12 +507,7 @@ static void log_primary_group_change(
 
                struct json_object json;
                json = audit_group_json(
-                       module,
-                       request,
-                       action,
-                       user,
-                       group,
-                       status);
+                   module, request, action, user, group, EVT_ID_NONE, status);
                audit_log_json(
                        &json,
                        DBGC_DSDB_GROUP_AUDIT_JSON,
@@ -515,6 +520,13 @@ static void log_primary_group_change(
                                &json);
                }
                json_free(&json);
+               if (request->operation == LDB_ADD) {
+                       /*
+                        * Have just added a user, generate a groupChange
+                        * message indicating the user has been added to thier
+                        * new PrimaryGroup.
+                        */
+               }
        }
        TALLOC_FREE(ctx);
 }
@@ -532,12 +544,12 @@ static void log_primary_group_change(
  * @param status the LDB status code for the processing of the request.
  *
  */
-static void log_membership_change(
-       struct ldb_module *module,
-       const struct ldb_request *request,
-       const char *action,
-       const char *user,
-       const int  status)
+static void log_membership_change(struct ldb_module *module,
+                                 const struct ldb_request *request,
+                                 const char *action,
+                                 const char *user,
+                                 const enum event_id_type event_id,
+                                 const int status)
 {
        const char *group = NULL;
        struct audit_context *ac =
@@ -569,12 +581,7 @@ static void log_membership_change(
                (ac->msg_ctx && ac->send_events)) {
                struct json_object json;
                json = audit_group_json(
-                       module,
-                       request,
-                       action,
-                       user,
-                       group,
-                       status);
+                   module, request, action, user, group, event_id, status);
                audit_log_json(
                        &json,
                        DBGC_DSDB_GROUP_AUDIT_JSON,
@@ -591,6 +598,68 @@ static void log_membership_change(
        TALLOC_FREE(ctx);
 }
 
+/*
+ * @brief Get the windows event type id for removing a user from a group type.
+ *
+ * @param group_type the type of the current group, see libds/common/flags.h
+ *
+ * @return the Windows Event Id
+ *
+ */
+static enum event_id_type get_remove_member_event(uint32_t group_type)
+{
+
+       switch (group_type) {
+       case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
+               return EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP;
+       case GTYPE_SECURITY_GLOBAL_GROUP:
+               return EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP;
+       case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
+               return EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP;
+       case GTYPE_SECURITY_UNIVERSAL_GROUP:
+               return EVT_ID_USER_REMOVED_FROM_UNIVERSAL_SEC_GROUP;
+       case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
+               return EVT_ID_USER_REMOVED_FROM_GLOBAL_GROUP;
+       case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
+               return EVT_ID_USER_REMOVED_FROM_LOCAL_GROUP;
+       case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
+               return EVT_ID_USER_REMOVED_FROM_UNIVERSAL_GROUP;
+       default:
+               return EVT_ID_NONE;
+       }
+}
+
+/*
+ * @brief Get the windows event type id for adding a user to a group type.
+ *
+ * @param group_type the type of the current group, see libds/common/flags.h
+ *
+ * @return the Windows Event Id
+ *
+ */
+static enum event_id_type get_add_member_event(uint32_t group_type)
+{
+
+       switch (group_type) {
+       case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
+               return EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP;
+       case GTYPE_SECURITY_GLOBAL_GROUP:
+               return EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP;
+       case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
+               return EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP;
+       case GTYPE_SECURITY_UNIVERSAL_GROUP:
+               return EVT_ID_USER_ADDED_TO_UNIVERSAL_SEC_GROUP;
+       case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
+               return EVT_ID_USER_ADDED_TO_GLOBAL_GROUP;
+       case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
+               return EVT_ID_USER_ADDED_TO_LOCAL_GROUP;
+       case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
+               return EVT_ID_USER_ADDED_TO_UNIVERSAL_GROUP;
+       default:
+               return EVT_ID_NONE;
+       }
+}
+
 /*
  * @brief Log all the changes to a users group membership.
  *
@@ -604,12 +673,12 @@ static void log_membership_change(
  * @param status the LDB status code for the processing of the request.
  *
  */
-static void log_membership_changes(
-       struct ldb_module *module,
-       const struct ldb_request *request,
-       struct ldb_message_element *el,
-       struct ldb_message_element *old_el,
-       int status)
+static void log_membership_changes(struct ldb_module *module,
+                                  const struct ldb_request *request,
+                                  struct ldb_message_element *el,
+                                  struct ldb_message_element *old_el,
+                                  uint32_t group_type,
+                                  int status)
 {
        unsigned int i, old_i, new_i;
        unsigned int old_num_values;
@@ -674,6 +743,7 @@ static void log_membership_changes(
                         * the new record. So it's been deleted
                         */
                        const char *user = NULL;
+                       enum event_id_type event_id;
                        if (old_val->dsdb_dn == NULL) {
                                really_parse_trusted_dn(
                                        ctx,
@@ -682,12 +752,9 @@ static void log_membership_changes(
                                        LDB_SYNTAX_DN);
                        }
                        user = ldb_dn_get_linearized(old_val->dsdb_dn->dn);
+                       event_id = get_remove_member_event(group_type);
                        log_membership_change(
-                               module,
-                               request,
-                               "Removed",
-                               user,
-                               status);
+                           module, request, "Removed", user, event_id, status);
                        old_i++;
                } else if (cmp == BINARY_EQUAL) {
                        /*
@@ -739,27 +806,31 @@ static void log_membership_changes(
                                 * DN has been deleted.
                                 */
                                const char *user = NULL;
+                               enum event_id_type event_id;
                                user = ldb_dn_get_linearized(
                                        old_val->dsdb_dn->dn);
-                               log_membership_change(
-                                       module,
-                                       request,
-                                       "Removed",
-                                       user,
-                                       status);
+                               event_id = get_remove_member_event(group_type);
+                               log_membership_change(module,
+                                                     request,
+                                                     "Removed",
+                                                     user,
+                                                     event_id,
+                                                     status);
                        } else {
                                /*
                                 * DN has been re-added
                                 */
                                const char *user = NULL;
+                               enum event_id_type event_id;
                                user = ldb_dn_get_linearized(
                                        new_val->dsdb_dn->dn);
-                               log_membership_change(
-                                       module,
-                                       request,
-                                       "Added",
-                                       user,
-                                       status);
+                               event_id = get_add_member_event(group_type);
+                               log_membership_change(module,
+                                                     request,
+                                                     "Added",
+                                                     user,
+                                                     event_id,
+                                                     status);
                        }
                        old_i++;
                        new_i++;
@@ -769,6 +840,7 @@ static void log_membership_changes(
                         * original, so it must have been added.
                         */
                        const char *user = NULL;
+                       enum event_id_type event_id;
                        if ( new_val->dsdb_dn == NULL) {
                                really_parse_trusted_dn(
                                        ctx,
@@ -777,12 +849,9 @@ static void log_membership_changes(
                                        LDB_SYNTAX_DN);
                        }
                        user = ldb_dn_get_linearized(new_val->dsdb_dn->dn);
+                       event_id = get_add_member_event(group_type);
                        log_membership_change(
-                               module,
-                               request,
-                               "Added",
-                               user,
-                               status);
+                           module, request, "Added", user, event_id, status);
                        new_i++;
                }
        }
@@ -790,6 +859,46 @@ static void log_membership_changes(
        TALLOC_FREE(ctx);
 }
 
+/*
+ * @brief log a group change message for a newly added user.
+ *
+ * When a user is added we need to generate a GroupChange Add message to
+ * log that the user has been added to their PrimaryGroup
+ */
+static void log_new_user_added_to_primary_group(
+    TALLOC_CTX *ctx,
+    struct audit_callback_context *acc,
+    const char *group,
+    const int status)
+{
+       uint32_t group_type;
+       enum event_id_type event_id = EVT_ID_NONE;
+       struct ldb_result *res = NULL;
+       struct ldb_dn *group_dn = NULL;
+       struct ldb_context *ldb = NULL;
+       int ret;
+
+       ldb = ldb_module_get_ctx(acc->module);
+       group_dn = ldb_dn_new(ctx, ldb, group);
+       ret = dsdb_module_search_dn(acc->module,
+                                   ctx,
+                                   &res,
+                                   group_dn,
+                                   group_type_attr,
+                                   DSDB_FLAG_NEXT_MODULE |
+                                       DSDB_SEARCH_REVEAL_INTERNALS |
+                                       DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
+                                   NULL);
+       if (ret == LDB_SUCCESS) {
+               const char *user = NULL;
+               group_type =
+                   ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
+               event_id = get_add_member_event(group_type);
+               user = dsdb_audit_get_primary_dn(acc->request);
+               log_membership_change(
+                   acc->module, acc->request, "Added", user, event_id, status);
+       }
+}
 
 /*
  * @brief Log the details of a primary group change.
@@ -811,6 +920,7 @@ static void log_user_primary_group_change(
        struct dom_sid *account_sid = NULL;
        int ret;
        const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
+
        if (status == LDB_SUCCESS && msg != NULL) {
                struct ldb_result *res = NULL;
                ret = dsdb_module_search_dn(
@@ -853,6 +963,16 @@ static void log_user_primary_group_change(
                        "PrimaryGroup",
                        group,
                        status);
+               /*
+                * Are we adding a new user with the primaryGroupID
+                * set. If so and we're generating JSON audit logs, will need to
+                * generate an "Add" message with the appropriate windows
+                * event id.
+                */
+               if (acc->request->operation == LDB_ADD) {
+                       log_new_user_added_to_primary_group(
+                           ctx, acc, group, status);
+               }
        }
        TALLOC_FREE(ctx);
 }
@@ -866,7 +986,6 @@ static void log_user_primary_group_change(
  * @param acc details of the group memberships before the operation.
  * @param status The status code returned by the operation.
  *
- * @return an LDB status code.
  */
 static void log_group_membership_changes(
        struct audit_callback_context *acc,
@@ -875,6 +994,7 @@ static void log_group_membership_changes(
        TALLOC_CTX *ctx = talloc_new(NULL);
        struct ldb_message_element *new_val = NULL;
        int ret;
+       uint32_t group_type;
        const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
        if (status == LDB_SUCCESS && msg != NULL) {
                struct ldb_result *res = NULL;
@@ -883,21 +1003,23 @@ static void log_group_membership_changes(
                        ctx,
                        &res,
                        msg->dn,
-                       member_attr,
+                       group_attrs,
                        DSDB_FLAG_NEXT_MODULE |
                        DSDB_SEARCH_REVEAL_INTERNALS |
                        DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
                        NULL);
                if (ret == LDB_SUCCESS) {
                        new_val = ldb_msg_find_element(res->msgs[0], "member");
-               }
+                       group_type = ldb_msg_find_attr_as_uint(
+                           res->msgs[0], "groupType", 0);
+                       log_membership_changes(acc->module,
+                                              acc->request,
+                                              new_val,
+                                              acc->members,
+                                              group_type,
+                                              status);
+                               }
        }
-       log_membership_changes(
-               acc->module,
-               acc->request,
-               new_val,
-               acc->members,
-               status);
        TALLOC_FREE(ctx);
 }
 
@@ -1300,7 +1422,7 @@ static int set_group_modify_callback(
                context,
                &res,
                req->op.add.message->dn,
-               member_attr,
+               group_attrs,
                DSDB_FLAG_NEXT_MODULE |
                DSDB_SEARCH_REVEAL_INTERNALS |
                DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
index 241c053..a6b9d60 100644 (file)
@@ -82,8 +82,8 @@ void audit_message_send(
        messages_sent++;
 }
 
-#define check_group_change_message(m, u, a)\
-       _check_group_change_message(m, u, a, __FILE__, __LINE__);
+#define check_group_change_message(m, u, a, e)                                 \
+       _check_group_change_message(m, u, a, e, __FILE__, __LINE__);
 /*
  * declare the internal cmocka cm_print_error so that we can output messages
  * in sub unit format
@@ -102,17 +102,18 @@ void cm_print_error(const char * const format, ...);
  * There should be a user element matching the expected value
  * There should be an action matching the expected value
  */
-static void _check_group_change_message(
-       const int message,
-       const char *user,
-       const char *action,
-       const char *file,
-       const int line)
+static void _check_group_change_message(const int message,
+                                       const char *user,
+                                       const char *action,
+                                       enum event_id_type event_id,
+                                       const char *file,
+                                       const int line)
 {
        struct json_object json;
        json_t *audit = NULL;
        json_t *v = NULL;
        const char* value;
+       int int_value;
        int cmp;
 
        json = messages[message];
@@ -157,12 +158,11 @@ static void _check_group_change_message(
        /*
         * Validate the groupChange element
         */
-       if (json_object_size(audit) != 10) {
-               cm_print_error(
-                   "Unexpected number of elements in groupChange "
-                   "%zu != %d\n",
-                   json_object_size(audit),
-                   10);
+       if (json_object_size(audit) != 11) {
+               cm_print_error("Unexpected number of elements in groupChange "
+                              "%zu != %d\n",
+                              json_object_size(audit),
+                              11);
                _fail(file, line);
        }
        /*
@@ -183,7 +183,6 @@ static void _check_group_change_message(
                    user);
                _fail(file, line);
        }
-
        /*
         * Validate the action element
         */
@@ -202,6 +201,23 @@ static void _check_group_change_message(
                    action);
                _fail(file, line);
        }
+
+       /*
+        * Validate the eventId element
+        */
+       v = json_object_get(audit, "eventId");
+       if (v == NULL) {
+               cm_print_error("No eventId element\n");
+               _fail(file, line);
+       }
+
+       int_value = json_integer_value(v);
+       if (int_value != event_id) {
+               cm_print_error("Unexpected eventId \"%d\" != \"%d\"\n",
+                              int_value,
+                              event_id);
+               _fail(file, line);
+       }
 }
 
 #define check_timestamp(b, t)\
@@ -752,6 +768,7 @@ static void test_audit_group_json(void **state)
        struct GUID transaction_id;
        const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
 
+       enum event_id_type event_id = EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP;
 
        struct json_object json;
        json_t *audit = NULL;
@@ -779,13 +796,13 @@ static void test_audit_group_json(void **state)
        add_transaction_id(req, TRANSACTION);
 
        before = time(NULL);
-       json = audit_group_json(
-               module,
-               req,
-               "the-action",
-               "the-user-name",
-               "the-group-name",
-               LDB_ERR_OPERATIONS_ERROR);
+       json = audit_group_json(module,
+                               req,
+                               "the-action",
+                               "the-user-name",
+                               "the-group-name",
+                               event_id,
+                               LDB_ERR_OPERATIONS_ERROR);
        assert_int_equal(3, json_object_size(json.root));
 
        v = json_object_get(json.root, "type");
@@ -800,12 +817,18 @@ static void test_audit_group_json(void **state)
        audit = json_object_get(json.root, "groupChange");
        assert_non_null(audit);
        assert_true(json_is_object(audit));
-       assert_int_equal(10, json_object_size(audit));
+       assert_int_equal(11, json_object_size(audit));
 
        o = json_object_get(audit, "version");
        assert_non_null(o);
        check_version(o, AUDIT_MAJOR, AUDIT_MINOR);
 
+       v = json_object_get(audit, "eventId");
+       assert_non_null(v);
+       assert_true(json_is_integer(v));
+       assert_int_equal(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP,
+                        json_integer_value(v));
+
        v = json_object_get(audit, "statusCode");
        assert_non_null(v);
        assert_true(json_is_integer(v));
@@ -887,6 +910,7 @@ static void test_log_membership_changes_removed(void **state)
        struct ldb_request *req = NULL;
        struct ldb_message_element *new_el = NULL;
        struct ldb_message_element *old_el = NULL;
+       uint32_t group_type = GTYPE_SECURITY_GLOBAL_GROUP;
        int status = 0;
        TALLOC_CTX *ctx = talloc_new(NULL);
 
@@ -931,7 +955,7 @@ static void test_log_membership_changes_removed(void **state)
         * call log_membership_changes
         */
        messages_sent = 0;
-       log_membership_changes(module, req, new_el, old_el, status);
+       log_membership_changes(module, req, new_el, old_el, group_type, status);
 
        /*
         * Check the results
@@ -941,7 +965,8 @@ static void test_log_membership_changes_removed(void **state)
        check_group_change_message(
            0,
            "cn=grpadttstuser01,cn=users,DC=addom,DC=samba,DC=example,DC=com",
-           "Removed");
+           "Removed",
+           EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP);
 
        /*
         * Clean up
@@ -969,6 +994,7 @@ static void test_log_membership_changes_remove_all(void **state)
        struct ldb_message_element *new_el = NULL;
        struct ldb_message_element *old_el = NULL;
        int status = 0;
+       uint32_t group_type = GTYPE_SECURITY_BUILTIN_LOCAL_GROUP;
        TALLOC_CTX *ctx = talloc_new(NULL);
 
        setup_ldb(ctx, &ldb, &module, IP, SESSION, SID);
@@ -1007,7 +1033,7 @@ static void test_log_membership_changes_remove_all(void **state)
         * call log_membership_changes
         */
        messages_sent = 0;
-       log_membership_changes( module, req, new_el, old_el, status);
+       log_membership_changes(module, req, new_el, old_el, group_type, status);
 
        /*
         * Check the results
@@ -1017,12 +1043,14 @@ static void test_log_membership_changes_remove_all(void **state)
        check_group_change_message(
            0,
            "cn=grpadttstuser01,cn=users,DC=addom,DC=samba,DC=example,DC=com",
-           "Removed");
+           "Removed",
+           EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP);
 
        check_group_change_message(
            1,
            "CN=testuser131953,CN=Users,DC=addom,DC=samba,DC=example,DC=com",
-           "Removed");
+           "Removed",
+           EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP);
 
        /*
         * Clean up
@@ -1052,6 +1080,7 @@ static void test_log_membership_changes_added(void **state)
        struct ldb_request *req = NULL;
        struct ldb_message_element *new_el = NULL;
        struct ldb_message_element *old_el = NULL;
+       uint32_t group_type = GTYPE_SECURITY_DOMAIN_LOCAL_GROUP;
        int status = 0;
        TALLOC_CTX *ctx = talloc_new(NULL);
 
@@ -1095,7 +1124,7 @@ static void test_log_membership_changes_added(void **state)
         * call log_membership_changes
         */
        messages_sent = 0;
-       log_membership_changes( module, req, new_el, old_el, status);
+       log_membership_changes(module, req, new_el, old_el, group_type, status);
 
        /*
         * Check the results
@@ -1105,7 +1134,8 @@ static void test_log_membership_changes_added(void **state)
        check_group_change_message(
            0,
            "cn=grpadttstuser01,cn=users,DC=addom,DC=samba,DC=example,DC=com",
-           "Added");
+           "Added",
+           EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP);
 
        /*
         * Clean up
@@ -1133,6 +1163,7 @@ static void test_log_membership_changes_add_to_empty(void **state)
        struct ldb_request *req = NULL;
        struct ldb_message_element *new_el = NULL;
        struct ldb_message_element *old_el = NULL;
+       uint32_t group_type = GTYPE_SECURITY_UNIVERSAL_GROUP;
        int status = 0;
        TALLOC_CTX *ctx = talloc_new(NULL);
 
@@ -1174,18 +1205,20 @@ static void test_log_membership_changes_add_to_empty(void **state)
         * Run log membership changes
         */
        messages_sent = 0;
-       log_membership_changes( module, req, new_el, old_el, status);
+       log_membership_changes(module, req, new_el, old_el, group_type, status);
        assert_int_equal(2, messages_sent);
 
        check_group_change_message(
            0,
            "cn=grpadttstuser01,cn=users,DC=addom,DC=samba,DC=example,DC=com",
-           "Added");
+           "Added",
+           EVT_ID_USER_ADDED_TO_UNIVERSAL_SEC_GROUP);
 
        check_group_change_message(
            1,
-            "CN=testuser131953,CN=Users,DC=addom,DC=samba,DC=example,DC=com",
-           "Added");
+           "CN=testuser131953,CN=Users,DC=addom,DC=samba,DC=example,DC=com",
+           "Added",
+           EVT_ID_USER_ADDED_TO_UNIVERSAL_SEC_GROUP);
 
        json_free(&messages[0]);
        json_free(&messages[1]);
@@ -1216,6 +1249,7 @@ static void test_log_membership_changes_rmd_flags(void **state)
        struct ldb_request *req = NULL;
        struct ldb_message_element *new_el = NULL;
        struct ldb_message_element *old_el = NULL;
+       uint32_t group_type = GTYPE_SECURITY_GLOBAL_GROUP;
        int status = 0;
        TALLOC_CTX *ctx = talloc_new(NULL);
 
@@ -1286,7 +1320,7 @@ static void test_log_membership_changes_rmd_flags(void **state)
         * call log_membership_changes
         */
        messages_sent = 0;
-       log_membership_changes( module, req, new_el, old_el, status);
+       log_membership_changes(module, req, new_el, old_el, group_type, status);
 
        /*
         * Check the results
@@ -1296,11 +1330,13 @@ static void test_log_membership_changes_rmd_flags(void **state)
        check_group_change_message(
            0,
            "cn=grpadttstuser03,cn=users,DC=addom,DC=samba,DC=example,DC=com",
-           "Removed");
+           "Removed",
+           EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP);
        check_group_change_message(
            1,
            "cn=grpadttstuser04,cn=users,DC=addom,DC=samba,DC=example,DC=com",
-           "Added");
+           "Added",
+           EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP);
 
        /*
         * Clean up
@@ -1310,6 +1346,71 @@ static void test_log_membership_changes_rmd_flags(void **state)
        TALLOC_FREE(ctx);
 }
 
+static void test_get_add_member_event(void **state)
+{
+       assert_int_equal(
+           EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP,
+           get_add_member_event(GTYPE_SECURITY_BUILTIN_LOCAL_GROUP));
+
+       assert_int_equal(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP,
+                        get_add_member_event(GTYPE_SECURITY_GLOBAL_GROUP));
+
+       assert_int_equal(
+           EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP,
+           get_add_member_event(GTYPE_SECURITY_DOMAIN_LOCAL_GROUP));
+
+       assert_int_equal(EVT_ID_USER_ADDED_TO_UNIVERSAL_SEC_GROUP,
+                        get_add_member_event(GTYPE_SECURITY_UNIVERSAL_GROUP));
+
+       assert_int_equal(EVT_ID_USER_ADDED_TO_GLOBAL_GROUP,
+                        get_add_member_event(GTYPE_DISTRIBUTION_GLOBAL_GROUP));
+
+       assert_int_equal(
+           EVT_ID_USER_ADDED_TO_LOCAL_GROUP,
+           get_add_member_event(GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP));
+
+       assert_int_equal(
+           EVT_ID_USER_ADDED_TO_UNIVERSAL_GROUP,
+           get_add_member_event(GTYPE_DISTRIBUTION_UNIVERSAL_GROUP));
+
+       assert_int_equal(EVT_ID_NONE, get_add_member_event(0));
+
+       assert_int_equal(EVT_ID_NONE, get_add_member_event(UINT32_MAX));
+}
+
+static void test_get_remove_member_event(void **state)
+{
+       assert_int_equal(
+           EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP,
+           get_remove_member_event(GTYPE_SECURITY_BUILTIN_LOCAL_GROUP));
+
+       assert_int_equal(EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP,
+                        get_remove_member_event(GTYPE_SECURITY_GLOBAL_GROUP));
+
+       assert_int_equal(
+           EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP,
+           get_remove_member_event(GTYPE_SECURITY_DOMAIN_LOCAL_GROUP));
+
+       assert_int_equal(
+           EVT_ID_USER_REMOVED_FROM_UNIVERSAL_SEC_GROUP,
+           get_remove_member_event(GTYPE_SECURITY_UNIVERSAL_GROUP));
+
+       assert_int_equal(
+           EVT_ID_USER_REMOVED_FROM_GLOBAL_GROUP,
+           get_remove_member_event(GTYPE_DISTRIBUTION_GLOBAL_GROUP));
+
+       assert_int_equal(
+           EVT_ID_USER_REMOVED_FROM_LOCAL_GROUP,
+           get_remove_member_event(GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP));
+
+       assert_int_equal(
+           EVT_ID_USER_REMOVED_FROM_UNIVERSAL_GROUP,
+           get_remove_member_event(GTYPE_DISTRIBUTION_UNIVERSAL_GROUP));
+
+       assert_int_equal(EVT_ID_NONE, get_remove_member_event(0));
+
+       assert_int_equal(EVT_ID_NONE, get_remove_member_event(UINT32_MAX));
+}
 /*
  * Note: to run under valgrind us:
  *       valgrind --suppressions=test_group_audit.valgrind bin/test_group_audit
@@ -1319,17 +1420,19 @@ static void test_log_membership_changes_rmd_flags(void **state)
  */
 int main(void) {
        const struct CMUnitTest tests[] = {
-               cmocka_unit_test(test_audit_group_json),
-               cmocka_unit_test(test_get_transaction_id),
-               cmocka_unit_test(test_audit_group_hr),
-               cmocka_unit_test(test_get_parsed_dns),
-               cmocka_unit_test(test_dn_compare),
-               cmocka_unit_test(test_get_primary_group_dn),
-               cmocka_unit_test(test_log_membership_changes_removed),
-               cmocka_unit_test(test_log_membership_changes_remove_all),
-               cmocka_unit_test(test_log_membership_changes_added),
-               cmocka_unit_test(test_log_membership_changes_add_to_empty),
-               cmocka_unit_test(test_log_membership_changes_rmd_flags),
+           cmocka_unit_test(test_audit_group_json),
+           cmocka_unit_test(test_get_transaction_id),
+           cmocka_unit_test(test_audit_group_hr),
+           cmocka_unit_test(test_get_parsed_dns),
+           cmocka_unit_test(test_dn_compare),
+           cmocka_unit_test(test_get_primary_group_dn),
+           cmocka_unit_test(test_log_membership_changes_removed),
+           cmocka_unit_test(test_log_membership_changes_remove_all),
+           cmocka_unit_test(test_log_membership_changes_added),
+           cmocka_unit_test(test_log_membership_changes_add_to_empty),
+           cmocka_unit_test(test_log_membership_changes_rmd_flags),
+           cmocka_unit_test(test_get_add_member_event),
+           cmocka_unit_test(test_get_remove_member_event),
        };
 
        cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
index 3c8829e..ea9f2b7 100644 (file)
@@ -139,6 +139,7 @@ static void test_audit_group_json(void **state)
        struct GUID transaction_id;
        const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
 
+       enum event_id_type event_id = EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP;
 
        struct json_object json;
 
@@ -166,13 +167,13 @@ static void test_audit_group_json(void **state)
 
        will_return(__wrap_json_new_object, false);
 
-       json = audit_group_json(
-               module,
-               req,
-               "the-action",
-               "the-user-name",
-               "the-group-name",
-               LDB_ERR_OPERATIONS_ERROR);
+       json = audit_group_json(module,
+                               req,
+                               "the-action",
+                               "the-user-name",
+                               "the-group-name",
+                               event_id,
+                               LDB_ERR_OPERATIONS_ERROR);
        assert_true(json_is_invalid(&json));
 
        /*
@@ -182,13 +183,13 @@ static void test_audit_group_json(void **state)
        will_return(__wrap_json_new_object, true);
        will_return(__wrap_json_add_version, JSON_ERROR);
 
-       json = audit_group_json(
-               module,
-               req,
-               "the-action",
-               "the-user-name",
-               "the-group-name",
-               LDB_ERR_OPERATIONS_ERROR);
+       json = audit_group_json(module,
+                               req,
+                               "the-action",
+                               "the-user-name",
+                               "the-group-name",
+                               event_id,
+                               LDB_ERR_OPERATIONS_ERROR);
        assert_true(json_is_invalid(&json));
 
        /*
@@ -199,13 +200,13 @@ static void test_audit_group_json(void **state)
        will_return(__wrap_json_add_version, 0);
        will_return(__wrap_json_new_object, false);
 
-       json = audit_group_json(
-               module,
-               req,
-               "the-action",
-               "the-user-name",
-               "the-group-name",
-               LDB_ERR_OPERATIONS_ERROR);
+       json = audit_group_json(module,
+                               req,
+                               "the-action",
+                               "the-user-name",
+                               "the-group-name",
+                               event_id,
+                               LDB_ERR_OPERATIONS_ERROR);
        assert_true(json_is_invalid(&json));
 
        /*
@@ -216,13 +217,13 @@ static void test_audit_group_json(void **state)
        will_return(__wrap_json_new_object, true);
        will_return(__wrap_json_add_timestamp, JSON_ERROR);
 
-       json = audit_group_json(
-               module,
-               req,
-               "the-action",
-               "the-user-name",
-               "the-group-name",
-               LDB_ERR_OPERATIONS_ERROR);
+       json = audit_group_json(module,
+                               req,
+                               "the-action",
+                               "the-user-name",
+                               "the-group-name",
+                               event_id,
+                               LDB_ERR_OPERATIONS_ERROR);
        assert_true(json_is_invalid(&json));
 
 
@@ -234,13 +235,13 @@ static void test_audit_group_json(void **state)
        will_return(__wrap_json_new_object, true);
        will_return(__wrap_json_add_timestamp, 0);
 
-       json = audit_group_json(
-               module,
-               req,
-               "the-action",
-               "the-user-name",
-               "the-group-name",
-               LDB_ERR_OPERATIONS_ERROR);
+       json = audit_group_json(module,
+                               req,
+                               "the-action",
+                               "the-user-name",
+                               "the-group-name",
+                               event_id,
+                               LDB_ERR_OPERATIONS_ERROR);
        assert_false(json_is_invalid(&json));
 
        json_free(&json);