4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * Provide an audit log of changes made to group memberships
26 #include "ldb_module.h"
27 #include "lib/audit_logging/audit_logging.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "dsdb/samdb/ldb_modules/util.h"
31 #include "dsdb/samdb/ldb_modules/audit_util_proto.h"
32 #include "libcli/security/dom_sid.h"
33 #include "auth/common_auth.h"
34 #include "param/param.h"
36 #define AUDIT_JSON_TYPE "groupChange"
37 #define AUDIT_HR_TAG "Group Change"
40 #define GROUP_LOG_LVL 5
42 static const char * const member_attr[] = {"member", NULL};
43 static const char * const primary_group_attr[] = {
48 struct audit_context {
50 struct imessaging_context *msg_ctx;
53 struct audit_callback_context {
54 struct ldb_request *request;
55 struct ldb_module *module;
56 struct ldb_message_element *members;
57 uint32_t primary_group;
59 struct audit_callback_context *acc,
64 * @brief get the transaction id.
66 * Get the id of the transaction that the current request is contained in.
68 * @param req the request.
70 * @return the transaction id GUID, or NULL if it is not there.
72 static struct GUID *get_transaction_id(
73 const struct ldb_request *request)
75 struct ldb_control *control;
76 struct dsdb_control_transaction_identifier *transaction_id;
78 control = ldb_request_get_control(
79 discard_const(request),
80 DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID);
81 if (control == NULL) {
84 transaction_id = talloc_get_type(
86 struct dsdb_control_transaction_identifier);
87 if (transaction_id == NULL) {
90 return &transaction_id->transaction_guid;
94 * @brief generate a JSON log entry for a group change.
96 * Generate a JSON object containing details of a users group change.
98 * @param module the ldb module
99 * @param request the ldb_request
100 * @param action the change action being performed
101 * @param user the user name
102 * @param group the group name
103 * @param status the ldb status code for the ldb operation.
105 * @return A json object containing the details.
106 * NULL if an error was detected
108 static struct json_object audit_group_json(
109 const struct ldb_module *module,
110 const struct ldb_request *request,
116 struct ldb_context *ldb = NULL;
117 const struct dom_sid *sid = NULL;
118 struct json_object wrapper = json_empty_object;
119 struct json_object audit = json_empty_object;
120 const struct tsocket_address *remote = NULL;
121 const struct GUID *unique_session_token = NULL;
122 struct GUID *transaction_id = NULL;
125 ldb = ldb_module_get_ctx(discard_const(module));
127 remote = dsdb_audit_get_remote_address(ldb);
128 sid = dsdb_audit_get_user_sid(module);
129 unique_session_token = dsdb_audit_get_unique_session_token(module);
130 transaction_id = get_transaction_id(request);
132 audit = json_new_object();
133 if (json_is_invalid(&audit)) {
136 rc = json_add_version(&audit, AUDIT_MAJOR, AUDIT_MINOR);
140 rc = json_add_int(&audit, "statusCode", status);
144 rc = json_add_string(&audit, "status", ldb_strerror(status));
148 rc = json_add_string(&audit, "action", action);
152 rc = json_add_address(&audit, "remoteAddress", remote);
156 rc = json_add_sid(&audit, "userSid", sid);
160 rc = json_add_string(&audit, "group", group);
164 rc = json_add_guid(&audit, "transactionId", transaction_id);
168 rc = json_add_guid(&audit, "sessionId", unique_session_token);
172 rc = json_add_string(&audit, "user", user);
177 wrapper = json_new_object();
178 if (json_is_invalid(&wrapper)) {
181 rc = json_add_timestamp(&wrapper);
185 rc = json_add_string(&wrapper, "type", AUDIT_JSON_TYPE);
189 rc = json_add_object(&wrapper, AUDIT_JSON_TYPE, &audit);
197 * On a failure audit will not have been added to wrapper so it
198 * needs to free it to avoid a leak.
200 * wrapper is freed to invalidate it as it will have only been
201 * partially constructed and may be inconsistent.
203 * All the json manipulation routines handle a freed object correctly
207 DBG_ERR("Failed to create group change JSON log message\n");
212 * @brief generate a human readable log entry for a group change.
214 * Generate a human readable log entry containing details of a users group
217 * @param ctx the talloc context owning the returned log entry
218 * @param module the ldb module
219 * @param request the ldb_request
220 * @param action the change action being performed
221 * @param user the user name
222 * @param group the group name
223 * @param status the ldb status code for the ldb operation.
225 * @return A human readable log line.
227 static char *audit_group_human_readable(
229 const struct ldb_module *module,
230 const struct ldb_request *request,
236 struct ldb_context *ldb = NULL;
237 const char *remote_host = NULL;
238 const struct dom_sid *sid = NULL;
239 const char *user_sid = NULL;
240 const char *timestamp = NULL;
241 char *log_entry = NULL;
243 TALLOC_CTX *ctx = talloc_new(NULL);
245 ldb = ldb_module_get_ctx(discard_const(module));
247 remote_host = dsdb_audit_get_remote_host(ldb, ctx);
248 sid = dsdb_audit_get_user_sid(module);
249 user_sid = dom_sid_string(ctx, sid);
250 timestamp = audit_get_timestamp(ctx);
252 log_entry = talloc_asprintf(
254 "[%s] at [%s] status [%s] "
255 "Remote host [%s] SID [%s] Group [%s] User [%s]",
258 ldb_strerror(status),
268 * @brief generate an array of parsed_dns, deferring the actual parsing.
270 * Get an array of 'struct parsed_dns' without the parsing.
271 * The parsed_dns are parsed only when needed to avoid the expense of parsing.
273 * This procedure assumes that the dn's are sorted in GUID order and contains
274 * no duplicates. This should be valid as the module sits below repl_meta_data
275 * which ensures this.
277 * @param mem_ctx The memory context that will own the generated array
278 * @param el The message element used to generate the array.
280 * @return an array of struct parsed_dns, or NULL in the event of an error
282 static struct parsed_dn *get_parsed_dns(
284 struct ldb_message_element *el)
286 struct parsed_dn *pdn = NULL;
290 if (el == NULL || el->num_values == 0) {
294 pdn = talloc_zero_array(mem_ctx, struct parsed_dn, el->num_values);
296 DBG_ERR("Out of memory\n");
300 for (i = 0; i < el->num_values; i++) {
301 pdn[i].v = &el->values[i];
307 enum dn_compare_result {
314 * @brief compare parsed_dns
316 * Compare two parsed_dn structures, parsing the entries if necessary.
317 * To avoid the overhead of parsing the DN's this function does a binary
318 * compare first. Only parsing the DN's they are not equal at a binary level.
320 * @param ctx talloc context that will own the parsed dsdb_dn
321 * @param ldb ldb_context
322 * @param old_val The old value
323 * @param new_val The old value
325 * @return BINARY_EQUAL values are equal at a binary level
326 * EQUAL DN's are equal but the meta data is different
327 * LESS_THAN old value < new value
328 * GREATER_THAN old value > new value
331 static enum dn_compare_result dn_compare(
333 struct ldb_context *ldb,
334 struct parsed_dn *old_val,
335 struct parsed_dn *new_val) {
340 * Do a binary compare first to avoid unnecessary parsing
342 if (data_blob_cmp(new_val->v, old_val->v) == 0) {
344 * Values are equal at a binary level so no need
345 * for further processing
350 * Values not equal at the binary level, so lets
351 * do a GUID ordering compare. To do this we will need to ensure
352 * that the dn's have been parsed.
354 if (old_val->dsdb_dn == NULL) {
355 really_parse_trusted_dn(
361 if (new_val->dsdb_dn == NULL) {
362 really_parse_trusted_dn(
369 res = ndr_guid_compare(&new_val->guid, &old_val->guid);
372 } else if (res == 0) {
380 * @brief Get the DN of a users primary group as a printable string.
382 * Get the DN of a users primary group as a printable string.
384 * @param mem_ctx Talloc context the the returned string will be allocated on.
385 * @param module The ldb module
386 * @param account_sid The SID for the uses account.
387 * @param primary_group_rid The RID for the users primary group.
389 * @return a formatted DN, or null if there is an error.
391 static const char *get_primary_group_dn(
393 struct ldb_module *module,
394 struct dom_sid *account_sid,
395 uint32_t primary_group_rid)
399 struct ldb_context *ldb = NULL;
400 struct dom_sid *domain_sid = NULL;
401 struct dom_sid *primary_group_sid = NULL;
403 struct ldb_dn *dn = NULL;
404 struct ldb_message *msg = NULL;
407 ldb = ldb_module_get_ctx(module);
409 status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
410 if (!NT_STATUS_IS_OK(status)) {
414 primary_group_sid = dom_sid_add_rid(
418 if (!primary_group_sid) {
422 sid = dom_sid_string(mem_ctx, primary_group_sid);
427 dn = ldb_dn_new_fmt(mem_ctx, ldb, "<SID=%s>", sid);
431 rc = dsdb_search_one(
440 if (rc != LDB_SUCCESS) {
444 return ldb_dn_get_linearized(msg->dn);
448 * @brief Log details of a change to a users primary group.
450 * Log details of a change to a users primary group.
452 * @param module The ldb module.
453 * @param request The request deing logged.
454 * @param action Description of the action being performed.
455 * @param group The linearized for of the group DN
456 * @param status the LDB status code for the processing of the request.
459 static void log_primary_group_change(
460 struct ldb_module *module,
461 const struct ldb_request *request,
466 const char *user = NULL;
468 struct audit_context *ac =
470 ldb_module_get_private(module),
471 struct audit_context);
473 TALLOC_CTX *ctx = talloc_new(NULL);
475 user = dsdb_audit_get_primary_dn(request);
476 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
477 char *message = NULL;
478 message = audit_group_human_readable(
486 audit_log_human_text(
489 DBGC_DSDB_GROUP_AUDIT,
491 TALLOC_FREE(message);
494 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
495 (ac->msg_ctx && ac->send_events)) {
497 struct json_object json;
498 json = audit_group_json(
508 DBGC_DSDB_GROUP_AUDIT_JSON,
510 if (ac->send_events) {
513 DSDB_GROUP_EVENT_NAME,
523 * @brief Log details of a single change to a users group membership.
525 * Log details of a change to a users group membership, except for changes
526 * to their primary group which is handled by log_primary_group_change.
528 * @param module The ldb module.
529 * @param request The request being logged.
530 * @param action Description of the action being performed.
531 * @param user The linearized form of the users DN
532 * @param status the LDB status code for the processing of the request.
535 static void log_membership_change(
536 struct ldb_module *module,
537 const struct ldb_request *request,
542 const char *group = NULL;
543 struct audit_context *ac =
545 ldb_module_get_private(module),
546 struct audit_context);
548 TALLOC_CTX *ctx = talloc_new(NULL);
549 group = dsdb_audit_get_primary_dn(request);
550 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
551 char *message = NULL;
552 message = audit_group_human_readable(
560 audit_log_human_text(
563 DBGC_DSDB_GROUP_AUDIT,
565 TALLOC_FREE(message);
568 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
569 (ac->msg_ctx && ac->send_events)) {
570 struct json_object json;
571 json = audit_group_json(
581 DBGC_DSDB_GROUP_AUDIT_JSON,
583 if (ac->send_events) {
586 DSDB_GROUP_EVENT_NAME,
596 * @brief Log all the changes to a users group membership.
598 * Log details of a change to a users group memberships, except for changes
599 * to their primary group which is handled by log_primary_group_change.
601 * @param module The ldb module.
602 * @param request The request being logged.
603 * @param action Description of the action being performed.
604 * @param user The linearized form of the users DN
605 * @param status the LDB status code for the processing of the request.
608 static void log_membership_changes(
609 struct ldb_module *module,
610 const struct ldb_request *request,
611 struct ldb_message_element *el,
612 struct ldb_message_element *old_el,
615 unsigned int i, old_i, new_i;
616 unsigned int old_num_values;
617 unsigned int max_num_values;
618 unsigned int new_num_values;
619 struct parsed_dn *old_val = NULL;
620 struct parsed_dn *new_val = NULL;
621 struct parsed_dn *new_values = NULL;
622 struct parsed_dn *old_values = NULL;
623 struct ldb_context *ldb = NULL;
625 TALLOC_CTX *ctx = talloc_new(NULL);
627 old_num_values = old_el ? old_el->num_values : 0;
628 new_num_values = el ? el->num_values : 0;
629 max_num_values = old_num_values + new_num_values;
631 if (max_num_values == 0) {
633 * There is nothing to do!
639 old_values = get_parsed_dns(ctx, old_el);
640 new_values = get_parsed_dns(ctx, el);
641 ldb = ldb_module_get_ctx(module);
645 for (i = 0; i < max_num_values; i++) {
646 enum dn_compare_result cmp;
647 if (old_i < old_num_values && new_i < new_num_values) {
649 * Both list have values, so compare the values
651 old_val = &old_values[old_i];
652 new_val = &new_values[new_i];
653 cmp = dn_compare(ctx, ldb, old_val, new_val);
654 } else if (old_i < old_num_values) {
656 * the new list is empty, read the old list
658 old_val = &old_values[old_i];
661 } else if (new_i < new_num_values) {
663 * the old list is empty, read new list
666 new_val = &new_values[new_i];
672 if (cmp == LESS_THAN) {
674 * Have an entry in the original record that is not in
675 * the new record. So it's been deleted
677 const char *user = NULL;
678 if (old_val->dsdb_dn == NULL) {
679 really_parse_trusted_dn(
685 user = ldb_dn_get_linearized(old_val->dsdb_dn->dn);
686 log_membership_change(
693 } else if (cmp == BINARY_EQUAL) {
695 * DN's unchanged at binary level so nothing to do.
699 } else if (cmp == EQUAL) {
701 * DN is unchanged now need to check the flags to
702 * determine if a record has been deleted or undeleted
706 if (old_val->dsdb_dn == NULL) {
707 really_parse_trusted_dn(
713 if (new_val->dsdb_dn == NULL) {
714 really_parse_trusted_dn(
721 dsdb_get_extended_dn_uint32(
722 old_val->dsdb_dn->dn,
725 dsdb_get_extended_dn_uint32(
726 new_val->dsdb_dn->dn,
729 if (new_flags == old_flags) {
731 * No changes to the Repl meta data so can
732 * no need to log the change
738 if (new_flags & DSDB_RMD_FLAG_DELETED) {
740 * DN has been deleted.
742 const char *user = NULL;
743 user = ldb_dn_get_linearized(
744 old_val->dsdb_dn->dn);
745 log_membership_change(
753 * DN has been re-added
755 const char *user = NULL;
756 user = ldb_dn_get_linearized(
757 new_val->dsdb_dn->dn);
758 log_membership_change(
769 * Member in the updated record that's not in the
770 * original, so it must have been added.
772 const char *user = NULL;
773 if ( new_val->dsdb_dn == NULL) {
774 really_parse_trusted_dn(
780 user = ldb_dn_get_linearized(new_val->dsdb_dn->dn);
781 log_membership_change(
796 * @brief Log the details of a primary group change.
798 * Retrieve the users primary groupo after the operation has completed
799 * and call log_primary_group_change to log the actual changes.
801 * @param acc details of the primary group before the operation.
802 * @param status The status code returned by the operation.
804 * @return an LDB status code.
806 static void log_user_primary_group_change(
807 struct audit_callback_context *acc,
810 TALLOC_CTX *ctx = talloc_new(NULL);
812 struct dom_sid *account_sid = NULL;
814 const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
815 if (status == LDB_SUCCESS && msg != NULL) {
816 struct ldb_result *res = NULL;
817 ret = dsdb_module_search_dn(
823 DSDB_FLAG_NEXT_MODULE |
824 DSDB_SEARCH_REVEAL_INTERNALS |
825 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
827 if (ret == LDB_SUCCESS) {
828 new_rid = ldb_msg_find_attr_as_uint(
832 account_sid = samdb_result_dom_sid(
839 * If we don't have a new value then the user has been deleted
840 * which we currently do not log.
841 * Otherwise only log if the primary group has actually changed.
843 if (account_sid != NULL &&
845 acc->primary_group != new_rid) {
846 const char* group = get_primary_group_dn(
851 log_primary_group_change(
862 * @brief log the changes to users group membership.
864 * Retrieve the users group memberships after the operation has completed
865 * and call log_membership_changes to log the actual changes.
867 * @param acc details of the group memberships before the operation.
868 * @param status The status code returned by the operation.
870 * @return an LDB status code.
872 static void log_group_membership_changes(
873 struct audit_callback_context *acc,
876 TALLOC_CTX *ctx = talloc_new(NULL);
877 struct ldb_message_element *new_val = NULL;
879 const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
880 if (status == LDB_SUCCESS && msg != NULL) {
881 struct ldb_result *res = NULL;
882 ret = dsdb_module_search_dn(
888 DSDB_FLAG_NEXT_MODULE |
889 DSDB_SEARCH_REVEAL_INTERNALS |
890 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
892 if (ret == LDB_SUCCESS) {
893 new_val = ldb_msg_find_element(res->msgs[0], "member");
896 log_membership_changes(
906 * @brief call back function to log changes to the group memberships.
908 * Call back function to log changes to the uses broup memberships.
910 * @param req the ldb request.
911 * @param ares the ldb result
913 * @return am LDB status code.
915 static int group_audit_callback(
916 struct ldb_request *req,
917 struct ldb_reply *ares)
919 struct audit_callback_context *ac = NULL;
921 ac = talloc_get_type(
923 struct audit_callback_context);
926 return ldb_module_done(
927 ac->request, NULL, NULL,
928 LDB_ERR_OPERATIONS_ERROR);
931 /* pass on to the callback */
932 switch (ares->type) {
933 case LDB_REPLY_ENTRY:
934 return ldb_module_send_entry(
939 case LDB_REPLY_REFERRAL:
940 return ldb_module_send_referral(
946 * Log on DONE now we have a result code
948 ac->log_changes(ac, ares->error);
949 return ldb_module_done(
958 return LDB_ERR_OPERATIONS_ERROR;
963 * @brief Does this request change the primary group.
965 * Does the request change the primary group, i.e. does it contain the
966 * primaryGroupID attribute.
968 * @param req the request to examine.
970 * @return True if the request modifies the primary group.
972 static bool has_primary_group_id(struct ldb_request *req)
974 struct ldb_message_element *el = NULL;
975 const struct ldb_message *msg = NULL;
977 msg = dsdb_audit_get_message(req);
978 el = ldb_msg_find_element(msg, "primaryGroupID");
984 * @brief Does this request change group membership.
986 * Does the request change the ses group memberships, i.e. does it contain the
989 * @param req the request to examine.
991 * @return True if the request modifies the users group memberships.
993 static bool has_group_membership_changes(struct ldb_request *req)
995 struct ldb_message_element *el = NULL;
996 const struct ldb_message *msg = NULL;
998 msg = dsdb_audit_get_message(req);
999 el = ldb_msg_find_element(msg, "member");
1001 return (el != NULL);
1007 * @brief Install the callback function to log an add request.
1009 * Install the callback function to log an add request changing the users
1010 * group memberships. As we want to log the returned status code, we need to
1011 * register a callback function that will be called once the operation has
1014 * This function reads the current user record so that we can log the before
1017 * @param module The ldb module.
1018 * @param req The modify request.
1020 * @return and LDB status code.
1022 static int set_group_membership_add_callback(
1023 struct ldb_module *module,
1024 struct ldb_request *req)
1026 struct audit_callback_context *context = NULL;
1027 struct ldb_request *new_req = NULL;
1028 struct ldb_context *ldb = NULL;
1031 * Adding group memberships so will need to log the changes.
1033 ldb = ldb_module_get_ctx(module);
1034 context = talloc_zero(req, struct audit_callback_context);
1036 if (context == NULL) {
1037 return ldb_oom(ldb);
1039 context->request = req;
1040 context->module = module;
1041 context->log_changes = log_group_membership_changes;
1043 * We want to log the return code status, so we need to register
1044 * a callback function to get the actual result.
1045 * We need to take a new copy so that we don't alter the callers copy
1047 ret = ldb_build_add_req(
1051 req->op.add.message,
1054 group_audit_callback,
1056 if (ret != LDB_SUCCESS) {
1059 return ldb_next_request(module, new_req);
1064 * @brief Install the callback function to log a modify request.
1066 * Install the callback function to log a modify request changing the primary
1067 * group . As we want to log the returned status code, we need to register a
1068 * callback function that will be called once the operation has completed.
1070 * This function reads the current user record so that we can log the before
1073 * @param module The ldb module.
1074 * @param req The modify request.
1076 * @return and LDB status code.
1078 static int set_primary_group_modify_callback(
1079 struct ldb_module *module,
1080 struct ldb_request *req)
1082 struct audit_callback_context *context = NULL;
1083 struct ldb_request *new_req = NULL;
1084 struct ldb_context *ldb = NULL;
1085 const struct ldb_message *msg = NULL;
1086 struct ldb_result *res = NULL;
1089 TALLOC_CTX *ctx = talloc_new(NULL);
1091 ldb = ldb_module_get_ctx(module);
1093 context = talloc_zero(req, struct audit_callback_context);
1094 if (context == NULL) {
1098 context->request = req;
1099 context->module = module;
1100 context->log_changes = log_user_primary_group_change;
1102 msg = dsdb_audit_get_message(req);
1103 ret = dsdb_module_search_dn(
1109 DSDB_FLAG_NEXT_MODULE |
1110 DSDB_SEARCH_REVEAL_INTERNALS |
1111 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1113 if (ret == LDB_SUCCESS) {
1115 pg = ldb_msg_find_attr_as_uint(
1119 context->primary_group = pg;
1122 * We want to log the return code status, so we need to register
1123 * a callback function to get the actual result.
1124 * We need to take a new copy so that we don't alter the callers copy
1126 ret = ldb_build_mod_req(
1130 req->op.add.message,
1133 group_audit_callback,
1135 if (ret != LDB_SUCCESS) {
1138 ret = ldb_next_request(module, new_req);
1145 * @brief Install the callback function to log an add request.
1147 * Install the callback function to log an add request changing the primary
1148 * group . As we want to log the returned status code, we need to register a
1149 * callback function that will be called once the operation has completed.
1151 * This function reads the current user record so that we can log the before
1154 * @param module The ldb module.
1155 * @param req The modify request.
1157 * @return and LDB status code.
1159 static int set_primary_group_add_callback(
1160 struct ldb_module *module,
1161 struct ldb_request *req)
1163 struct audit_callback_context *context = NULL;
1164 struct ldb_request *new_req = NULL;
1165 struct ldb_context *ldb = NULL;
1168 * Adding a user with a primary group.
1170 ldb = ldb_module_get_ctx(module);
1171 context = talloc_zero(req, struct audit_callback_context);
1173 if (context == NULL) {
1174 return ldb_oom(ldb);
1176 context->request = req;
1177 context->module = module;
1178 context->log_changes = log_user_primary_group_change;
1180 * We want to log the return code status, so we need to register
1181 * a callback function to get the actual result.
1182 * We need to take a new copy so that we don't alter the callers copy
1184 ret = ldb_build_add_req(
1188 req->op.add.message,
1191 group_audit_callback,
1193 if (ret != LDB_SUCCESS) {
1196 return ldb_next_request(module, new_req);
1200 * @brief Module handler for add operations.
1202 * Inspect the current add request, and if needed log any group membership
1205 * @param module The ldb module.
1206 * @param req The modify request.
1208 * @return and LDB status code.
1210 static int group_add(
1211 struct ldb_module *module,
1212 struct ldb_request *req)
1215 struct audit_context *ac =
1217 ldb_module_get_private(module),
1218 struct audit_context);
1220 * Currently we don't log replicated group changes
1222 if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1223 return ldb_next_request(module, req);
1226 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL) ||
1227 CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
1228 (ac->msg_ctx && ac->send_events)) {
1230 * Avoid the overheads of logging unless it has been
1233 if (has_group_membership_changes(req)) {
1234 return set_group_membership_add_callback(module, req);
1236 if (has_primary_group_id(req)) {
1237 return set_primary_group_add_callback(module, req);
1240 return ldb_next_request(module, req);
1244 * @brief Module handler for delete operations.
1246 * Currently there is no logging for delete operations.
1248 * @param module The ldb module.
1249 * @param req The modify request.
1251 * @return and LDB status code.
1253 static int group_delete(
1254 struct ldb_module *module,
1255 struct ldb_request *req)
1257 return ldb_next_request(module, req);
1261 * @brief Install the callback function to log a modify request.
1263 * Install the callback function to log a modify request. As we want to log the
1264 * returned status code, we need to register a callback function that will be
1265 * called once the operation has completed.
1267 * This function reads the current user record so that we can log the before
1270 * @param module The ldb module.
1271 * @param req The modify request.
1273 * @return and LDB status code.
1275 static int set_group_modify_callback(
1276 struct ldb_module *module,
1277 struct ldb_request *req)
1279 struct audit_callback_context *context = NULL;
1280 struct ldb_request *new_req = NULL;
1281 struct ldb_context *ldb = NULL;
1282 struct ldb_result *res = NULL;
1285 ldb = ldb_module_get_ctx(module);
1286 context = talloc_zero(req, struct audit_callback_context);
1288 if (context == NULL) {
1289 return ldb_oom(ldb);
1291 context->request = req;
1292 context->module = module;
1293 context->log_changes = log_group_membership_changes;
1296 * About to change the group memberships need to read
1297 * the current state from the database.
1299 ret = dsdb_module_search_dn(
1303 req->op.add.message->dn,
1305 DSDB_FLAG_NEXT_MODULE |
1306 DSDB_SEARCH_REVEAL_INTERNALS |
1307 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1309 if (ret == LDB_SUCCESS) {
1310 context->members = ldb_msg_find_element(res->msgs[0], "member");
1313 ret = ldb_build_mod_req(
1317 req->op.mod.message,
1320 group_audit_callback,
1322 if (ret != LDB_SUCCESS) {
1325 return ldb_next_request(module, new_req);
1329 * @brief Module handler for modify operations.
1331 * Inspect the current modify request, and if needed log any group membership
1334 * @param module The ldb module.
1335 * @param req The modify request.
1337 * @return and LDB status code.
1339 static int group_modify(
1340 struct ldb_module *module,
1341 struct ldb_request *req)
1344 struct audit_context *ac =
1346 ldb_module_get_private(module),
1347 struct audit_context);
1349 * Currently we don't log replicated group changes
1351 if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1352 return ldb_next_request(module, req);
1355 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL) ||
1356 CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
1357 (ac->msg_ctx && ac->send_events)) {
1359 * Avoid the overheads of logging unless it has been
1362 if (has_group_membership_changes(req)) {
1363 return set_group_modify_callback(module, req);
1365 if (has_primary_group_id(req)) {
1366 return set_primary_group_modify_callback(module, req);
1369 return ldb_next_request(module, req);
1373 * @brief ldb module initialisation
1375 * Initialise the module, loading the private data etc.
1377 * @param module The ldb module to initialise.
1379 * @return An LDB status code.
1381 static int group_init(struct ldb_module *module)
1384 struct ldb_context *ldb = ldb_module_get_ctx(module);
1385 struct audit_context *context = NULL;
1386 struct loadparm_context *lp_ctx
1387 = talloc_get_type_abort(
1388 ldb_get_opaque(ldb, "loadparm"),
1389 struct loadparm_context);
1390 struct tevent_context *ev = ldb_get_event_context(ldb);
1392 context = talloc_zero(module, struct audit_context);
1393 if (context == NULL) {
1394 return ldb_module_oom(module);
1397 if (lp_ctx && lpcfg_dsdb_group_change_notification(lp_ctx)) {
1398 context->send_events = true;
1399 context->msg_ctx = imessaging_client_init(context,
1404 ldb_module_set_private(module, context);
1405 return ldb_next_init(module);
1408 static const struct ldb_module_ops ldb_group_audit_log_module_ops = {
1409 .name = "group_audit_log",
1411 .modify = group_modify,
1412 .del = group_delete,
1413 .init_context = group_init,
1416 int ldb_group_audit_log_module_init(const char *version)
1418 LDB_MODULE_CHECK_VERSION(version);
1419 return ldb_register_module(&ldb_group_audit_log_module_ops);