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