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;
95 * @brief generate a JSON log entry for a group change.
97 * Generate a JSON object containing details of a users group change.
99 * @param module the ldb module
100 * @param request the ldb_request
101 * @param action the change action being performed
102 * @param user the user name
103 * @param group the group name
104 * @param status the ldb status code for the ldb operation.
106 * @return A json object containing the details.
107 * NULL if an error was detected
109 static struct json_object audit_group_json(
110 const struct ldb_module *module,
111 const struct ldb_request *request,
117 struct ldb_context *ldb = NULL;
118 const struct dom_sid *sid = NULL;
119 struct json_object wrapper = json_empty_object;
120 struct json_object audit = json_empty_object;
121 const struct tsocket_address *remote = NULL;
122 const struct GUID *unique_session_token = NULL;
123 struct GUID *transaction_id = NULL;
126 ldb = ldb_module_get_ctx(discard_const(module));
128 remote = dsdb_audit_get_remote_address(ldb);
129 sid = dsdb_audit_get_user_sid(module);
130 unique_session_token = dsdb_audit_get_unique_session_token(module);
131 transaction_id = get_transaction_id(request);
133 audit = json_new_object();
134 if (json_is_invalid(&audit)) {
137 rc = json_add_version(&audit, AUDIT_MAJOR, AUDIT_MINOR);
141 rc = json_add_int(&audit, "statusCode", status);
145 rc = json_add_string(&audit, "status", ldb_strerror(status));
149 rc = json_add_string(&audit, "action", action);
153 rc = json_add_address(&audit, "remoteAddress", remote);
157 rc = json_add_sid(&audit, "userSid", sid);
161 rc = json_add_string(&audit, "group", group);
165 rc = json_add_guid(&audit, "transactionId", transaction_id);
169 rc = json_add_guid(&audit, "sessionId", unique_session_token);
173 rc = json_add_string(&audit, "user", user);
178 wrapper = json_new_object();
179 if (json_is_invalid(&wrapper)) {
182 rc = json_add_timestamp(&wrapper);
186 rc = json_add_string(&wrapper, "type", AUDIT_JSON_TYPE);
190 rc = json_add_object(&wrapper, AUDIT_JSON_TYPE, &audit);
198 * On a failure audit will not have been added to wrapper so it
199 * needs to free it to avoid a leak.
201 * wrapper is freed to invalidate it as it will have only been
202 * partially constructed and may be inconsistent.
204 * All the json manipulation routines handle a freed object correctly
208 DBG_ERR("Failed to create group change JSON log message\n");
214 * @brief generate a human readable log entry for a group change.
216 * Generate a human readable log entry containing details of a users group
219 * @param ctx the talloc context owning the returned log entry
220 * @param module the ldb module
221 * @param request the ldb_request
222 * @param action the change action being performed
223 * @param user the user name
224 * @param group the group name
225 * @param status the ldb status code for the ldb operation.
227 * @return A human readable log line.
229 static char *audit_group_human_readable(
231 const struct ldb_module *module,
232 const struct ldb_request *request,
238 struct ldb_context *ldb = NULL;
239 const char *remote_host = NULL;
240 const struct dom_sid *sid = NULL;
241 const char *user_sid = NULL;
242 const char *timestamp = NULL;
243 char *log_entry = NULL;
245 TALLOC_CTX *ctx = talloc_new(NULL);
247 ldb = ldb_module_get_ctx(discard_const(module));
249 remote_host = dsdb_audit_get_remote_host(ldb, ctx);
250 sid = dsdb_audit_get_user_sid(module);
251 user_sid = dom_sid_string(ctx, sid);
252 timestamp = audit_get_timestamp(ctx);
254 log_entry = talloc_asprintf(
256 "[%s] at [%s] status [%s] "
257 "Remote host [%s] SID [%s] Group [%s] User [%s]",
260 ldb_strerror(status),
270 * @brief generate an array of parsed_dns, deferring the actual parsing.
272 * Get an array of 'struct parsed_dns' without the parsing.
273 * The parsed_dns are parsed only when needed to avoid the expense of parsing.
275 * This procedure assumes that the dn's are sorted in GUID order and contains
276 * no duplicates. This should be valid as the module sits below repl_meta_data
277 * which ensures this.
279 * @param mem_ctx The memory context that will own the generated array
280 * @param el The message element used to generate the array.
282 * @return an array of struct parsed_dns, or NULL in the event of an error
284 static struct parsed_dn *get_parsed_dns(
286 struct ldb_message_element *el)
288 struct parsed_dn *pdn = NULL;
292 if (el == NULL || el->num_values == 0) {
296 pdn = talloc_zero_array(mem_ctx, struct parsed_dn, el->num_values);
298 DBG_ERR("Out of memory\n");
302 for (i = 0; i < el->num_values; i++) {
303 pdn[i].v = &el->values[i];
309 enum dn_compare_result {
316 * @brief compare parsed_dns
318 * Compare two parsed_dn structures, parsing the entries if necessary.
319 * To avoid the overhead of parsing the DN's this function does a binary
320 * compare first. Only parsing the DN's they are not equal at a binary level.
322 * @param ctx talloc context that will own the parsed dsdb_dn
323 * @param ldb ldb_context
324 * @param old_val The old value
325 * @param new_val The old value
327 * @return BINARY_EQUAL values are equal at a binary level
328 * EQUAL DN's are equal but the meta data is different
329 * LESS_THAN old value < new value
330 * GREATER_THAN old value > new value
333 static enum dn_compare_result dn_compare(
335 struct ldb_context *ldb,
336 struct parsed_dn *old_val,
337 struct parsed_dn *new_val) {
342 * Do a binary compare first to avoid unnecessary parsing
344 if (data_blob_cmp(new_val->v, old_val->v) == 0) {
346 * Values are equal at a binary level so no need
347 * for further processing
352 * Values not equal at the binary level, so lets
353 * do a GUID ordering compare. To do this we will need to ensure
354 * that the dn's have been parsed.
356 if (old_val->dsdb_dn == NULL) {
357 really_parse_trusted_dn(
363 if (new_val->dsdb_dn == NULL) {
364 really_parse_trusted_dn(
371 res = ndr_guid_compare(&new_val->guid, &old_val->guid);
374 } else if (res == 0) {
382 * @brief Get the DN of a users primary group as a printable string.
384 * Get the DN of a users primary group as a printable string.
386 * @param mem_ctx Talloc context the the returned string will be allocated on.
387 * @param module The ldb module
388 * @param account_sid The SID for the uses account.
389 * @param primary_group_rid The RID for the users primary group.
391 * @return a formatted DN, or null if there is an error.
393 static const char *get_primary_group_dn(
395 struct ldb_module *module,
396 struct dom_sid *account_sid,
397 uint32_t primary_group_rid)
401 struct ldb_context *ldb = NULL;
402 struct dom_sid *domain_sid = NULL;
403 struct dom_sid *primary_group_sid = NULL;
405 struct ldb_dn *dn = NULL;
406 struct ldb_message *msg = NULL;
409 ldb = ldb_module_get_ctx(module);
411 status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
412 if (!NT_STATUS_IS_OK(status)) {
416 primary_group_sid = dom_sid_add_rid(
420 if (!primary_group_sid) {
424 sid = dom_sid_string(mem_ctx, primary_group_sid);
429 dn = ldb_dn_new_fmt(mem_ctx, ldb, "<SID=%s>", sid);
433 rc = dsdb_search_one(
442 if (rc != LDB_SUCCESS) {
446 return ldb_dn_get_linearized(msg->dn);
450 * @brief Log details of a change to a users primary group.
452 * Log details of a change to a users primary group.
454 * @param module The ldb module.
455 * @param request The request deing logged.
456 * @param action Description of the action being performed.
457 * @param group The linearized for of the group DN
458 * @param status the LDB status code for the processing of the request.
461 static void log_primary_group_change(
462 struct ldb_module *module,
463 const struct ldb_request *request,
468 const char *user = NULL;
470 struct audit_context *ac =
472 ldb_module_get_private(module),
473 struct audit_context);
475 TALLOC_CTX *ctx = talloc_new(NULL);
477 user = dsdb_audit_get_primary_dn(request);
478 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
479 char *message = NULL;
480 message = audit_group_human_readable(
488 audit_log_human_text(
491 DBGC_DSDB_GROUP_AUDIT,
493 TALLOC_FREE(message);
497 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
498 (ac->msg_ctx && ac->send_events)) {
500 struct json_object json;
501 json = audit_group_json(
511 DBGC_DSDB_GROUP_AUDIT_JSON,
513 if (ac->send_events) {
516 DSDB_GROUP_EVENT_NAME,
527 * @brief Log details of a single change to a users group membership.
529 * Log details of a change to a users group membership, except for changes
530 * to their primary group which is handled by log_primary_group_change.
532 * @param module The ldb module.
533 * @param request The request being logged.
534 * @param action Description of the action being performed.
535 * @param user The linearized form of the users DN
536 * @param status the LDB status code for the processing of the request.
539 static void log_membership_change(
540 struct ldb_module *module,
541 const struct ldb_request *request,
546 const char *group = NULL;
547 struct audit_context *ac =
549 ldb_module_get_private(module),
550 struct audit_context);
552 TALLOC_CTX *ctx = talloc_new(NULL);
553 group = dsdb_audit_get_primary_dn(request);
554 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
555 char *message = NULL;
556 message = audit_group_human_readable(
564 audit_log_human_text(
567 DBGC_DSDB_GROUP_AUDIT,
569 TALLOC_FREE(message);
573 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
574 (ac->msg_ctx && ac->send_events)) {
575 struct json_object json;
576 json = audit_group_json(
586 DBGC_DSDB_GROUP_AUDIT_JSON,
588 if (ac->send_events) {
591 DSDB_GROUP_EVENT_NAME,
602 * @brief Log all the changes to a users group membership.
604 * Log details of a change to a users group memberships, except for changes
605 * to their primary group which is handled by log_primary_group_change.
607 * @param module The ldb module.
608 * @param request The request being logged.
609 * @param action Description of the action being performed.
610 * @param user The linearized form of the users DN
611 * @param status the LDB status code for the processing of the request.
614 static void log_membership_changes(
615 struct ldb_module *module,
616 const struct ldb_request *request,
617 struct ldb_message_element *el,
618 struct ldb_message_element *old_el,
621 unsigned int i, old_i, new_i;
622 unsigned int old_num_values;
623 unsigned int max_num_values;
624 unsigned int new_num_values;
625 struct parsed_dn *old_val = NULL;
626 struct parsed_dn *new_val = NULL;
627 struct parsed_dn *new_values = NULL;
628 struct parsed_dn *old_values = NULL;
629 struct ldb_context *ldb = NULL;
631 TALLOC_CTX *ctx = talloc_new(NULL);
633 old_num_values = old_el ? old_el->num_values : 0;
634 new_num_values = el ? el->num_values : 0;
635 max_num_values = old_num_values + new_num_values;
637 if (max_num_values == 0) {
639 * There is nothing to do!
645 old_values = get_parsed_dns(ctx, old_el);
646 new_values = get_parsed_dns(ctx, el);
647 ldb = ldb_module_get_ctx(module);
651 for (i = 0; i < max_num_values; i++) {
652 enum dn_compare_result cmp;
653 if (old_i < old_num_values && new_i < new_num_values) {
655 * Both list have values, so compare the values
657 old_val = &old_values[old_i];
658 new_val = &new_values[new_i];
659 cmp = dn_compare(ctx, ldb, old_val, new_val);
660 } else if (old_i < old_num_values) {
662 * the new list is empty, read the old list
664 old_val = &old_values[old_i];
667 } else if (new_i < new_num_values) {
669 * the old list is empty, read new list
672 new_val = &new_values[new_i];
678 if (cmp == LESS_THAN) {
680 * Have an entry in the original record that is not in
681 * the new record. So it's been deleted
683 const char *user = NULL;
684 if (old_val->dsdb_dn == NULL) {
685 really_parse_trusted_dn(
691 user = ldb_dn_get_linearized(old_val->dsdb_dn->dn);
692 log_membership_change(
699 } else if (cmp == BINARY_EQUAL) {
701 * DN's unchanged at binary level so nothing to do.
705 } else if (cmp == EQUAL) {
707 * DN is unchanged now need to check the flags to
708 * determine if a record has been deleted or undeleted
712 if (old_val->dsdb_dn == NULL) {
713 really_parse_trusted_dn(
719 if (new_val->dsdb_dn == NULL) {
720 really_parse_trusted_dn(
727 dsdb_get_extended_dn_uint32(
728 old_val->dsdb_dn->dn,
731 dsdb_get_extended_dn_uint32(
732 new_val->dsdb_dn->dn,
735 if (new_flags == old_flags) {
737 * No changes to the Repl meta data so can
738 * no need to log the change
744 if (new_flags & DSDB_RMD_FLAG_DELETED) {
746 * DN has been deleted.
748 const char *user = NULL;
749 user = ldb_dn_get_linearized(
750 old_val->dsdb_dn->dn);
751 log_membership_change(
759 * DN has been re-added
761 const char *user = NULL;
762 user = ldb_dn_get_linearized(
763 new_val->dsdb_dn->dn);
764 log_membership_change(
775 * Member in the updated record that's not in the
776 * original, so it must have been added.
778 const char *user = NULL;
779 if ( new_val->dsdb_dn == NULL) {
780 really_parse_trusted_dn(
786 user = ldb_dn_get_linearized(new_val->dsdb_dn->dn);
787 log_membership_change(
802 * @brief Log the details of a primary group change.
804 * Retrieve the users primary groupo after the operation has completed
805 * and call log_primary_group_change to log the actual changes.
807 * @param acc details of the primary group before the operation.
808 * @param status The status code returned by the operation.
810 * @return an LDB status code.
812 static void log_user_primary_group_change(
813 struct audit_callback_context *acc,
816 TALLOC_CTX *ctx = talloc_new(NULL);
818 struct dom_sid *account_sid = NULL;
820 const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
821 if (status == LDB_SUCCESS && msg != NULL) {
822 struct ldb_result *res = NULL;
823 ret = dsdb_module_search_dn(
829 DSDB_FLAG_NEXT_MODULE |
830 DSDB_SEARCH_REVEAL_INTERNALS |
831 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
833 if (ret == LDB_SUCCESS) {
834 new_rid = ldb_msg_find_attr_as_uint(
838 account_sid = samdb_result_dom_sid(
845 * If we don't have a new value then the user has been deleted
846 * which we currently do not log.
847 * Otherwise only log if the primary group has actually changed.
849 if (account_sid != NULL &&
851 acc->primary_group != new_rid) {
852 const char* group = get_primary_group_dn(
857 log_primary_group_change(
868 * @brief log the changes to users group membership.
870 * Retrieve the users group memberships after the operation has completed
871 * and call log_membership_changes to log the actual changes.
873 * @param acc details of the group memberships before the operation.
874 * @param status The status code returned by the operation.
876 * @return an LDB status code.
878 static void log_group_membership_changes(
879 struct audit_callback_context *acc,
882 TALLOC_CTX *ctx = talloc_new(NULL);
883 struct ldb_message_element *new_val = NULL;
885 const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
886 if (status == LDB_SUCCESS && msg != NULL) {
887 struct ldb_result *res = NULL;
888 ret = dsdb_module_search_dn(
894 DSDB_FLAG_NEXT_MODULE |
895 DSDB_SEARCH_REVEAL_INTERNALS |
896 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
898 if (ret == LDB_SUCCESS) {
899 new_val = ldb_msg_find_element(res->msgs[0], "member");
902 log_membership_changes(
912 * @brief call back function to log changes to the group memberships.
914 * Call back function to log changes to the uses broup memberships.
916 * @param req the ldb request.
917 * @param ares the ldb result
919 * @return am LDB status code.
921 static int group_audit_callback(
922 struct ldb_request *req,
923 struct ldb_reply *ares)
925 struct audit_callback_context *ac = NULL;
927 ac = talloc_get_type(
929 struct audit_callback_context);
932 return ldb_module_done(
933 ac->request, NULL, NULL,
934 LDB_ERR_OPERATIONS_ERROR);
937 /* pass on to the callback */
938 switch (ares->type) {
939 case LDB_REPLY_ENTRY:
940 return ldb_module_send_entry(
945 case LDB_REPLY_REFERRAL:
946 return ldb_module_send_referral(
952 * Log on DONE now we have a result code
954 ac->log_changes(ac, ares->error);
955 return ldb_module_done(
964 return LDB_ERR_OPERATIONS_ERROR;
969 * @brief Does this request change the primary group.
971 * Does the request change the primary group, i.e. does it contain the
972 * primaryGroupID attribute.
974 * @param req the request to examine.
976 * @return True if the request modifies the primary group.
978 static bool has_primary_group_id(struct ldb_request *req)
980 struct ldb_message_element *el = NULL;
981 const struct ldb_message *msg = NULL;
983 msg = dsdb_audit_get_message(req);
984 el = ldb_msg_find_element(msg, "primaryGroupID");
990 * @brief Does this request change group membership.
992 * Does the request change the ses group memberships, i.e. does it contain the
995 * @param req the request to examine.
997 * @return True if the request modifies the users group memberships.
999 static bool has_group_membership_changes(struct ldb_request *req)
1001 struct ldb_message_element *el = NULL;
1002 const struct ldb_message *msg = NULL;
1004 msg = dsdb_audit_get_message(req);
1005 el = ldb_msg_find_element(msg, "member");
1007 return (el != NULL);
1013 * @brief Install the callback function to log an add request.
1015 * Install the callback function to log an add request changing the users
1016 * group memberships. As we want to log the returned status code, we need to
1017 * register a callback function that will be called once the operation has
1020 * This function reads the current user record so that we can log the before
1023 * @param module The ldb module.
1024 * @param req The modify request.
1026 * @return and LDB status code.
1028 static int set_group_membership_add_callback(
1029 struct ldb_module *module,
1030 struct ldb_request *req)
1032 struct audit_callback_context *context = NULL;
1033 struct ldb_request *new_req = NULL;
1034 struct ldb_context *ldb = NULL;
1037 * Adding group memberships so will need to log the changes.
1039 ldb = ldb_module_get_ctx(module);
1040 context = talloc_zero(req, struct audit_callback_context);
1042 if (context == NULL) {
1043 return ldb_oom(ldb);
1045 context->request = req;
1046 context->module = module;
1047 context->log_changes = log_group_membership_changes;
1049 * We want to log the return code status, so we need to register
1050 * a callback function to get the actual result.
1051 * We need to take a new copy so that we don't alter the callers copy
1053 ret = ldb_build_add_req(
1057 req->op.add.message,
1060 group_audit_callback,
1062 if (ret != LDB_SUCCESS) {
1065 return ldb_next_request(module, new_req);
1070 * @brief Install the callback function to log a modify request.
1072 * Install the callback function to log a modify request changing the primary
1073 * group . As we want to log the returned status code, we need to register a
1074 * callback function that will be called once the operation has completed.
1076 * This function reads the current user record so that we can log the before
1079 * @param module The ldb module.
1080 * @param req The modify request.
1082 * @return and LDB status code.
1084 static int set_primary_group_modify_callback(
1085 struct ldb_module *module,
1086 struct ldb_request *req)
1088 struct audit_callback_context *context = NULL;
1089 struct ldb_request *new_req = NULL;
1090 struct ldb_context *ldb = NULL;
1091 const struct ldb_message *msg = NULL;
1092 struct ldb_result *res = NULL;
1095 TALLOC_CTX *ctx = talloc_new(NULL);
1097 ldb = ldb_module_get_ctx(module);
1099 context = talloc_zero(req, struct audit_callback_context);
1100 if (context == NULL) {
1104 context->request = req;
1105 context->module = module;
1106 context->log_changes = log_user_primary_group_change;
1108 msg = dsdb_audit_get_message(req);
1109 ret = dsdb_module_search_dn(
1115 DSDB_FLAG_NEXT_MODULE |
1116 DSDB_SEARCH_REVEAL_INTERNALS |
1117 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1119 if (ret == LDB_SUCCESS) {
1121 pg = ldb_msg_find_attr_as_uint(
1125 context->primary_group = pg;
1128 * We want to log the return code status, so we need to register
1129 * a callback function to get the actual result.
1130 * We need to take a new copy so that we don't alter the callers copy
1132 ret = ldb_build_mod_req(
1136 req->op.add.message,
1139 group_audit_callback,
1141 if (ret != LDB_SUCCESS) {
1144 ret = ldb_next_request(module, new_req);
1151 * @brief Install the callback function to log an add request.
1153 * Install the callback function to log an add request changing the primary
1154 * group . As we want to log the returned status code, we need to register a
1155 * callback function that will be called once the operation has completed.
1157 * This function reads the current user record so that we can log the before
1160 * @param module The ldb module.
1161 * @param req The modify request.
1163 * @return and LDB status code.
1165 static int set_primary_group_add_callback(
1166 struct ldb_module *module,
1167 struct ldb_request *req)
1169 struct audit_callback_context *context = NULL;
1170 struct ldb_request *new_req = NULL;
1171 struct ldb_context *ldb = NULL;
1174 * Adding a user with a primary group.
1176 ldb = ldb_module_get_ctx(module);
1177 context = talloc_zero(req, struct audit_callback_context);
1179 if (context == NULL) {
1180 return ldb_oom(ldb);
1182 context->request = req;
1183 context->module = module;
1184 context->log_changes = log_user_primary_group_change;
1186 * We want to log the return code status, so we need to register
1187 * a callback function to get the actual result.
1188 * We need to take a new copy so that we don't alter the callers copy
1190 ret = ldb_build_add_req(
1194 req->op.add.message,
1197 group_audit_callback,
1199 if (ret != LDB_SUCCESS) {
1202 return ldb_next_request(module, new_req);
1206 * @brief Module handler for add operations.
1208 * Inspect the current add request, and if needed log any group membership
1211 * @param module The ldb module.
1212 * @param req The modify request.
1214 * @return and LDB status code.
1216 static int group_add(
1217 struct ldb_module *module,
1218 struct ldb_request *req)
1221 struct audit_context *ac =
1223 ldb_module_get_private(module),
1224 struct audit_context);
1226 * Currently we don't log replicated group changes
1228 if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1229 return ldb_next_request(module, req);
1232 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL) ||
1233 CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
1234 (ac->msg_ctx && ac->send_events)) {
1236 * Avoid the overheads of logging unless it has been
1239 if (has_group_membership_changes(req)) {
1240 return set_group_membership_add_callback(module, req);
1242 if (has_primary_group_id(req)) {
1243 return set_primary_group_add_callback(module, req);
1246 return ldb_next_request(module, req);
1250 * @brief Module handler for delete operations.
1252 * Currently there is no logging for delete operations.
1254 * @param module The ldb module.
1255 * @param req The modify request.
1257 * @return and LDB status code.
1259 static int group_delete(
1260 struct ldb_module *module,
1261 struct ldb_request *req)
1263 return ldb_next_request(module, req);
1267 * @brief Install the callback function to log a modify request.
1269 * Install the callback function to log a modify request. As we want to log the
1270 * returned status code, we need to register a callback function that will be
1271 * called once the operation has completed.
1273 * This function reads the current user record so that we can log the before
1276 * @param module The ldb module.
1277 * @param req The modify request.
1279 * @return and LDB status code.
1281 static int set_group_modify_callback(
1282 struct ldb_module *module,
1283 struct ldb_request *req)
1285 struct audit_callback_context *context = NULL;
1286 struct ldb_request *new_req = NULL;
1287 struct ldb_context *ldb = NULL;
1288 struct ldb_result *res = NULL;
1291 ldb = ldb_module_get_ctx(module);
1292 context = talloc_zero(req, struct audit_callback_context);
1294 if (context == NULL) {
1295 return ldb_oom(ldb);
1297 context->request = req;
1298 context->module = module;
1299 context->log_changes = log_group_membership_changes;
1302 * About to change the group memberships need to read
1303 * the current state from the database.
1305 ret = dsdb_module_search_dn(
1309 req->op.add.message->dn,
1311 DSDB_FLAG_NEXT_MODULE |
1312 DSDB_SEARCH_REVEAL_INTERNALS |
1313 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1315 if (ret == LDB_SUCCESS) {
1316 context->members = ldb_msg_find_element(res->msgs[0], "member");
1319 ret = ldb_build_mod_req(
1323 req->op.mod.message,
1326 group_audit_callback,
1328 if (ret != LDB_SUCCESS) {
1331 return ldb_next_request(module, new_req);
1335 * @brief Module handler for modify operations.
1337 * Inspect the current modify request, and if needed log any group membership
1340 * @param module The ldb module.
1341 * @param req The modify request.
1343 * @return and LDB status code.
1345 static int group_modify(
1346 struct ldb_module *module,
1347 struct ldb_request *req)
1350 struct audit_context *ac =
1352 ldb_module_get_private(module),
1353 struct audit_context);
1355 * Currently we don't log replicated group changes
1357 if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1358 return ldb_next_request(module, req);
1361 if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL) ||
1362 CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
1363 (ac->msg_ctx && ac->send_events)) {
1365 * Avoid the overheads of logging unless it has been
1368 if (has_group_membership_changes(req)) {
1369 return set_group_modify_callback(module, req);
1371 if (has_primary_group_id(req)) {
1372 return set_primary_group_modify_callback(module, req);
1375 return ldb_next_request(module, req);
1379 * @brief ldb module initialisation
1381 * Initialise the module, loading the private data etc.
1383 * @param module The ldb module to initialise.
1385 * @return An LDB status code.
1387 static int group_init(struct ldb_module *module)
1390 struct ldb_context *ldb = ldb_module_get_ctx(module);
1391 struct audit_context *context = NULL;
1392 struct loadparm_context *lp_ctx
1393 = talloc_get_type_abort(
1394 ldb_get_opaque(ldb, "loadparm"),
1395 struct loadparm_context);
1396 struct tevent_context *ev = ldb_get_event_context(ldb);
1398 context = talloc_zero(module, struct audit_context);
1399 if (context == NULL) {
1400 return ldb_module_oom(module);
1403 if (lp_ctx && lpcfg_dsdb_group_change_notification(lp_ctx)) {
1404 context->send_events = true;
1405 context->msg_ctx = imessaging_client_init(context,
1410 ldb_module_set_private(module, context);
1411 return ldb_next_init(module);
1414 static const struct ldb_module_ops ldb_group_audit_log_module_ops = {
1415 .name = "group_audit_log",
1417 .modify = group_modify,
1418 .del = group_delete,
1419 .init_context = group_init,
1422 int ldb_group_audit_log_module_init(const char *version)
1424 LDB_MODULE_CHECK_VERSION(version);
1425 return ldb_register_module(&ldb_group_audit_log_module_ops);