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