dsdb/group_audit: use common get_parsed_dns_trusted()
[ambi/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         int ret;
295         struct parsed_dn *pdn = NULL;
296
297         if (el == NULL || el->num_values == 0) {
298                 return NULL;
299         }
300
301         ret = get_parsed_dns_trusted(mem_ctx, el, &pdn);
302         if (ret == LDB_ERR_OPERATIONS_ERROR) {
303                 DBG_ERR("Out of memory\n");
304                 return NULL;
305         }
306         return pdn;
307
308 }
309
310 enum dn_compare_result {
311         LESS_THAN,
312         BINARY_EQUAL,
313         EQUAL,
314         GREATER_THAN
315 };
316 /*
317  * @brief compare parsed_dn, using GUID ordering
318  *
319  * Compare two parsed_dn structures, using GUID ordering.
320  * To avoid the overhead of parsing the DN's this function does a binary
321  * compare first. The DN's tre only parsed if they are not equal at a binary
322  * level.
323  *
324  * @param ctx talloc context that will own the parsed dsdb_dn
325  * @param ldb ldb_context
326  * @param dn1 The first dn
327  * @param dn2 The second dn
328  *
329  * @return BINARY_EQUAL values are equal at a binary level
330  *         EQUAL        DN's are equal but the meta data is different
331  *         LESS_THAN    dn1's GUID is less than dn2's GUID
332  *         GREATER_THAN dn1's GUID is greater than  dn2's GUID
333  *
334  */
335 static enum dn_compare_result dn_compare(
336         TALLOC_CTX *mem_ctx,
337         struct ldb_context *ldb,
338         struct parsed_dn *dn1,
339         struct parsed_dn *dn2) {
340
341         int res = 0;
342
343         /*
344          * Do a binary compare first to avoid unnecessary parsing
345          */
346         if (data_blob_cmp(dn1->v, dn2->v) == 0) {
347                 /*
348                  * Values are equal at a binary level so no need
349                  * for further processing
350                  */
351                 return BINARY_EQUAL;
352         }
353         /*
354          * Values not equal at the binary level, so lets
355          * do a GUID ordering compare. To do this we will need to ensure
356          * that the dn's have been parsed.
357          */
358         if (dn1->dsdb_dn == NULL) {
359                 really_parse_trusted_dn(
360                         mem_ctx,
361                         ldb,
362                         dn1,
363                         LDB_SYNTAX_DN);
364         }
365         if (dn2->dsdb_dn == NULL) {
366                 really_parse_trusted_dn(
367                         mem_ctx,
368                         ldb,
369                         dn2,
370                         LDB_SYNTAX_DN);
371         }
372
373         res = ndr_guid_compare(&dn1->guid, &dn2->guid);
374         if (res < 0) {
375                 return LESS_THAN;
376         } else if (res == 0) {
377                 return EQUAL;
378         } else {
379                 return GREATER_THAN;
380         }
381 }
382
383 /*
384  * @brief Get the DN of a users primary group as a printable string.
385  *
386  * Get the DN of a users primary group as a printable string.
387  *
388  * @param mem_ctx Talloc context the the returned string will be allocated on.
389  * @param module The ldb module
390  * @param account_sid The SID for the uses account.
391  * @param primary_group_rid The RID for the users primary group.
392  *
393  * @return a formatted DN, or null if there is an error.
394  */
395 static const char *get_primary_group_dn(
396         TALLOC_CTX *mem_ctx,
397         struct ldb_module *module,
398         struct dom_sid *account_sid,
399         uint32_t primary_group_rid)
400 {
401         NTSTATUS status;
402
403         struct ldb_context *ldb = NULL;
404         struct dom_sid *domain_sid = NULL;
405         struct dom_sid *primary_group_sid = NULL;
406         char *sid = NULL;
407         struct ldb_dn *dn = NULL;
408         struct ldb_message *msg = NULL;
409         int rc;
410
411         ldb = ldb_module_get_ctx(module);
412
413         status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
414         if (!NT_STATUS_IS_OK(status)) {
415                 return NULL;
416         }
417
418         primary_group_sid = dom_sid_add_rid(
419                 mem_ctx,
420                 domain_sid,
421                 primary_group_rid);
422         if (!primary_group_sid) {
423                 return NULL;
424         }
425
426         sid = dom_sid_string(mem_ctx, primary_group_sid);
427         if (sid == NULL) {
428                 return NULL;
429         }
430
431         dn = ldb_dn_new_fmt(mem_ctx, ldb, "<SID=%s>", sid);
432         if(dn == NULL) {
433                 return sid;
434         }
435         rc = dsdb_search_one(
436                 ldb,
437                 mem_ctx,
438                 &msg,
439                 dn,
440                 LDB_SCOPE_BASE,
441                 NULL,
442                 0,
443                 NULL);
444         if (rc != LDB_SUCCESS) {
445                 return NULL;
446         }
447
448         return ldb_dn_get_linearized(msg->dn);
449 }
450
451 /*
452  * @brief Log details of a change to a users primary group.
453  *
454  * Log details of a change to a users primary group.
455  * There is no windows event id associated with a Primary Group change.
456  * However for a new user we generate an added to group event.
457  *
458  * @param module The ldb module.
459  * @param request The request being logged.
460  * @param action Description of the action being performed.
461  * @param group The linearized for of the group DN
462  * @param status the LDB status code for the processing of the request.
463  *
464  */
465 static void log_primary_group_change(
466         struct ldb_module *module,
467         const struct ldb_request *request,
468         const char *action,
469         const char *group,
470         const int  status)
471 {
472         const char *user = NULL;
473
474         struct audit_context *ac =
475                 talloc_get_type(
476                         ldb_module_get_private(module),
477                         struct audit_context);
478
479         TALLOC_CTX *ctx = talloc_new(NULL);
480
481         user = dsdb_audit_get_primary_dn(request);
482         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
483                 char *message = NULL;
484                 message = audit_group_human_readable(
485                         ctx,
486                         module,
487                         request,
488                         action,
489                         user,
490                         group,
491                         status);
492                 audit_log_human_text(
493                         AUDIT_HR_TAG,
494                         message,
495                         DBGC_DSDB_GROUP_AUDIT,
496                         GROUP_LOG_LVL);
497                 TALLOC_FREE(message);
498         }
499
500         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
501                 (ac->msg_ctx && ac->send_events)) {
502
503                 struct json_object json;
504                 json = audit_group_json(
505                     module, request, action, user, group, EVT_ID_NONE, status);
506                 audit_log_json(
507                         &json,
508                         DBGC_DSDB_GROUP_AUDIT_JSON,
509                         GROUP_LOG_LVL);
510                 if (ac->send_events) {
511                         audit_message_send(
512                                 ac->msg_ctx,
513                                 DSDB_GROUP_EVENT_NAME,
514                                 MSG_GROUP_LOG,
515                                 &json);
516                 }
517                 json_free(&json);
518                 if (request->operation == LDB_ADD) {
519                         /*
520                          * Have just added a user, generate a groupChange
521                          * message indicating the user has been added to thier
522                          * new PrimaryGroup.
523                          */
524                 }
525         }
526         TALLOC_FREE(ctx);
527 }
528
529 /*
530  * @brief Log details of a single change to a users group membership.
531  *
532  * Log details of a change to a users group membership, except for changes
533  * to their primary group which is handled by log_primary_group_change.
534  *
535  * @param module The ldb module.
536  * @param request The request being logged.
537  * @param action Description of the action being performed.
538  * @param user The linearized form of the users DN
539  * @param status the LDB status code for the processing of the request.
540  *
541  */
542 static void log_membership_change(struct ldb_module *module,
543                                   const struct ldb_request *request,
544                                   const char *action,
545                                   const char *user,
546                                   const enum event_id_type event_id,
547                                   const int status)
548 {
549         const char *group = NULL;
550         struct audit_context *ac =
551                 talloc_get_type(
552                         ldb_module_get_private(module),
553                         struct audit_context);
554
555         TALLOC_CTX *ctx = talloc_new(NULL);
556         group = dsdb_audit_get_primary_dn(request);
557         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL)) {
558                 char *message = NULL;
559                 message = audit_group_human_readable(
560                         ctx,
561                         module,
562                         request,
563                         action,
564                         user,
565                         group,
566                         status);
567                 audit_log_human_text(
568                         AUDIT_HR_TAG,
569                         message,
570                         DBGC_DSDB_GROUP_AUDIT,
571                         GROUP_LOG_LVL);
572                 TALLOC_FREE(message);
573         }
574
575         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
576                 (ac->msg_ctx && ac->send_events)) {
577                 struct json_object json;
578                 json = audit_group_json(
579                     module, request, action, user, group, event_id, status);
580                 audit_log_json(
581                         &json,
582                         DBGC_DSDB_GROUP_AUDIT_JSON,
583                         GROUP_LOG_LVL);
584                 if (ac->send_events) {
585                         audit_message_send(
586                                 ac->msg_ctx,
587                                 DSDB_GROUP_EVENT_NAME,
588                                 MSG_GROUP_LOG,
589                                 &json);
590                 }
591                 json_free(&json);
592         }
593         TALLOC_FREE(ctx);
594 }
595
596 /*
597  * @brief Get the windows event type id for removing a user from a group type.
598  *
599  * @param group_type the type of the current group, see libds/common/flags.h
600  *
601  * @return the Windows Event Id
602  *
603  */
604 static enum event_id_type get_remove_member_event(uint32_t group_type)
605 {
606
607         switch (group_type) {
608         case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
609                 return EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP;
610         case GTYPE_SECURITY_GLOBAL_GROUP:
611                 return EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP;
612         case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
613                 return EVT_ID_USER_REMOVED_FROM_LOCAL_SEC_GROUP;
614         case GTYPE_SECURITY_UNIVERSAL_GROUP:
615                 return EVT_ID_USER_REMOVED_FROM_UNIVERSAL_SEC_GROUP;
616         case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
617                 return EVT_ID_USER_REMOVED_FROM_GLOBAL_GROUP;
618         case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
619                 return EVT_ID_USER_REMOVED_FROM_LOCAL_GROUP;
620         case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
621                 return EVT_ID_USER_REMOVED_FROM_UNIVERSAL_GROUP;
622         default:
623                 return EVT_ID_NONE;
624         }
625 }
626
627 /*
628  * @brief Get the windows event type id for adding a user to a group type.
629  *
630  * @param group_type the type of the current group, see libds/common/flags.h
631  *
632  * @return the Windows Event Id
633  *
634  */
635 static enum event_id_type get_add_member_event(uint32_t group_type)
636 {
637
638         switch (group_type) {
639         case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
640                 return EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP;
641         case GTYPE_SECURITY_GLOBAL_GROUP:
642                 return EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP;
643         case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
644                 return EVT_ID_USER_ADDED_TO_LOCAL_SEC_GROUP;
645         case GTYPE_SECURITY_UNIVERSAL_GROUP:
646                 return EVT_ID_USER_ADDED_TO_UNIVERSAL_SEC_GROUP;
647         case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
648                 return EVT_ID_USER_ADDED_TO_GLOBAL_GROUP;
649         case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
650                 return EVT_ID_USER_ADDED_TO_LOCAL_GROUP;
651         case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
652                 return EVT_ID_USER_ADDED_TO_UNIVERSAL_GROUP;
653         default:
654                 return EVT_ID_NONE;
655         }
656 }
657
658 /*
659  * @brief Log all the changes to a users group membership.
660  *
661  * Log details of a change to a users group memberships, except for changes
662  * to their primary group which is handled by log_primary_group_change.
663  *
664  * @param module The ldb module.
665  * @param request The request being logged.
666  * @param action Description of the action being performed.
667  * @param user The linearized form of the users DN
668  * @param status the LDB status code for the processing of the request.
669  *
670  */
671 static void log_membership_changes(struct ldb_module *module,
672                                    const struct ldb_request *request,
673                                    struct ldb_message_element *el,
674                                    struct ldb_message_element *old_el,
675                                    uint32_t group_type,
676                                    int status)
677 {
678         unsigned int i, old_i, new_i;
679         unsigned int old_num_values;
680         unsigned int max_num_values;
681         unsigned int new_num_values;
682         struct parsed_dn *old_val = NULL;
683         struct parsed_dn *new_val = NULL;
684         struct parsed_dn *new_values = NULL;
685         struct parsed_dn *old_values = NULL;
686         struct ldb_context *ldb = NULL;
687
688         TALLOC_CTX *ctx = talloc_new(NULL);
689
690         old_num_values = old_el ? old_el->num_values : 0;
691         new_num_values = el ? el->num_values : 0;
692         max_num_values = old_num_values + new_num_values;
693
694         if (max_num_values == 0) {
695                 /*
696                  * There is nothing to do!
697                  */
698                 TALLOC_FREE(ctx);
699                 return;
700         }
701
702         old_values = get_parsed_dns(ctx, old_el);
703         new_values = get_parsed_dns(ctx, el);
704         ldb = ldb_module_get_ctx(module);
705
706         old_i = 0;
707         new_i = 0;
708         for (i = 0; i < max_num_values; i++) {
709                 enum dn_compare_result cmp;
710                 if (old_i < old_num_values && new_i < new_num_values) {
711                         /*
712                          * Both list have values, so compare the values
713                          */
714                         old_val = &old_values[old_i];
715                         new_val = &new_values[new_i];
716                         cmp = dn_compare(ctx, ldb, old_val, new_val);
717                 } else if (old_i < old_num_values) {
718                         /*
719                          * the new list is empty, read the old list
720                          */
721                         old_val = &old_values[old_i];
722                         new_val = NULL;
723                         cmp = LESS_THAN;
724                 } else if (new_i < new_num_values) {
725                         /*
726                          * the old list is empty, read new list
727                          */
728                         old_val = NULL;
729                         new_val = &new_values[new_i];
730                         cmp = GREATER_THAN;
731                 } else {
732                         break;
733                 }
734
735                 if (cmp == LESS_THAN) {
736                         /*
737                          * Have an entry in the original record that is not in
738                          * the new record. So it's been deleted
739                          */
740                         const char *user = NULL;
741                         enum event_id_type event_id;
742                         if (old_val->dsdb_dn == NULL) {
743                                 really_parse_trusted_dn(
744                                         ctx,
745                                         ldb,
746                                         old_val,
747                                         LDB_SYNTAX_DN);
748                         }
749                         user = ldb_dn_get_linearized(old_val->dsdb_dn->dn);
750                         event_id = get_remove_member_event(group_type);
751                         log_membership_change(
752                             module, request, "Removed", user, event_id, status);
753                         old_i++;
754                 } else if (cmp == BINARY_EQUAL) {
755                         /*
756                          * DN's unchanged at binary level so nothing to do.
757                          */
758                         old_i++;
759                         new_i++;
760                 } else if (cmp == EQUAL) {
761                         /*
762                          * DN is unchanged now need to check the flags to
763                          * determine if a record has been deleted or undeleted
764                          */
765                         uint32_t old_flags;
766                         uint32_t new_flags;
767                         if (old_val->dsdb_dn == NULL) {
768                                 really_parse_trusted_dn(
769                                         ctx,
770                                         ldb,
771                                         old_val,
772                                         LDB_SYNTAX_DN);
773                         }
774                         if (new_val->dsdb_dn == NULL) {
775                                 really_parse_trusted_dn(
776                                         ctx,
777                                         ldb,
778                                         new_val,
779                                         LDB_SYNTAX_DN);
780                         }
781
782                         dsdb_get_extended_dn_uint32(
783                                 old_val->dsdb_dn->dn,
784                                 &old_flags,
785                                 "RMD_FLAGS");
786                         dsdb_get_extended_dn_uint32(
787                                 new_val->dsdb_dn->dn,
788                                 &new_flags,
789                                 "RMD_FLAGS");
790                         if (new_flags == old_flags) {
791                                 /*
792                                  * No changes to the Repl meta data so can
793                                  * no need to log the change
794                                  */
795                                 old_i++;
796                                 new_i++;
797                                 continue;
798                         }
799                         if (new_flags & DSDB_RMD_FLAG_DELETED) {
800                                 /*
801                                  * DN has been deleted.
802                                  */
803                                 const char *user = NULL;
804                                 enum event_id_type event_id;
805                                 user = ldb_dn_get_linearized(
806                                         old_val->dsdb_dn->dn);
807                                 event_id = get_remove_member_event(group_type);
808                                 log_membership_change(module,
809                                                       request,
810                                                       "Removed",
811                                                       user,
812                                                       event_id,
813                                                       status);
814                         } else {
815                                 /*
816                                  * DN has been re-added
817                                  */
818                                 const char *user = NULL;
819                                 enum event_id_type event_id;
820                                 user = ldb_dn_get_linearized(
821                                         new_val->dsdb_dn->dn);
822                                 event_id = get_add_member_event(group_type);
823                                 log_membership_change(module,
824                                                       request,
825                                                       "Added",
826                                                       user,
827                                                       event_id,
828                                                       status);
829                         }
830                         old_i++;
831                         new_i++;
832                 } else {
833                         /*
834                          * Member in the updated record that's not in the
835                          * original, so it must have been added.
836                          */
837                         const char *user = NULL;
838                         enum event_id_type event_id;
839                         if ( new_val->dsdb_dn == NULL) {
840                                 really_parse_trusted_dn(
841                                         ctx,
842                                         ldb,
843                                         new_val,
844                                         LDB_SYNTAX_DN);
845                         }
846                         user = ldb_dn_get_linearized(new_val->dsdb_dn->dn);
847                         event_id = get_add_member_event(group_type);
848                         log_membership_change(
849                             module, request, "Added", user, event_id, status);
850                         new_i++;
851                 }
852         }
853
854         TALLOC_FREE(ctx);
855 }
856
857 /*
858  * @brief log a group change message for a newly added user.
859  *
860  * When a user is added we need to generate a GroupChange Add message to
861  * log that the user has been added to their PrimaryGroup
862  */
863 static void log_new_user_added_to_primary_group(
864     TALLOC_CTX *ctx,
865     struct audit_callback_context *acc,
866     const char *group,
867     const int status)
868 {
869         uint32_t group_type;
870         enum event_id_type event_id = EVT_ID_NONE;
871         struct ldb_result *res = NULL;
872         struct ldb_dn *group_dn = NULL;
873         struct ldb_context *ldb = NULL;
874         int ret;
875
876         ldb = ldb_module_get_ctx(acc->module);
877         group_dn = ldb_dn_new(ctx, ldb, group);
878         ret = dsdb_module_search_dn(acc->module,
879                                     ctx,
880                                     &res,
881                                     group_dn,
882                                     group_type_attr,
883                                     DSDB_FLAG_NEXT_MODULE |
884                                         DSDB_SEARCH_REVEAL_INTERNALS |
885                                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
886                                     NULL);
887         if (ret == LDB_SUCCESS) {
888                 const char *user = NULL;
889                 group_type =
890                     ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
891                 event_id = get_add_member_event(group_type);
892                 user = dsdb_audit_get_primary_dn(acc->request);
893                 log_membership_change(
894                     acc->module, acc->request, "Added", user, event_id, status);
895         }
896 }
897
898 /*
899  * @brief Log the details of a primary group change.
900  *
901  * Retrieve the users primary groupo after the operation has completed
902  * and call log_primary_group_change to log the actual changes.
903  *
904  * @param acc details of the primary group before the operation.
905  * @param status The status code returned by the operation.
906  *
907  * @return an LDB status code.
908  */
909 static void log_user_primary_group_change(
910         struct audit_callback_context *acc,
911         const int status)
912 {
913         TALLOC_CTX *ctx = talloc_new(NULL);
914         uint32_t new_rid;
915         struct dom_sid *account_sid = NULL;
916         int ret;
917         const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
918
919         if (status == LDB_SUCCESS && msg != NULL) {
920                 struct ldb_result *res = NULL;
921                 ret = dsdb_module_search_dn(
922                         acc->module,
923                         ctx,
924                         &res,
925                         msg->dn,
926                         primary_group_attr,
927                         DSDB_FLAG_NEXT_MODULE |
928                         DSDB_SEARCH_REVEAL_INTERNALS |
929                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
930                         NULL);
931                 if (ret == LDB_SUCCESS) {
932                         new_rid = ldb_msg_find_attr_as_uint(
933                                 msg,
934                                 "primaryGroupID",
935                                 ~0);
936                         account_sid = samdb_result_dom_sid(
937                                 ctx,
938                                 res->msgs[0],
939                                 "objectSid");
940                 }
941         }
942         /*
943          * If we don't have a new value then the user has been deleted
944          * which we currently do not log.
945          * Otherwise only log if the primary group has actually changed.
946          */
947         if (account_sid != NULL &&
948             new_rid != ~0 &&
949             acc->primary_group != new_rid) {
950                 const char* group = get_primary_group_dn(
951                         ctx,
952                         acc->module,
953                         account_sid,
954                         new_rid);
955                 log_primary_group_change(
956                         acc->module,
957                         acc->request,
958                         "PrimaryGroup",
959                         group,
960                         status);
961                 /*
962                  * Are we adding a new user with the primaryGroupID
963                  * set. If so and we're generating JSON audit logs, will need to
964                  * generate an "Add" message with the appropriate windows
965                  * event id.
966                  */
967                 if (acc->request->operation == LDB_ADD) {
968                         log_new_user_added_to_primary_group(
969                             ctx, acc, group, status);
970                 }
971         }
972         TALLOC_FREE(ctx);
973 }
974
975 /*
976  * @brief log the changes to users group membership.
977  *
978  * Retrieve the users group memberships after the operation has completed
979  * and call log_membership_changes to log the actual changes.
980  *
981  * @param acc details of the group memberships before the operation.
982  * @param status The status code returned by the operation.
983  *
984  */
985 static void log_group_membership_changes(
986         struct audit_callback_context *acc,
987         const int status)
988 {
989         TALLOC_CTX *ctx = talloc_new(NULL);
990         struct ldb_message_element *new_val = NULL;
991         int ret;
992         uint32_t group_type = 0;
993         const struct ldb_message *msg = dsdb_audit_get_message(acc->request);
994         if (status == LDB_SUCCESS && msg != NULL) {
995                 struct ldb_result *res = NULL;
996                 ret = dsdb_module_search_dn(
997                         acc->module,
998                         ctx,
999                         &res,
1000                         msg->dn,
1001                         group_attrs,
1002                         DSDB_FLAG_NEXT_MODULE |
1003                         DSDB_SEARCH_REVEAL_INTERNALS |
1004                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1005                         NULL);
1006                 if (ret == LDB_SUCCESS) {
1007                         new_val = ldb_msg_find_element(res->msgs[0], "member");
1008                         group_type = ldb_msg_find_attr_as_uint(
1009                             res->msgs[0], "groupType", 0);
1010                         log_membership_changes(acc->module,
1011                                                acc->request,
1012                                                new_val,
1013                                                acc->members,
1014                                                group_type,
1015                                                status);
1016                         TALLOC_FREE(ctx);
1017                         return;
1018                 }
1019         }
1020         /*
1021          * If we get here either
1022          *   one of the lower level modules failed and the group record did
1023          *   not get updated
1024          * or
1025          *   the updated group record could not be read.
1026          *
1027          * In both cases it does not make sense to log individual membership
1028          * changes so we log a group membership change "Failure" message.
1029          *
1030          */
1031         log_membership_change(acc->module,
1032                               acc->request,
1033                               "Failure",
1034                               "",
1035                               EVT_ID_NONE,
1036                               status);
1037         TALLOC_FREE(ctx);
1038 }
1039
1040 /*
1041  * @brief call back function to log changes to the group memberships.
1042  *
1043  * Call back function to log changes to the uses broup memberships.
1044  *
1045  * @param req the ldb request.
1046  * @param ares the ldb result
1047  *
1048  * @return am LDB status code.
1049  */
1050 static int group_audit_callback(
1051         struct ldb_request *req,
1052         struct ldb_reply *ares)
1053 {
1054         struct audit_callback_context *ac = NULL;
1055
1056         ac = talloc_get_type(
1057                 req->context,
1058                 struct audit_callback_context);
1059
1060         if (!ares) {
1061                 return ldb_module_done(
1062                                 ac->request, NULL, NULL,
1063                                 LDB_ERR_OPERATIONS_ERROR);
1064         }
1065
1066         /* pass on to the callback */
1067         switch (ares->type) {
1068         case LDB_REPLY_ENTRY:
1069                 return ldb_module_send_entry(
1070                         ac->request,
1071                         ares->message,
1072                         ares->controls);
1073
1074         case LDB_REPLY_REFERRAL:
1075                 return ldb_module_send_referral(
1076                         ac->request,
1077                         ares->referral);
1078
1079         case LDB_REPLY_DONE:
1080                 /*
1081                  * Log on DONE now we have a result code
1082                  */
1083                 ac->log_changes(ac, ares->error);
1084                 return ldb_module_done(
1085                         ac->request,
1086                         ares->controls,
1087                         ares->response,
1088                         ares->error);
1089                 break;
1090
1091         default:
1092                 /* Can't happen */
1093                 return LDB_ERR_OPERATIONS_ERROR;
1094         }
1095 }
1096
1097 /*
1098  * @brief Does this request change the primary group.
1099  *
1100  * Does the request change the primary group, i.e. does it contain the
1101  * primaryGroupID attribute.
1102  *
1103  * @param req the request to examine.
1104  *
1105  * @return True if the request modifies the primary group.
1106  */
1107 static bool has_primary_group_id(struct ldb_request *req)
1108 {
1109         struct ldb_message_element *el = NULL;
1110         const struct ldb_message *msg = NULL;
1111
1112         msg = dsdb_audit_get_message(req);
1113         el = ldb_msg_find_element(msg, "primaryGroupID");
1114
1115         return (el != NULL);
1116 }
1117
1118 /*
1119  * @brief Does this request change group membership.
1120  *
1121  * Does the request change the ses group memberships, i.e. does it contain the
1122  * member attribute.
1123  *
1124  * @param req the request to examine.
1125  *
1126  * @return True if the request modifies the users group memberships.
1127  */
1128 static bool has_group_membership_changes(struct ldb_request *req)
1129 {
1130         struct ldb_message_element *el = NULL;
1131         const struct ldb_message *msg = NULL;
1132
1133         msg = dsdb_audit_get_message(req);
1134         el = ldb_msg_find_element(msg, "member");
1135
1136         return (el != NULL);
1137 }
1138
1139
1140
1141 /*
1142  * @brief Install the callback function to log an add request.
1143  *
1144  * Install the callback function to log an add request changing the users
1145  * group memberships. As we want to log the returned status code, we need to
1146  * register a callback function that will be called once the operation has
1147  * completed.
1148  *
1149  * This function reads the current user record so that we can log the before
1150  * and after state.
1151  *
1152  * @param module The ldb module.
1153  * @param req The modify request.
1154  *
1155  * @return and LDB status code.
1156  */
1157 static int set_group_membership_add_callback(
1158         struct ldb_module *module,
1159         struct ldb_request *req)
1160 {
1161         struct audit_callback_context *context = NULL;
1162         struct ldb_request *new_req = NULL;
1163         struct ldb_context *ldb = NULL;
1164         int ret;
1165         /*
1166          * Adding group memberships so will need to log the changes.
1167          */
1168         ldb = ldb_module_get_ctx(module);
1169         context = talloc_zero(req, struct audit_callback_context);
1170
1171         if (context == NULL) {
1172                 return ldb_oom(ldb);
1173         }
1174         context->request = req;
1175         context->module = module;
1176         context->log_changes = log_group_membership_changes;
1177         /*
1178          * We want to log the return code status, so we need to register
1179          * a callback function to get the actual result.
1180          * We need to take a new copy so that we don't alter the callers copy
1181          */
1182         ret = ldb_build_add_req(
1183                 &new_req,
1184                 ldb,
1185                 req,
1186                 req->op.add.message,
1187                 req->controls,
1188                 context,
1189                 group_audit_callback,
1190                 req);
1191         if (ret != LDB_SUCCESS) {
1192                 return ret;
1193         }
1194         return ldb_next_request(module, new_req);
1195 }
1196
1197
1198 /*
1199  * @brief Install the callback function to log a modify request.
1200  *
1201  * Install the callback function to log a modify request changing the primary
1202  * group . As we want to log the returned status code, we need to register a
1203  * callback function that will be called once the operation has completed.
1204  *
1205  * This function reads the current user record so that we can log the before
1206  * and after state.
1207  *
1208  * @param module The ldb module.
1209  * @param req The modify request.
1210  *
1211  * @return and LDB status code.
1212  */
1213 static int set_primary_group_modify_callback(
1214         struct ldb_module *module,
1215         struct ldb_request *req)
1216 {
1217         struct audit_callback_context *context = NULL;
1218         struct ldb_request *new_req = NULL;
1219         struct ldb_context *ldb = NULL;
1220         const struct ldb_message *msg = NULL;
1221         struct ldb_result *res = NULL;
1222         int ret;
1223
1224         TALLOC_CTX *ctx = talloc_new(NULL);
1225
1226         ldb = ldb_module_get_ctx(module);
1227
1228         context = talloc_zero(req, struct audit_callback_context);
1229         if (context == NULL) {
1230                 ret = ldb_oom(ldb);
1231                 goto exit;
1232         }
1233         context->request = req;
1234         context->module = module;
1235         context->log_changes = log_user_primary_group_change;
1236
1237         msg = dsdb_audit_get_message(req);
1238         ret = dsdb_module_search_dn(
1239                 module,
1240                 ctx,
1241                 &res,
1242                 msg->dn,
1243                 primary_group_attr,
1244                 DSDB_FLAG_NEXT_MODULE |
1245                 DSDB_SEARCH_REVEAL_INTERNALS |
1246                 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1247                 NULL);
1248         if (ret == LDB_SUCCESS) {
1249                 uint32_t pg;
1250                 pg = ldb_msg_find_attr_as_uint(
1251                         res->msgs[0],
1252                         "primaryGroupID",
1253                         ~0);
1254                 context->primary_group = pg;
1255         }
1256         /*
1257          * We want to log the return code status, so we need to register
1258          * a callback function to get the actual result.
1259          * We need to take a new copy so that we don't alter the callers copy
1260          */
1261         ret = ldb_build_mod_req(
1262                 &new_req,
1263                 ldb,
1264                 req,
1265                 req->op.add.message,
1266                 req->controls,
1267                 context,
1268                 group_audit_callback,
1269                 req);
1270         if (ret != LDB_SUCCESS) {
1271                 goto exit;
1272         }
1273         ret = ldb_next_request(module, new_req);
1274 exit:
1275         TALLOC_FREE(ctx);
1276         return ret;
1277 }
1278
1279 /*
1280  * @brief Install the callback function to log an add request.
1281  *
1282  * Install the callback function to log an add request changing the primary
1283  * group . As we want to log the returned status code, we need to register a
1284  * callback function that will be called once the operation has completed.
1285  *
1286  * This function reads the current user record so that we can log the before
1287  * and after state.
1288  *
1289  * @param module The ldb module.
1290  * @param req The modify request.
1291  *
1292  * @return and LDB status code.
1293  */
1294 static int set_primary_group_add_callback(
1295         struct ldb_module *module,
1296         struct ldb_request *req)
1297 {
1298         struct audit_callback_context *context = NULL;
1299         struct ldb_request *new_req = NULL;
1300         struct ldb_context *ldb = NULL;
1301         int ret;
1302         /*
1303          * Adding a user with a primary group.
1304          */
1305         ldb = ldb_module_get_ctx(module);
1306         context = talloc_zero(req, struct audit_callback_context);
1307
1308         if (context == NULL) {
1309                 return ldb_oom(ldb);
1310         }
1311         context->request = req;
1312         context->module = module;
1313         context->log_changes = log_user_primary_group_change;
1314         /*
1315          * We want to log the return code status, so we need to register
1316          * a callback function to get the actual result.
1317          * We need to take a new copy so that we don't alter the callers copy
1318          */
1319         ret = ldb_build_add_req(
1320                 &new_req,
1321                 ldb,
1322                 req,
1323                 req->op.add.message,
1324                 req->controls,
1325                 context,
1326                 group_audit_callback,
1327                 req);
1328         if (ret != LDB_SUCCESS) {
1329                 return ret;
1330         }
1331         return ldb_next_request(module, new_req);
1332 }
1333
1334 /*
1335  * @brief Module handler for add operations.
1336  *
1337  * Inspect the current add request, and if needed log any group membership
1338  * changes.
1339  *
1340  * @param module The ldb module.
1341  * @param req The modify request.
1342  *
1343  * @return and LDB status code.
1344  */
1345 static int group_add(
1346         struct ldb_module *module,
1347         struct ldb_request *req)
1348 {
1349
1350         struct audit_context *ac =
1351                 talloc_get_type(
1352                         ldb_module_get_private(module),
1353                         struct audit_context);
1354         /*
1355          * Currently we don't log replicated group changes
1356          */
1357         if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1358                 return ldb_next_request(module, req);
1359         }
1360
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)) {
1364                 /*
1365                  * Avoid the overheads of logging unless it has been
1366                  * enabled
1367                  */
1368                 if (has_group_membership_changes(req)) {
1369                         return set_group_membership_add_callback(module, req);
1370                 }
1371                 if (has_primary_group_id(req)) {
1372                         return set_primary_group_add_callback(module, req);
1373                 }
1374         }
1375         return ldb_next_request(module, req);
1376 }
1377
1378 /*
1379  * @brief Module handler for delete operations.
1380  *
1381  * Currently there is no logging for delete operations.
1382  *
1383  * @param module The ldb module.
1384  * @param req The modify request.
1385  *
1386  * @return and LDB status code.
1387  */
1388 static int group_delete(
1389         struct ldb_module *module,
1390         struct ldb_request *req)
1391 {
1392         return ldb_next_request(module, req);
1393 }
1394
1395 /*
1396  * @brief Install the callback function to log a modify request.
1397  *
1398  * Install the callback function to log a modify request. As we want to log the
1399  * returned status code, we need to register a callback function that will be
1400  * called once the operation has completed.
1401  *
1402  * This function reads the current user record so that we can log the before
1403  * and after state.
1404  *
1405  * @param module The ldb module.
1406  * @param req The modify request.
1407  *
1408  * @return and LDB status code.
1409  */
1410 static int set_group_modify_callback(
1411         struct ldb_module *module,
1412         struct ldb_request *req)
1413 {
1414         struct audit_callback_context *context = NULL;
1415         struct ldb_request *new_req = NULL;
1416         struct ldb_context *ldb = NULL;
1417         struct ldb_result *res = NULL;
1418         int ret;
1419
1420         ldb = ldb_module_get_ctx(module);
1421         context = talloc_zero(req, struct audit_callback_context);
1422
1423         if (context == NULL) {
1424                 return ldb_oom(ldb);
1425         }
1426         context->request = req;
1427         context->module  = module;
1428         context->log_changes = log_group_membership_changes;
1429
1430         /*
1431          * About to change the group memberships need to read
1432          * the current state from the database.
1433          */
1434         ret = dsdb_module_search_dn(
1435                 module,
1436                 context,
1437                 &res,
1438                 req->op.add.message->dn,
1439                 group_attrs,
1440                 DSDB_FLAG_NEXT_MODULE |
1441                 DSDB_SEARCH_REVEAL_INTERNALS |
1442                 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
1443                 NULL);
1444         if (ret == LDB_SUCCESS) {
1445                 context->members = ldb_msg_find_element(res->msgs[0], "member");
1446         }
1447
1448         ret = ldb_build_mod_req(
1449                 &new_req,
1450                 ldb,
1451                 req,
1452                 req->op.mod.message,
1453                 req->controls,
1454                 context,
1455                 group_audit_callback,
1456                 req);
1457         if (ret != LDB_SUCCESS) {
1458                 return ret;
1459         }
1460         return ldb_next_request(module, new_req);
1461 }
1462
1463 /*
1464  * @brief Module handler for modify operations.
1465  *
1466  * Inspect the current modify request, and if needed log any group membership
1467  * changes.
1468  *
1469  * @param module The ldb module.
1470  * @param req The modify request.
1471  *
1472  * @return and LDB status code.
1473  */
1474 static int group_modify(
1475         struct ldb_module *module,
1476         struct ldb_request *req)
1477 {
1478
1479         struct audit_context *ac =
1480                 talloc_get_type(
1481                         ldb_module_get_private(module),
1482                         struct audit_context);
1483         /*
1484          * Currently we don't log replicated group changes
1485          */
1486         if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1487                 return ldb_next_request(module, req);
1488         }
1489
1490         if (CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT, GROUP_LOG_LVL) ||
1491             CHECK_DEBUGLVLC(DBGC_DSDB_GROUP_AUDIT_JSON, GROUP_LOG_LVL) ||
1492                 (ac->msg_ctx && ac->send_events)) {
1493                 /*
1494                  * Avoid the overheads of logging unless it has been
1495                  * enabled
1496                  */
1497                 if (has_group_membership_changes(req)) {
1498                         return set_group_modify_callback(module, req);
1499                 }
1500                 if (has_primary_group_id(req)) {
1501                         return set_primary_group_modify_callback(module, req);
1502                 }
1503         }
1504         return ldb_next_request(module, req);
1505 }
1506
1507 /*
1508  * @brief ldb module initialisation
1509  *
1510  * Initialise the module, loading the private data etc.
1511  *
1512  * @param module The ldb module to initialise.
1513  *
1514  * @return An LDB status code.
1515  */
1516 static int group_init(struct ldb_module *module)
1517 {
1518
1519         struct ldb_context *ldb = ldb_module_get_ctx(module);
1520         struct audit_context *context = NULL;
1521         struct loadparm_context *lp_ctx
1522                 = talloc_get_type_abort(
1523                         ldb_get_opaque(ldb, "loadparm"),
1524                         struct loadparm_context);
1525         struct tevent_context *ev = ldb_get_event_context(ldb);
1526
1527         context = talloc_zero(module, struct audit_context);
1528         if (context == NULL) {
1529                 return ldb_module_oom(module);
1530         }
1531
1532         if (lp_ctx && lpcfg_dsdb_group_change_notification(lp_ctx)) {
1533                 context->send_events = true;
1534                 context->msg_ctx = imessaging_client_init(context,
1535                                                           lp_ctx,
1536                                                           ev);
1537         }
1538
1539         ldb_module_set_private(module, context);
1540         return ldb_next_init(module);
1541 }
1542
1543 static const struct ldb_module_ops ldb_group_audit_log_module_ops = {
1544         .name              = "group_audit_log",
1545         .add               = group_add,
1546         .modify            = group_modify,
1547         .del               = group_delete,
1548         .init_context      = group_init,
1549 };
1550
1551 int ldb_group_audit_log_module_init(const char *version)
1552 {
1553         LDB_MODULE_CHECK_VERSION(version);
1554         return ldb_register_module(&ldb_group_audit_log_module_ops);
1555 }