s4 group_audit: Add Windows Event Id's to Group membership changes
[metze/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / group_audit.c
1 /*
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
5
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.
10
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.
15
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/>.
18 */
19
20 /*
21  * Provide an audit log of changes made to group memberships
22  *
23  */
24
25 #include "includes.h"
26 #include "ldb_module.h"
27 #include "lib/audit_logging/audit_logging.h"
28 #include "librpc/gen_ndr/windows_event_ids.h"
29
30 #include "dsdb/samdb/samdb.h"
31 #include "dsdb/samdb/ldb_modules/util.h"
32 #include "dsdb/samdb/ldb_modules/audit_util_proto.h"
33 #include "libcli/security/dom_sid.h"
34 #include "auth/common_auth.h"
35 #include "param/param.h"
36
37 #define AUDIT_JSON_TYPE "groupChange"
38 #define AUDIT_HR_TAG "Group Change"
39 #define AUDIT_MAJOR 1
40 #define AUDIT_MINOR 1
41 #define GROUP_LOG_LVL 5
42
43 static const char *const group_attrs[] = {"member", "groupType", NULL};
44 static const char *const group_type_attr[] = {"groupType", NULL};
45 static const char * const primary_group_attr[] = {
46         "primaryGroupID",
47         "objectSID",
48         NULL};
49
50 struct audit_context {
51         bool send_events;
52         struct imessaging_context *msg_ctx;
53 };
54
55 struct audit_callback_context {
56         struct ldb_request *request;
57         struct ldb_module *module;
58         struct ldb_message_element *members;
59         uint32_t primary_group;
60         void (*log_changes)(
61                 struct audit_callback_context *acc,
62                 const int status);
63 };
64
65 /*
66  * @brief get the transaction id.
67  *
68  * Get the id of the transaction that the current request is contained in.
69  *
70  * @param req the request.
71  *
72  * @return the transaction id GUID, or NULL if it is not there.
73  */
74 static struct GUID *get_transaction_id(
75         const struct ldb_request *request)
76 {
77         struct ldb_control *control;
78         struct dsdb_control_transaction_identifier *transaction_id;
79
80         control = ldb_request_get_control(
81                 discard_const(request),
82                 DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID);
83         if (control == NULL) {
84                 return NULL;
85         }
86         transaction_id = talloc_get_type(
87                 control->data,
88                 struct dsdb_control_transaction_identifier);
89         if (transaction_id == NULL) {
90                 return NULL;
91         }
92         return &transaction_id->transaction_guid;
93 }
94
95 /*
96  * @brief generate a JSON log entry for a group change.
97  *
98  * Generate a JSON object containing details of a users group change.
99  *
100  * @param module the ldb module
101  * @param request the ldb_request
102  * @param action the change action being performed
103  * @param user the user name
104  * @param group the group name
105  * @param status the ldb status code for the ldb operation.
106  *
107  * @return A json object containing the details.
108  *         NULL if an error was detected
109  */
110 static struct json_object audit_group_json(const struct ldb_module *module,
111                                            const struct ldb_request *request,
112                                            const char *action,
113                                            const char *user,
114                                            const char *group,
115                                            const enum event_id_type event_id,
116                                            const int status)
117 {
118         struct ldb_context *ldb = NULL;
119         const struct dom_sid *sid = NULL;
120         struct json_object wrapper = json_empty_object;
121         struct json_object audit = json_empty_object;
122         const struct tsocket_address *remote = NULL;
123         const struct GUID *unique_session_token = NULL;
124         struct GUID *transaction_id = NULL;
125         int rc = 0;
126
127         ldb = ldb_module_get_ctx(discard_const(module));
128
129         remote = dsdb_audit_get_remote_address(ldb);
130         sid = dsdb_audit_get_user_sid(module);
131         unique_session_token = dsdb_audit_get_unique_session_token(module);
132         transaction_id = get_transaction_id(request);
133
134         audit = json_new_object();
135         if (json_is_invalid(&audit)) {
136                 goto failure;
137         }
138         rc = json_add_version(&audit, AUDIT_MAJOR, AUDIT_MINOR);
139         if (rc != 0) {
140                 goto failure;
141         }
142         if (event_id != EVT_ID_NONE) {
143                 rc = json_add_int(&audit, "eventId", event_id);
144                 if (rc != 0) {
145                         goto failure;
146                 }
147         }
148         rc = json_add_int(&audit, "statusCode", status);
149         if (rc != 0) {
150                 goto failure;
151         }
152         rc = json_add_string(&audit, "status", ldb_strerror(status));
153         if (rc != 0) {
154                 goto failure;
155         }
156         rc = json_add_string(&audit, "action", action);
157         if (rc != 0) {
158                 goto failure;
159         }
160         rc = json_add_address(&audit, "remoteAddress", remote);
161         if (rc != 0) {
162                 goto failure;
163         }
164         rc = json_add_sid(&audit, "userSid", sid);
165         if (rc != 0) {
166                 goto failure;
167         }
168         rc = json_add_string(&audit, "group", group);
169         if (rc != 0) {
170                 goto failure;
171         }
172         rc = json_add_guid(&audit, "transactionId", transaction_id);
173         if (rc != 0) {
174                 goto failure;
175         }
176         rc = json_add_guid(&audit, "sessionId", unique_session_token);
177         if (rc != 0) {
178                 goto failure;
179         }
180         rc = json_add_string(&audit, "user", user);
181         if (rc != 0) {
182                 goto failure;
183         }
184
185         wrapper = json_new_object();
186         if (json_is_invalid(&wrapper)) {
187                 goto failure;
188         }
189         rc = json_add_timestamp(&wrapper);
190         if (rc != 0) {
191                 goto failure;
192         }
193         rc = json_add_string(&wrapper, "type", AUDIT_JSON_TYPE);
194         if (rc != 0) {
195                 goto failure;
196         }
197         rc = json_add_object(&wrapper, AUDIT_JSON_TYPE, &audit);
198         if (rc != 0) {
199                 goto failure;
200         }
201
202         return wrapper;
203 failure:
204         /*
205          * On a failure audit will not have been added to wrapper so it
206          * needs to free it to avoid a leak.
207          *
208          * wrapper is freed to invalidate it as it will have only been
209          * partially constructed and may be inconsistent.
210          *
211          * All the json manipulation routines handle a freed object correctly
212          */
213         json_free(&audit);
214         json_free(&wrapper);
215         DBG_ERR("Failed to create group change JSON log message\n");
216         return wrapper;
217 }
218
219 /*
220  * @brief generate a human readable log entry for a group change.
221  *
222  * Generate a human readable log entry containing details of a users group
223  * change.
224  *
225  * @param ctx the talloc context owning the returned log entry
226  * @param module the ldb module
227  * @param request the ldb_request
228  * @param action the change action being performed
229  * @param user the user name
230  * @param group the group name
231  * @param status the ldb status code for the ldb operation.
232  *
233  * @return A human readable log line.
234  */
235 static char *audit_group_human_readable(
236         TALLOC_CTX *mem_ctx,
237         const struct ldb_module *module,
238         const struct ldb_request *request,
239         const char *action,
240         const char *user,
241         const char *group,
242         const int status)
243 {
244         struct ldb_context *ldb = NULL;
245         const char *remote_host = NULL;
246         const struct dom_sid *sid = NULL;
247         const char *user_sid = NULL;
248         const char *timestamp = NULL;
249         char *log_entry = NULL;
250
251         TALLOC_CTX *ctx = talloc_new(NULL);
252
253         ldb = ldb_module_get_ctx(discard_const(module));
254
255         remote_host = dsdb_audit_get_remote_host(ldb, ctx);
256         sid = dsdb_audit_get_user_sid(module);
257         user_sid = dom_sid_string(ctx, sid);
258         timestamp = audit_get_timestamp(ctx);
259
260         log_entry = talloc_asprintf(
261                 mem_ctx,
262                 "[%s] at [%s] status [%s] "
263                 "Remote host [%s] SID [%s] Group [%s] User [%s]",
264                 action,
265                 timestamp,
266                 ldb_strerror(status),
267                 remote_host,
268                 user_sid,
269                 group,
270                 user);
271         TALLOC_FREE(ctx);
272         return log_entry;
273 }
274
275 /*
276  * @brief generate an array of parsed_dns, deferring the actual parsing.
277  *
278  * Get an array of 'struct parsed_dns' without the parsing.
279  * The parsed_dns are parsed only when needed to avoid the expense of parsing.
280  *
281  * This procedure assumes that the dn's are sorted in GUID order and contains
282  * no duplicates.  This should be valid as the module sits below repl_meta_data
283  * which ensures this.
284  *
285  * @param mem_ctx The memory context that will own the generated array
286  * @param el The message element used to generate the array.
287  *
288  * @return an array of struct parsed_dns, or NULL in the event of an error
289  */
290 static struct parsed_dn *get_parsed_dns(
291         TALLOC_CTX *mem_ctx,
292         struct ldb_message_element *el)
293 {
294         struct parsed_dn *pdn = NULL;
295
296         int i;
297
298         if (el == NULL || el->num_values == 0) {
299                 return NULL;
300         }
301
302         pdn = talloc_zero_array(mem_ctx, struct parsed_dn, el->num_values);
303         if (pdn == NULL) {
304                 DBG_ERR("Out of memory\n");
305                 return NULL;
306         }
307
308         for (i = 0; i < el->num_values; i++) {
309                 pdn[i].v = &el->values[i];
310         }
311         return pdn;
312
313 }
314
315 enum dn_compare_result {
316         LESS_THAN,
317         BINARY_EQUAL,
318         EQUAL,
319         GREATER_THAN
320 };
321 /*
322  * @brief compare parsed_dn, using GUID ordering
323  *
324  * Compare two parsed_dn structures, using GUID ordering.
325  * To avoid the overhead of parsing the DN's this function does a binary
326  * compare first. The DN's tre only parsed if they are not equal at a binary
327  * level.
328  *
329  * @param ctx talloc context that will own the parsed dsdb_dn
330  * @param ldb ldb_context
331  * @param dn1 The first dn
332  * @param dn2 The second dn
333  *
334  * @return BINARY_EQUAL values are equal at a binary level
335  *         EQUAL        DN's are equal but the meta data is different
336  *         LESS_THAN    dn1's GUID is less than dn2's GUID
337  *         GREATER_THAN dn1's GUID is greater than  dn2's GUID
338  *
339  */
340 static enum dn_compare_result dn_compare(
341         TALLOC_CTX *mem_ctx,
342         struct ldb_context *ldb,
343         struct parsed_dn *dn1,
344         struct parsed_dn *dn2) {
345
346         int res = 0;
347
348         /*
349          * Do a binary compare first to avoid unnecessary parsing
350          */
351         if (data_blob_cmp(dn1->v, dn2->v) == 0) {
352                 /*
353                  * Values are equal at a binary level so no need
354                  * for further processing
355                  */
356                 return BINARY_EQUAL;
357         }
358         /*
359          * Values not equal at the binary level, so lets
360          * do a GUID ordering compare. To do this we will need to ensure
361          * that the dn's have been parsed.
362          */
363         if (dn1->dsdb_dn == NULL) {
364                 really_parse_trusted_dn(
365                         mem_ctx,
366                         ldb,
367                         dn1,
368                         LDB_SYNTAX_DN);
369         }
370         if (dn2->dsdb_dn == NULL) {
371                 really_parse_trusted_dn(
372                         mem_ctx,
373                         ldb,
374                         dn2,
375                         LDB_SYNTAX_DN);
376         }
377
378         res = ndr_guid_compare(&dn1->guid, &dn2->guid);
379         if (res < 0) {
380                 return LESS_THAN;
381         } else if (res == 0) {
382                 return EQUAL;
383         } else {
384                 return GREATER_THAN;
385         }
386 }
387
388 /*
389  * @brief Get the DN of a users primary group as a printable string.
390  *
391  * Get the DN of a users primary group as a printable string.
392  *
393  * @param mem_ctx Talloc context the the returned string will be allocated on.
394  * @param module The ldb module
395  * @param account_sid The SID for the uses account.
396  * @param primary_group_rid The RID for the users primary group.
397  *
398  * @return a formatted DN, or null if there is an error.
399  */
400 static const char *get_primary_group_dn(
401         TALLOC_CTX *mem_ctx,
402         struct ldb_module *module,
403         struct dom_sid *account_sid,
404         uint32_t primary_group_rid)
405 {
406         NTSTATUS status;
407
408         struct ldb_context *ldb = NULL;
409         struct dom_sid *domain_sid = NULL;
410         struct dom_sid *primary_group_sid = NULL;
411         char *sid = NULL;
412         struct ldb_dn *dn = NULL;
413         struct ldb_message *msg = NULL;
414         int rc;
415
416         ldb = ldb_module_get_ctx(module);
417
418         status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
419         if (!NT_STATUS_IS_OK(status)) {
420                 return NULL;
421         }
422
423         primary_group_sid = dom_sid_add_rid(
424                 mem_ctx,
425                 domain_sid,
426                 primary_group_rid);
427         if (!primary_group_sid) {
428                 return NULL;
429         }
430
431         sid = dom_sid_string(mem_ctx, primary_group_sid);
432         if (sid == NULL) {
433                 return NULL;
434         }
435
436         dn = ldb_dn_new_fmt(mem_ctx, ldb, "<SID=%s>", sid);
437         if(dn == NULL) {
438                 return sid;
439         }
440         rc = dsdb_search_one(
441                 ldb,
442                 mem_ctx,
443                 &msg,
444                 dn,
445                 LDB_SCOPE_BASE,
446                 NULL,
447                 0,
448                 NULL);
449         if (rc != LDB_SUCCESS) {
450                 return NULL;
451         }
452
453         return ldb_dn_get_linearized(msg->dn);
454 }
455
456 /*
457  * @brief Log details of a change to a users primary group.
458  *
459  * Log details of a change to a users primary group.
460  * There is no windows event id associated with a Primary Group change.
461  * However for a new user we generate an added to group event.
462  *
463  * @param module The ldb module.
464  * @param request The request being logged.
465  * @param action Description of the action being performed.
466  * @param group The linearized for of the group DN
467  * @param status the LDB status code for the processing of the request.
468  *
469  */
470 static void log_primary_group_change(
471         struct ldb_module *module,
472         const struct ldb_request *request,
473         const char *action,
474         const char *group,
475         const int  status)
476 {
477         const char *user = NULL;
478
479         struct audit_context *ac =
480                 talloc_get_type(
481                         ldb_module_get_private(module),
482                         struct audit_context);
483
484         TALLOC_CTX *ctx = talloc_new(NULL);
485
486         user = dsdb_audit_get_primary_dn(request);
487         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
488                 char *message = NULL;
489                 message = audit_group_human_readable(
490                         ctx,
491                         module,
492                         request,
493                         action,
494                         user,
495                         group,
496                         status);
497                 audit_log_human_text(
498                         AUDIT_HR_TAG,
499                         message,
500                         DBGC_DSDB_GROUP_AUDIT,
501                         GROUP_LOG_LVL);
502                 TALLOC_FREE(message);
503         }
504
505         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
506                 (ac->msg_ctx && ac->send_events)) {
507
508                 struct json_object json;
509                 json = audit_group_json(
510                     module, request, action, user, group, EVT_ID_NONE, status);
511                 audit_log_json(
512                         &json,
513                         DBGC_DSDB_GROUP_AUDIT_JSON,
514                         GROUP_LOG_LVL);
515                 if (ac->send_events) {
516                         audit_message_send(
517                                 ac->msg_ctx,
518                                 DSDB_GROUP_EVENT_NAME,
519                                 MSG_GROUP_LOG,
520                                 &json);
521                 }
522                 json_free(&json);
523                 if (request->operation == LDB_ADD) {
524                         /*
525                          * Have just added a user, generate a groupChange
526                          * message indicating the user has been added to thier
527                          * new PrimaryGroup.
528                          */
529                 }
530         }
531         TALLOC_FREE(ctx);
532 }
533
534 /*
535  * @brief Log details of a single change to a users group membership.
536  *
537  * Log details of a change to a users group membership, except for changes
538  * to their primary group which is handled by log_primary_group_change.
539  *
540  * @param module The ldb module.
541  * @param request The request being logged.
542  * @param action Description of the action being performed.
543  * @param user The linearized form of the users DN
544  * @param status the LDB status code for the processing of the request.
545  *
546  */
547 static void log_membership_change(struct ldb_module *module,
548                                   const struct ldb_request *request,
549                                   const char *action,
550                                   const char *user,
551                                   const enum event_id_type event_id,
552                                   const int status)
553 {
554         const char *group = NULL;
555         struct audit_context *ac =
556                 talloc_get_type(
557                         ldb_module_get_private(module),
558                         struct audit_context);
559
560         TALLOC_CTX *ctx = talloc_new(NULL);
561         group = dsdb_audit_get_primary_dn(request);
562         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
563                 char *message = NULL;
564                 message = audit_group_human_readable(
565                         ctx,
566                         module,
567                         request,
568                         action,
569                         user,
570                         group,
571                         status);
572                 audit_log_human_text(
573                         AUDIT_HR_TAG,
574                         message,
575                         DBGC_DSDB_GROUP_AUDIT,
576                         GROUP_LOG_LVL);
577                 TALLOC_FREE(message);
578         }
579
580         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
581                 (ac->msg_ctx && ac->send_events)) {
582                 struct json_object json;
583                 json = audit_group_json(
584                     module, request, action, user, group, event_id, status);
585                 audit_log_json(
586                         &json,
587                         DBGC_DSDB_GROUP_AUDIT_JSON,
588                         GROUP_LOG_LVL);
589                 if (ac->send_events) {
590                         audit_message_send(
591                                 ac->msg_ctx,
592                                 DSDB_GROUP_EVENT_NAME,
593                                 MSG_GROUP_LOG,
594                                 &json);
595                 }
596                 json_free(&json);
597         }
598         TALLOC_FREE(ctx);
599 }
600
601 /*
602  * @brief Get the windows event type id for removing a user from a group type.
603  *
604  * @param group_type the type of the current group, see libds/common/flags.h
605  *
606  * @return the Windows Event Id
607  *
608  */
609 static enum event_id_type get_remove_member_event(uint32_t group_type)
610 {
611
612         switch (group_type) {
613         case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
614                 return EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP;
615         case GTYPE_SECURITY_GLOBAL_GROUP:
616                 return EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP;
617         case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
618                 return EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP;
619         case GTYPE_SECURITY_UNIVERSAL_GROUP:
620                 return EVT_ID_USER_REMOVED_FROM_UNIVERSAL_SEC_GROUP;
621         case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
622                 return EVT_ID_USER_REMOVED_FROM_GLOBAL_GROUP;
623         case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
624                 return EVT_ID_USER_REMOVED_FROM_LOCAL_GROUP;
625         case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
626                 return EVT_ID_USER_REMOVED_FROM_UNIVERSAL_GROUP;
627         default:
628                 return EVT_ID_NONE;
629         }
630 }
631
632 /*
633  * @brief Get the windows event type id for adding a user to a group type.
634  *
635  * @param group_type the type of the current group, see libds/common/flags.h
636  *
637  * @return the Windows Event Id
638  *
639  */
640 static enum event_id_type get_add_member_event(uint32_t group_type)
641 {
642
643         switch (group_type) {
644         case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
645                 return EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP;
646         case GTYPE_SECURITY_GLOBAL_GROUP:
647                 return EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP;
648         case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
649                 return EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP;
650         case GTYPE_SECURITY_UNIVERSAL_GROUP:
651                 return EVT_ID_USER_ADDED_TO_UNIVERSAL_SEC_GROUP;
652         case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
653                 return EVT_ID_USER_ADDED_TO_GLOBAL_GROUP;
654         case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
655                 return EVT_ID_USER_ADDED_TO_LOCAL_GROUP;
656         case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
657                 return EVT_ID_USER_ADDED_TO_UNIVERSAL_GROUP;
658         default:
659                 return EVT_ID_NONE;
660         }
661 }
662
663 /*
664  * @brief Log all the changes to a users group membership.
665  *
666  * Log details of a change to a users group memberships, except for changes
667  * to their primary group which is handled by log_primary_group_change.
668  *
669  * @param module The ldb module.
670  * @param request The request being logged.
671  * @param action Description of the action being performed.
672  * @param user The linearized form of the users DN
673  * @param status the LDB status code for the processing of the request.
674  *
675  */
676 static void log_membership_changes(struct ldb_module *module,
677                                    const struct ldb_request *request,
678                                    struct ldb_message_element *el,
679                                    struct ldb_message_element *old_el,
680                                    uint32_t group_type,
681                                    int status)
682 {
683         unsigned int i, old_i, new_i;
684         unsigned int old_num_values;
685         unsigned int max_num_values;
686         unsigned int new_num_values;
687         struct parsed_dn *old_val = NULL;
688         struct parsed_dn *new_val = NULL;
689         struct parsed_dn *new_values = NULL;
690         struct parsed_dn *old_values = NULL;
691         struct ldb_context *ldb = NULL;
692
693         TALLOC_CTX *ctx = talloc_new(NULL);
694
695         old_num_values = old_el ? old_el->num_values : 0;
696         new_num_values = el ? el->num_values : 0;
697         max_num_values = old_num_values + new_num_values;
698
699         if (max_num_values == 0) {
700                 /*
701                  * There is nothing to do!
702                  */
703                 TALLOC_FREE(ctx);
704                 return;
705         }
706
707         old_values = get_parsed_dns(ctx, old_el);
708         new_values = get_parsed_dns(ctx, el);
709         ldb = ldb_module_get_ctx(module);
710
711         old_i = 0;
712         new_i = 0;
713         for (i = 0; i < max_num_values; i++) {
714                 enum dn_compare_result cmp;
715                 if (old_i < old_num_values && new_i < new_num_values) {
716                         /*
717                          * Both list have values, so compare the values
718                          */
719                         old_val = &old_values[old_i];
720                         new_val = &new_values[new_i];
721                         cmp = dn_compare(ctx, ldb, old_val, new_val);
722                 } else if (old_i < old_num_values) {
723                         /*
724                          * the new list is empty, read the old list
725                          */
726                         old_val = &old_values[old_i];
727                         new_val = NULL;
728                         cmp = LESS_THAN;
729                 } else if (new_i < new_num_values) {
730                         /*
731                          * the old list is empty, read new list
732                          */
733                         old_val = NULL;
734                         new_val = &new_values[new_i];
735                         cmp = GREATER_THAN;
736                 } else {
737                         break;
738                 }
739
740                 if (cmp == LESS_THAN) {
741                         /*
742                          * Have an entry in the original record that is not in
743                          * the new record. So it's been deleted
744                          */
745                         const char *user = NULL;
746                         enum event_id_type event_id;
747                         if (old_val->dsdb_dn == NULL) {
748                                 really_parse_trusted_dn(
749                                         ctx,
750                                         ldb,
751                                         old_val,
752                                         LDB_SYNTAX_DN);
753                         }
754                         user = ldb_dn_get_linearized(old_val->dsdb_dn->dn);
755                         event_id = get_remove_member_event(group_type);
756                         log_membership_change(
757                             module, request, "Removed", user, event_id, status);
758                         old_i++;
759                 } else if (cmp == BINARY_EQUAL) {
760                         /*
761                          * DN's unchanged at binary level so nothing to do.
762                          */
763                         old_i++;
764                         new_i++;
765                 } else if (cmp == EQUAL) {
766                         /*
767                          * DN is unchanged now need to check the flags to
768                          * determine if a record has been deleted or undeleted
769                          */
770                         uint32_t old_flags;
771                         uint32_t new_flags;
772                         if (old_val->dsdb_dn == NULL) {
773                                 really_parse_trusted_dn(
774                                         ctx,
775                                         ldb,
776                                         old_val,
777                                         LDB_SYNTAX_DN);
778                         }
779                         if (new_val->dsdb_dn == NULL) {
780                                 really_parse_trusted_dn(
781                                         ctx,
782                                         ldb,
783                                         new_val,
784                                         LDB_SYNTAX_DN);
785                         }
786
787                         dsdb_get_extended_dn_uint32(
788                                 old_val->dsdb_dn->dn,
789                                 &old_flags,
790                                 "RMD_FLAGS");
791                         dsdb_get_extended_dn_uint32(
792                                 new_val->dsdb_dn->dn,
793                                 &new_flags,
794                                 "RMD_FLAGS");
795                         if (new_flags == old_flags) {
796                                 /*
797                                  * No changes to the Repl meta data so can
798                                  * no need to log the change
799                                  */
800                                 old_i++;
801                                 new_i++;
802                                 continue;
803                         }
804                         if (new_flags & DSDB_RMD_FLAG_DELETED) {
805                                 /*
806                                  * DN has been deleted.
807                                  */
808                                 const char *user = NULL;
809                                 enum event_id_type event_id;
810                                 user = ldb_dn_get_linearized(
811                                         old_val->dsdb_dn->dn);
812                                 event_id = get_remove_member_event(group_type);
813                                 log_membership_change(module,
814                                                       request,
815                                                       "Removed",
816                                                       user,
817                                                       event_id,
818                                                       status);
819                         } else {
820                                 /*
821                                  * DN has been re-added
822                                  */
823                                 const char *user = NULL;
824                                 enum event_id_type event_id;
825                                 user = ldb_dn_get_linearized(
826                                         new_val->dsdb_dn->dn);
827                                 event_id = get_add_member_event(group_type);
828                                 log_membership_change(module,
829                                                       request,
830                                                       "Added",
831                                                       user,
832                                                       event_id,
833                                                       status);
834                         }
835                         old_i++;
836                         new_i++;
837                 } else {
838                         /*
839                          * Member in the updated record that's not in the
840                          * original, so it must have been added.
841                          */
842                         const char *user = NULL;
843                         enum event_id_type event_id;
844                         if ( new_val->dsdb_dn == NULL) {
845                                 really_parse_trusted_dn(
846                                         ctx,
847                                         ldb,
848                                         new_val,
849                                         LDB_SYNTAX_DN);
850                         }
851                         user = ldb_dn_get_linearized(new_val->dsdb_dn->dn);
852                         event_id = get_add_member_event(group_type);
853                         log_membership_change(
854                             module, request, "Added", user, event_id, status);
855                         new_i++;
856                 }
857         }
858
859         TALLOC_FREE(ctx);
860 }
861
862 /*
863  * @brief log a group change message for a newly added user.
864  *
865  * When a user is added we need to generate a GroupChange Add message to
866  * log that the user has been added to their PrimaryGroup
867  */
868 static void log_new_user_added_to_primary_group(
869     TALLOC_CTX *ctx,
870     struct audit_callback_context *acc,
871     const char *group,
872     const int status)
873 {
874         uint32_t group_type;
875         enum event_id_type event_id = EVT_ID_NONE;
876         struct ldb_result *res = NULL;
877         struct ldb_dn *group_dn = NULL;
878         struct ldb_context *ldb = NULL;
879         int ret;
880
881         ldb = ldb_module_get_ctx(acc->module);
882         group_dn = ldb_dn_new(ctx, ldb, group);
883         ret = dsdb_module_search_dn(acc->module,
884                                     ctx,
885                                     &res,
886                                     group_dn,
887                                     group_type_attr,
888                                     DSDB_FLAG_NEXT_MODULE |
889                                         DSDB_SEARCH_REVEAL_INTERNALS |
890                                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
891                                     NULL);
892         if (ret == LDB_SUCCESS) {
893                 const char *user = NULL;
894                 group_type =
895                     ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
896                 event_id = get_add_member_event(group_type);
897                 user = dsdb_audit_get_primary_dn(acc->request);
898                 log_membership_change(
899                     acc->module, acc->request, "Added", user, event_id, status);
900         }
901 }
902
903 /*
904  * @brief Log the details of a primary group change.
905  *
906  * Retrieve the users primary groupo after the operation has completed
907  * and call log_primary_group_change to log the actual changes.
908  *
909  * @param acc details of the primary group before the operation.
910  * @param status The status code returned by the operation.
911  *
912  * @return an LDB status code.
913  */
914 static void log_user_primary_group_change(
915         struct audit_callback_context *acc,
916         const int status)
917 {
918         TALLOC_CTX *ctx = talloc_new(NULL);
919         uint32_t new_rid;
920         struct dom_sid *account_sid = NULL;
921         int ret;
922         const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
923
924         if (status == LDB_SUCCESS && msg != NULL) {
925                 struct ldb_result *res = NULL;
926                 ret = dsdb_module_search_dn(
927                         acc->module,
928                         ctx,
929                         &res,
930                         msg->dn,
931                         primary_group_attr,
932                         DSDB_FLAG_NEXT_MODULE |
933                         DSDB_SEARCH_REVEAL_INTERNALS |
934                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
935                         NULL);
936                 if (ret == LDB_SUCCESS) {
937                         new_rid = ldb_msg_find_attr_as_uint(
938                                 msg,
939                                 "primaryGroupID",
940                                 ~0);
941                         account_sid = samdb_result_dom_sid(
942                                 ctx,
943                                 res->msgs[0],
944                                 "objectSid");
945                 }
946         }
947         /*
948          * If we don't have a new value then the user has been deleted
949          * which we currently do not log.
950          * Otherwise only log if the primary group has actually changed.
951          */
952         if (account_sid != NULL &&
953             new_rid != ~0 &&
954             acc->primary_group != new_rid) {
955                 const char* group = get_primary_group_dn(
956                         ctx,
957                         acc->module,
958                         account_sid,
959                         new_rid);
960                 log_primary_group_change(
961                         acc->module,
962                         acc->request,
963                         "PrimaryGroup",
964                         group,
965                         status);
966                 /*
967                  * Are we adding a new user with the primaryGroupID
968                  * set. If so and we're generating JSON audit logs, will need to
969                  * generate an "Add" message with the appropriate windows
970                  * event id.
971                  */
972                 if (acc->request->operation == LDB_ADD) {
973                         log_new_user_added_to_primary_group(
974                             ctx, acc, group, status);
975                 }
976         }
977         TALLOC_FREE(ctx);
978 }
979
980 /*
981  * @brief log the changes to users group membership.
982  *
983  * Retrieve the users group memberships after the operation has completed
984  * and call log_membership_changes to log the actual changes.
985  *
986  * @param acc details of the group memberships before the operation.
987  * @param status The status code returned by the operation.
988  *
989  */
990 static void log_group_membership_changes(
991         struct audit_callback_context *acc,
992         const int status)
993 {
994         TALLOC_CTX *ctx = talloc_new(NULL);
995         struct ldb_message_element *new_val = NULL;
996         int ret;
997         uint32_t group_type;
998         const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
999         if (status == LDB_SUCCESS && msg != NULL) {
1000                 struct ldb_result *res = NULL;
1001                 ret = dsdb_module_search_dn(
1002                         acc->module,
1003                         ctx,
1004                         &res,
1005                         msg->dn,
1006                         group_attrs,
1007                         DSDB_FLAG_NEXT_MODULE |
1008                         DSDB_SEARCH_REVEAL_INTERNALS |
1009                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1010                         NULL);
1011                 if (ret == LDB_SUCCESS) {
1012                         new_val = ldb_msg_find_element(res->msgs[0], "member");
1013                         group_type = ldb_msg_find_attr_as_uint(
1014                             res->msgs[0], "groupType", 0);
1015                         log_membership_changes(acc->module,
1016                                                acc->request,
1017                                                new_val,
1018                                                acc->members,
1019                                                group_type,
1020                                                status);
1021                                 }
1022         }
1023         TALLOC_FREE(ctx);
1024 }
1025
1026 /*
1027  * @brief call back function to log changes to the group memberships.
1028  *
1029  * Call back function to log changes to the uses broup memberships.
1030  *
1031  * @param req the ldb request.
1032  * @param ares the ldb result
1033  *
1034  * @return am LDB status code.
1035  */
1036 static int group_audit_callback(
1037         struct ldb_request *req,
1038         struct ldb_reply *ares)
1039 {
1040         struct audit_callback_context *ac = NULL;
1041
1042         ac = talloc_get_type(
1043                 req->context,
1044                 struct audit_callback_context);
1045
1046         if (!ares) {
1047                 return ldb_module_done(
1048                                 ac->request, NULL, NULL,
1049                                 LDB_ERR_OPERATIONS_ERROR);
1050         }
1051
1052         /* pass on to the callback */
1053         switch (ares->type) {
1054         case LDB_REPLY_ENTRY:
1055                 return ldb_module_send_entry(
1056                         ac->request,
1057                         ares->message,
1058                         ares->controls);
1059
1060         case LDB_REPLY_REFERRAL:
1061                 return ldb_module_send_referral(
1062                         ac->request,
1063                         ares->referral);
1064
1065         case LDB_REPLY_DONE:
1066                 /*
1067                  * Log on DONE now we have a result code
1068                  */
1069                 ac->log_changes(ac, ares->error);
1070                 return ldb_module_done(
1071                         ac->request,
1072                         ares->controls,
1073                         ares->response,
1074                         ares->error);
1075                 break;
1076
1077         default:
1078                 /* Can't happen */
1079                 return LDB_ERR_OPERATIONS_ERROR;
1080         }
1081 }
1082
1083 /*
1084  * @brief Does this request change the primary group.
1085  *
1086  * Does the request change the primary group, i.e. does it contain the
1087  * primaryGroupID attribute.
1088  *
1089  * @param req the request to examine.
1090  *
1091  * @return True if the request modifies the primary group.
1092  */
1093 static bool has_primary_group_id(struct ldb_request *req)
1094 {
1095         struct ldb_message_element *el = NULL;
1096         const struct ldb_message *msg = NULL;
1097
1098         msg = dsdb_audit_get_message(req);
1099         el = ldb_msg_find_element(msg, "primaryGroupID");
1100
1101         return (el != NULL);
1102 }
1103
1104 /*
1105  * @brief Does this request change group membership.
1106  *
1107  * Does the request change the ses group memberships, i.e. does it contain the
1108  * member attribute.
1109  *
1110  * @param req the request to examine.
1111  *
1112  * @return True if the request modifies the users group memberships.
1113  */
1114 static bool has_group_membership_changes(struct ldb_request *req)
1115 {
1116         struct ldb_message_element *el = NULL;
1117         const struct ldb_message *msg = NULL;
1118
1119         msg = dsdb_audit_get_message(req);
1120         el = ldb_msg_find_element(msg, "member");
1121
1122         return (el != NULL);
1123 }
1124
1125
1126
1127 /*
1128  * @brief Install the callback function to log an add request.
1129  *
1130  * Install the callback function to log an add request changing the users
1131  * group memberships. As we want to log the returned status code, we need to
1132  * register a callback function that will be called once the operation has
1133  * completed.
1134  *
1135  * This function reads the current user record so that we can log the before
1136  * and after state.
1137  *
1138  * @param module The ldb module.
1139  * @param req The modify request.
1140  *
1141  * @return and LDB status code.
1142  */
1143 static int set_group_membership_add_callback(
1144         struct ldb_module *module,
1145         struct ldb_request *req)
1146 {
1147         struct audit_callback_context *context = NULL;
1148         struct ldb_request *new_req = NULL;
1149         struct ldb_context *ldb = NULL;
1150         int ret;
1151         /*
1152          * Adding group memberships so will need to log the changes.
1153          */
1154         ldb = ldb_module_get_ctx(module);
1155         context = talloc_zero(req, struct audit_callback_context);
1156
1157         if (context == NULL) {
1158                 return ldb_oom(ldb);
1159         }
1160         context->request = req;
1161         context->module = module;
1162         context->log_changes = log_group_membership_changes;
1163         /*
1164          * We want to log the return code status, so we need to register
1165          * a callback function to get the actual result.
1166          * We need to take a new copy so that we don't alter the callers copy
1167          */
1168         ret = ldb_build_add_req(
1169                 &new_req,
1170                 ldb,
1171                 req,
1172                 req->op.add.message,
1173                 req->controls,
1174                 context,
1175                 group_audit_callback,
1176                 req);
1177         if (ret != LDB_SUCCESS) {
1178                 return ret;
1179         }
1180         return ldb_next_request(module, new_req);
1181 }
1182
1183
1184 /*
1185  * @brief Install the callback function to log a modify request.
1186  *
1187  * Install the callback function to log a modify request changing the primary
1188  * group . As we want to log the returned status code, we need to register a
1189  * callback function that will be called once the operation has completed.
1190  *
1191  * This function reads the current user record so that we can log the before
1192  * and after state.
1193  *
1194  * @param module The ldb module.
1195  * @param req The modify request.
1196  *
1197  * @return and LDB status code.
1198  */
1199 static int set_primary_group_modify_callback(
1200         struct ldb_module *module,
1201         struct ldb_request *req)
1202 {
1203         struct audit_callback_context *context = NULL;
1204         struct ldb_request *new_req = NULL;
1205         struct ldb_context *ldb = NULL;
1206         const struct ldb_message *msg = NULL;
1207         struct ldb_result *res = NULL;
1208         int ret;
1209
1210         TALLOC_CTX *ctx = talloc_new(NULL);
1211
1212         ldb = ldb_module_get_ctx(module);
1213
1214         context = talloc_zero(req, struct audit_callback_context);
1215         if (context == NULL) {
1216                 ret = ldb_oom(ldb);
1217                 goto exit;
1218         }
1219         context->request = req;
1220         context->module = module;
1221         context->log_changes = log_user_primary_group_change;
1222
1223         msg = dsdb_audit_get_message(req);
1224         ret = dsdb_module_search_dn(
1225                 module,
1226                 ctx,
1227                 &res,
1228                 msg->dn,
1229                 primary_group_attr,
1230                 DSDB_FLAG_NEXT_MODULE |
1231                 DSDB_SEARCH_REVEAL_INTERNALS |
1232                 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1233                 NULL);
1234         if (ret == LDB_SUCCESS) {
1235                 uint32_t pg;
1236                 pg = ldb_msg_find_attr_as_uint(
1237                         res->msgs[0],
1238                         "primaryGroupID",
1239                         ~0);
1240                 context->primary_group = pg;
1241         }
1242         /*
1243          * We want to log the return code status, so we need to register
1244          * a callback function to get the actual result.
1245          * We need to take a new copy so that we don't alter the callers copy
1246          */
1247         ret = ldb_build_mod_req(
1248                 &new_req,
1249                 ldb,
1250                 req,
1251                 req->op.add.message,
1252                 req->controls,
1253                 context,
1254                 group_audit_callback,
1255                 req);
1256         if (ret != LDB_SUCCESS) {
1257                 goto exit;
1258         }
1259         ret = ldb_next_request(module, new_req);
1260 exit:
1261         TALLOC_FREE(ctx);
1262         return ret;
1263 }
1264
1265 /*
1266  * @brief Install the callback function to log an add request.
1267  *
1268  * Install the callback function to log an add request changing the primary
1269  * group . As we want to log the returned status code, we need to register a
1270  * callback function that will be called once the operation has completed.
1271  *
1272  * This function reads the current user record so that we can log the before
1273  * and after state.
1274  *
1275  * @param module The ldb module.
1276  * @param req The modify request.
1277  *
1278  * @return and LDB status code.
1279  */
1280 static int set_primary_group_add_callback(
1281         struct ldb_module *module,
1282         struct ldb_request *req)
1283 {
1284         struct audit_callback_context *context = NULL;
1285         struct ldb_request *new_req = NULL;
1286         struct ldb_context *ldb = NULL;
1287         int ret;
1288         /*
1289          * Adding a user with a primary group.
1290          */
1291         ldb = ldb_module_get_ctx(module);
1292         context = talloc_zero(req, struct audit_callback_context);
1293
1294         if (context == NULL) {
1295                 return ldb_oom(ldb);
1296         }
1297         context->request = req;
1298         context->module = module;
1299         context->log_changes = log_user_primary_group_change;
1300         /*
1301          * We want to log the return code status, so we need to register
1302          * a callback function to get the actual result.
1303          * We need to take a new copy so that we don't alter the callers copy
1304          */
1305         ret = ldb_build_add_req(
1306                 &new_req,
1307                 ldb,
1308                 req,
1309                 req->op.add.message,
1310                 req->controls,
1311                 context,
1312                 group_audit_callback,
1313                 req);
1314         if (ret != LDB_SUCCESS) {
1315                 return ret;
1316         }
1317         return ldb_next_request(module, new_req);
1318 }
1319
1320 /*
1321  * @brief Module handler for add operations.
1322  *
1323  * Inspect the current add request, and if needed log any group membership
1324  * changes.
1325  *
1326  * @param module The ldb module.
1327  * @param req The modify request.
1328  *
1329  * @return and LDB status code.
1330  */
1331 static int group_add(
1332         struct ldb_module *module,
1333         struct ldb_request *req)
1334 {
1335
1336         struct audit_context *ac =
1337                 talloc_get_type(
1338                         ldb_module_get_private(module),
1339                         struct audit_context);
1340         /*
1341          * Currently we don't log replicated group changes
1342          */
1343         if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1344                 return ldb_next_request(module, req);
1345         }
1346
1347         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL) ||
1348                 CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
1349                 (ac->msg_ctx && ac->send_events)) {
1350                 /*
1351                  * Avoid the overheads of logging unless it has been
1352                  * enabled
1353                  */
1354                 if (has_group_membership_changes(req)) {
1355                         return set_group_membership_add_callback(module, req);
1356                 }
1357                 if (has_primary_group_id(req)) {
1358                         return set_primary_group_add_callback(module, req);
1359                 }
1360         }
1361         return ldb_next_request(module, req);
1362 }
1363
1364 /*
1365  * @brief Module handler for delete operations.
1366  *
1367  * Currently there is no logging for delete operations.
1368  *
1369  * @param module The ldb module.
1370  * @param req The modify request.
1371  *
1372  * @return and LDB status code.
1373  */
1374 static int group_delete(
1375         struct ldb_module *module,
1376         struct ldb_request *req)
1377 {
1378         return ldb_next_request(module, req);
1379 }
1380
1381 /*
1382  * @brief Install the callback function to log a modify request.
1383  *
1384  * Install the callback function to log a modify request. As we want to log the
1385  * returned status code, we need to register a callback function that will be
1386  * called once the operation has completed.
1387  *
1388  * This function reads the current user record so that we can log the before
1389  * and after state.
1390  *
1391  * @param module The ldb module.
1392  * @param req The modify request.
1393  *
1394  * @return and LDB status code.
1395  */
1396 static int set_group_modify_callback(
1397         struct ldb_module *module,
1398         struct ldb_request *req)
1399 {
1400         struct audit_callback_context *context = NULL;
1401         struct ldb_request *new_req = NULL;
1402         struct ldb_context *ldb = NULL;
1403         struct ldb_result *res = NULL;
1404         int ret;
1405
1406         ldb = ldb_module_get_ctx(module);
1407         context = talloc_zero(req, struct audit_callback_context);
1408
1409         if (context == NULL) {
1410                 return ldb_oom(ldb);
1411         }
1412         context->request = req;
1413         context->module  = module;
1414         context->log_changes = log_group_membership_changes;
1415
1416         /*
1417          * About to change the group memberships need to read
1418          * the current state from the database.
1419          */
1420         ret = dsdb_module_search_dn(
1421                 module,
1422                 context,
1423                 &res,
1424                 req->op.add.message->dn,
1425                 group_attrs,
1426                 DSDB_FLAG_NEXT_MODULE |
1427                 DSDB_SEARCH_REVEAL_INTERNALS |
1428                 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1429                 NULL);
1430         if (ret == LDB_SUCCESS) {
1431                 context->members = ldb_msg_find_element(res->msgs[0], "member");
1432         }
1433
1434         ret = ldb_build_mod_req(
1435                 &new_req,
1436                 ldb,
1437                 req,
1438                 req->op.mod.message,
1439                 req->controls,
1440                 context,
1441                 group_audit_callback,
1442                 req);
1443         if (ret != LDB_SUCCESS) {
1444                 return ret;
1445         }
1446         return ldb_next_request(module, new_req);
1447 }
1448
1449 /*
1450  * @brief Module handler for modify operations.
1451  *
1452  * Inspect the current modify request, and if needed log any group membership
1453  * changes.
1454  *
1455  * @param module The ldb module.
1456  * @param req The modify request.
1457  *
1458  * @return and LDB status code.
1459  */
1460 static int group_modify(
1461         struct ldb_module *module,
1462         struct ldb_request *req)
1463 {
1464
1465         struct audit_context *ac =
1466                 talloc_get_type(
1467                         ldb_module_get_private(module),
1468                         struct audit_context);
1469         /*
1470          * Currently we don't log replicated group changes
1471          */
1472         if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1473                 return ldb_next_request(module, req);
1474         }
1475
1476         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL) ||
1477             CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
1478                 (ac->msg_ctx && ac->send_events)) {
1479                 /*
1480                  * Avoid the overheads of logging unless it has been
1481                  * enabled
1482                  */
1483                 if (has_group_membership_changes(req)) {
1484                         return set_group_modify_callback(module, req);
1485                 }
1486                 if (has_primary_group_id(req)) {
1487                         return set_primary_group_modify_callback(module, req);
1488                 }
1489         }
1490         return ldb_next_request(module, req);
1491 }
1492
1493 /*
1494  * @brief ldb module initialisation
1495  *
1496  * Initialise the module, loading the private data etc.
1497  *
1498  * @param module The ldb module to initialise.
1499  *
1500  * @return An LDB status code.
1501  */
1502 static int group_init(struct ldb_module *module)
1503 {
1504
1505         struct ldb_context *ldb = ldb_module_get_ctx(module);
1506         struct audit_context *context = NULL;
1507         struct loadparm_context *lp_ctx
1508                 = talloc_get_type_abort(
1509                         ldb_get_opaque(ldb, "loadparm"),
1510                         struct loadparm_context);
1511         struct tevent_context *ev = ldb_get_event_context(ldb);
1512
1513         context = talloc_zero(module, struct audit_context);
1514         if (context == NULL) {
1515                 return ldb_module_oom(module);
1516         }
1517
1518         if (lp_ctx && lpcfg_dsdb_group_change_notification(lp_ctx)) {
1519                 context->send_events = true;
1520                 context->msg_ctx = imessaging_client_init(context,
1521                                                           lp_ctx,
1522                                                           ev);
1523         }
1524
1525         ldb_module_set_private(module, context);
1526         return ldb_next_init(module);
1527 }
1528
1529 static const struct ldb_module_ops ldb_group_audit_log_module_ops = {
1530         .name              = "group_audit_log",
1531         .add               = group_add,
1532         .modify            = group_modify,
1533         .del               = group_delete,
1534         .init_context      = group_init,
1535 };
1536
1537 int ldb_group_audit_log_module_init(const char *version)
1538 {
1539         LDB_MODULE_CHECK_VERSION(version);
1540         return ldb_register_module(&ldb_group_audit_log_module_ops);
1541 }