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