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