Line data Source code
1 : /*
2 : ldb database module utility 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 : * Common utility functions for SamDb audit logging.
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 "libcli/security/security_token.h"
33 : #include "auth/common_auth.h"
34 : #include "param/param.h"
35 : #include "dsdb/samdb/ldb_modules/audit_util_proto.h"
36 :
37 : #define MAX_LENGTH 1024
38 :
39 : #define min(a, b) (((a)>(b))?(b):(a))
40 :
41 : /*
42 : * List of attributes considered secret or confidential the values of these
43 : * attributes should not be displayed in log messages.
44 : */
45 : static const char * const secret_attributes[] = {
46 : DSDB_SECRET_ATTRIBUTES,
47 : NULL};
48 : /*
49 : * List of attributes that contain a password, used to detect password changes
50 : */
51 : static const char * const password_attributes[] = {
52 : DSDB_PASSWORD_ATTRIBUTES,
53 : NULL};
54 :
55 : /*
56 : * @brief Should the value of the specified value be redacted.
57 : *
58 : * The values of secret or password attributes should not be displayed.
59 : *
60 : * @param name The attributes name.
61 : *
62 : * @return True if the attribute should be redacted
63 : */
64 1745201 : bool dsdb_audit_redact_attribute(const char * name)
65 : {
66 :
67 1745201 : if (ldb_attr_in_list(secret_attributes, name)) {
68 12509 : return true;
69 : }
70 :
71 1732548 : if (ldb_attr_in_list(password_attributes, name)) {
72 2754 : return true;
73 : }
74 :
75 1647336 : return false;
76 : }
77 :
78 : /*
79 : * @brief is the attribute a password attribute?
80 : *
81 : * Is the attribute a password attribute.
82 : *
83 : * @return True if the attribute is a "Password" attribute.
84 : */
85 5602141 : bool dsdb_audit_is_password_attribute(const char * name)
86 : {
87 :
88 5602141 : bool is_password = ldb_attr_in_list(password_attributes, name);
89 5602141 : return is_password;
90 : }
91 :
92 : /*
93 : * @brief Get the remote address from the ldb context.
94 : *
95 : * The remote address is stored in the ldb opaque value "remoteAddress"
96 : * it is the responsibility of the higher level code to ensure that this
97 : * value is set.
98 : *
99 : * @param ldb the ldb_context.
100 : *
101 : * @return the remote address if known, otherwise NULL.
102 : */
103 418765 : const struct tsocket_address *dsdb_audit_get_remote_address(
104 : struct ldb_context *ldb)
105 : {
106 418765 : void *opaque_remote_address = NULL;
107 10193 : struct tsocket_address *remote_address;
108 :
109 418765 : opaque_remote_address = ldb_get_opaque(ldb,
110 : "remoteAddress");
111 418765 : if (opaque_remote_address == NULL) {
112 211176 : return NULL;
113 : }
114 :
115 198311 : remote_address = talloc_get_type(opaque_remote_address,
116 : struct tsocket_address);
117 198311 : return remote_address;
118 : }
119 :
120 : /*
121 : * @brief Get the actual user SID from ldb context.
122 : *
123 : * The actual user SID is stored in the ldb opaque value "networkSessionInfo"
124 : * it is the responsibility of the higher level code to ensure that this
125 : * value is set.
126 : *
127 : * @param ldb the ldb_context.
128 : *
129 : * @return the users actual sid.
130 : */
131 5301 : const struct dom_sid *dsdb_audit_get_actual_sid(struct ldb_context *ldb)
132 : {
133 5301 : void *opaque_session = NULL;
134 5301 : struct auth_session_info *session = NULL;
135 5301 : struct security_token *user_token = NULL;
136 :
137 5301 : opaque_session = ldb_get_opaque(ldb, DSDB_NETWORK_SESSION_INFO);
138 5301 : if (opaque_session == NULL) {
139 22 : return NULL;
140 : }
141 :
142 5277 : session = talloc_get_type(opaque_session, struct auth_session_info);
143 5277 : if (session == NULL) {
144 0 : return NULL;
145 : }
146 :
147 5277 : user_token = session->security_token;
148 5277 : if (user_token == NULL) {
149 0 : return NULL;
150 : }
151 5276 : return &user_token->sids[PRIMARY_USER_SID_INDEX];
152 : }
153 : /*
154 : * @brief get the ldb error string.
155 : *
156 : * Get the ldb error string if set, otherwise get the generic error code
157 : * for the status code.
158 : *
159 : * @param ldb the ldb_context.
160 : * @param status the ldb_status code.
161 : *
162 : * @return a string describing the error.
163 : */
164 3 : const char *dsdb_audit_get_ldb_error_string(
165 : struct ldb_module *module,
166 : int status)
167 : {
168 3 : struct ldb_context *ldb = ldb_module_get_ctx(module);
169 3 : const char *err_string = ldb_errstring(ldb);
170 :
171 3 : if (err_string == NULL) {
172 1 : return ldb_strerror(status);
173 : }
174 1 : return err_string;
175 : }
176 :
177 : /*
178 : * @brief get the SID of the user performing the operation.
179 : *
180 : * Get the SID of the user performing the operation.
181 : *
182 : * @param module the ldb_module.
183 : *
184 : * @return the SID of the currently logged on user.
185 : */
186 413470 : const struct dom_sid *dsdb_audit_get_user_sid(const struct ldb_module *module)
187 : {
188 413470 : struct security_token *user_token = NULL;
189 :
190 : /*
191 : * acl_user_token does not alter module so it's safe
192 : * to discard the const.
193 : */
194 413470 : user_token = acl_user_token(discard_const(module));
195 413470 : if (user_token == NULL) {
196 0 : return NULL;
197 : }
198 413463 : return &user_token->sids[PRIMARY_USER_SID_INDEX];
199 :
200 : }
201 :
202 : /*
203 : * @brief is operation being performed using the system session.
204 : *
205 : * Is the operation being performed using the system session.
206 : *
207 : * @param module the ldb_module.
208 : *
209 : * @return true if the operation is being performed using the system session.
210 : */
211 137716 : bool dsdb_audit_is_system_session(const struct ldb_module *module)
212 : {
213 137716 : struct security_token *user_token = NULL;
214 :
215 : /*
216 : * acl_user_token does not alter module and security_token_is_system
217 : * does not alter the security token so it's safe to discard the const.
218 : */
219 137716 : user_token = acl_user_token(discard_const(module));
220 137716 : if (user_token == NULL) {
221 0 : return false;
222 : }
223 137713 : return security_token_is_system(user_token);;
224 :
225 : }
226 :
227 : /*
228 : * @brief get the session identifier GUID
229 : *
230 : * Get the GUID that uniquely identifies the current authenticated session.
231 : *
232 : * @param module the ldb_module.
233 : *
234 : * @return the unique session GUID
235 : */
236 413461 : const struct GUID *dsdb_audit_get_unique_session_token(
237 : const struct ldb_module *module)
238 : {
239 413461 : struct ldb_context *ldb = ldb_module_get_ctx(discard_const(module));
240 10150 : struct auth_session_info *session_info
241 413461 : = (struct auth_session_info *)ldb_get_opaque(
242 : ldb,
243 : DSDB_SESSION_INFO);
244 413461 : if(!session_info) {
245 0 : return NULL;
246 : }
247 413457 : return &session_info->unique_session_token;
248 : }
249 :
250 : /*
251 : * @brief get the actual user session identifier
252 : *
253 : * Get the GUID that uniquely identifies the current authenticated session.
254 : * This is the session of the connected user, as it may differ from the
255 : * session the operation is being performed as, i.e. for operations performed
256 : * under the system session.
257 : *
258 : * @param context the ldb_context.
259 : *
260 : * @return the unique session GUID
261 : */
262 5297 : const struct GUID *dsdb_audit_get_actual_unique_session_token(
263 : struct ldb_context *ldb)
264 : {
265 36 : struct auth_session_info *session_info
266 5297 : = (struct auth_session_info *)ldb_get_opaque(
267 : ldb,
268 : DSDB_NETWORK_SESSION_INFO);
269 5297 : if(!session_info) {
270 22 : return NULL;
271 : }
272 5273 : return &session_info->unique_session_token;
273 : }
274 :
275 : /*
276 : * @brief Get a printable string value for the remote host address.
277 : *
278 : * Get a printable string representation of the remote host, for display in the
279 : * the audit logs.
280 : *
281 : * @param ldb the ldb context.
282 : * @param mem_ctx the talloc memory context that will own the returned string.
283 : *
284 : * @return A string representation of the remote host address or "Unknown"
285 : *
286 : */
287 9 : char *dsdb_audit_get_remote_host(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
288 : {
289 9 : const struct tsocket_address *remote_address;
290 9 : char* remote_host = NULL;
291 :
292 9 : remote_address = dsdb_audit_get_remote_address(ldb);
293 9 : if (remote_address == NULL) {
294 4 : remote_host = talloc_asprintf(mem_ctx, "Unknown");
295 4 : return remote_host;
296 : }
297 :
298 5 : remote_host = tsocket_address_string(remote_address, mem_ctx);
299 5 : return remote_host;
300 : }
301 :
302 : /*
303 : * @brief get a printable representation of the primary DN.
304 : *
305 : * Get a printable representation of the primary DN. The primary DN is the
306 : * DN of the object being added, deleted, modified or renamed.
307 : *
308 : * @param the ldb_request.
309 : *
310 : * @return a printable and linearized DN
311 : */
312 438966 : const char* dsdb_audit_get_primary_dn(const struct ldb_request *request)
313 : {
314 438966 : struct ldb_dn *dn = NULL;
315 438966 : switch (request->operation) {
316 236911 : case LDB_ADD:
317 236911 : if (request->op.add.message != NULL) {
318 236902 : dn = request->op.add.message->dn;
319 : }
320 228804 : break;
321 149950 : case LDB_MODIFY:
322 149950 : if (request->op.mod.message != NULL) {
323 149949 : dn = request->op.mod.message->dn;
324 : }
325 147892 : break;
326 52096 : case LDB_DELETE:
327 52096 : dn = request->op.del.dn;
328 52096 : break;
329 2 : case LDB_RENAME:
330 2 : dn = request->op.rename.olddn;
331 2 : break;
332 0 : default:
333 0 : dn = NULL;
334 0 : break;
335 : }
336 438949 : if (dn == NULL) {
337 0 : return NULL;
338 : }
339 438942 : return ldb_dn_get_linearized(dn);
340 : }
341 :
342 : /*
343 : * @brief Get the ldb_message from a request.
344 : *
345 : * Get the ldb_message for the request, returns NULL is there is no
346 : * associated ldb_message
347 : *
348 : * @param The request
349 : *
350 : * @return the message associated with this request, or NULL
351 : */
352 2723417 : const struct ldb_message *dsdb_audit_get_message(
353 : const struct ldb_request *request)
354 : {
355 2723417 : switch (request->operation) {
356 1074666 : case LDB_ADD:
357 1074666 : return request->op.add.message;
358 1486016 : case LDB_MODIFY:
359 1486016 : return request->op.mod.message;
360 162455 : default:
361 162455 : return NULL;
362 : }
363 : }
364 :
365 : /*
366 : * @brief get the secondary dn, i.e. the target dn for a rename.
367 : *
368 : * Get the secondary dn, i.e. the target for a rename. This is only applicable
369 : * got a rename operation, for the non rename operations this function returns
370 : * NULL.
371 : *
372 : * @param request the ldb_request.
373 : *
374 : * @return the secondary dn in a printable and linearized form.
375 : */
376 6 : const char *dsdb_audit_get_secondary_dn(const struct ldb_request *request)
377 : {
378 6 : switch (request->operation) {
379 2 : case LDB_RENAME:
380 2 : return ldb_dn_get_linearized(request->op.rename.newdn);
381 0 : default:
382 0 : return NULL;
383 : }
384 : }
385 :
386 : /*
387 : * @brief Map the request operation to a description.
388 : *
389 : * Get a description of the operation for logging
390 : *
391 : * @param request the ldb_request
392 : *
393 : * @return a string describing the operation, or "Unknown" if the operation
394 : * is not known.
395 : */
396 352533 : const char *dsdb_audit_get_operation_name(const struct ldb_request *request)
397 : {
398 352533 : switch (request->operation) {
399 0 : case LDB_SEARCH:
400 0 : return "Search";
401 170363 : case LDB_ADD:
402 170363 : return "Add";
403 130067 : case LDB_MODIFY:
404 130067 : return "Modify";
405 52095 : case LDB_DELETE:
406 52095 : return "Delete";
407 1 : case LDB_RENAME:
408 1 : return "Rename";
409 1 : case LDB_EXTENDED:
410 1 : return "Extended";
411 1 : case LDB_REQ_REGISTER_CONTROL:
412 1 : return "Register Control";
413 1 : case LDB_REQ_REGISTER_PARTITION:
414 1 : return "Register Partition";
415 1 : default:
416 1 : return "Unknown";
417 : }
418 : }
419 :
420 : /*
421 : * @brief get a description of a modify action for logging.
422 : *
423 : * Get a brief description of the modification action suitable for logging.
424 : *
425 : * @param flags the ldb_attributes flags.
426 : *
427 : * @return a brief description, or "unknown".
428 : */
429 225511 : const char *dsdb_audit_get_modification_action(unsigned int flags)
430 : {
431 225511 : switch (LDB_FLAG_MOD_TYPE(flags)) {
432 68963 : case LDB_FLAG_MOD_ADD:
433 68963 : return "add";
434 51875 : case LDB_FLAG_MOD_DELETE:
435 51875 : return "delete";
436 104541 : case LDB_FLAG_MOD_REPLACE:
437 104541 : return "replace";
438 7 : default:
439 7 : return "unknown";
440 : }
441 : }
442 :
443 : /*
444 : * @brief Add an ldb_value to a json object array
445 : *
446 : * Convert the current ldb_value to a JSON object and append it to array.
447 : * {
448 : * "value":"xxxxxxxx",
449 : * "base64":true
450 : * "truncated":true
451 : * }
452 : *
453 : * value is the JSON string representation of the ldb_val,
454 : * will be null if the value is zero length. The value will be
455 : * truncated if it is more than MAX_LENGTH bytes long. It will also
456 : * be base64 encoded if it contains any non printable characters.
457 : *
458 : * base64 Indicates that the value is base64 encoded, will be absent if the
459 : * value is not encoded.
460 : *
461 : * truncated Indicates that the length of the value exceeded MAX_LENGTH and was
462 : * truncated. Note that values are truncated and then base64 encoded.
463 : * so an encoded value can be longer than MAX_LENGTH.
464 : *
465 : * @param array the JSON array to append the value to.
466 : * @param lv the ldb_val to convert and append to the array.
467 : *
468 : */
469 2394282 : static int dsdb_audit_add_ldb_value(struct json_object *array,
470 : const struct ldb_val lv)
471 : {
472 117940 : bool base64;
473 117940 : int len;
474 2394282 : struct json_object value = json_empty_object;
475 2394282 : int rc = 0;
476 :
477 2394282 : json_assert_is_array(array);
478 2394282 : if (json_is_invalid(array)) {
479 0 : return -1;
480 : }
481 :
482 2394281 : if (lv.length == 0 || lv.data == NULL) {
483 3 : rc = json_add_object(array, NULL, NULL);
484 3 : if (rc != 0) {
485 0 : goto failure;
486 : }
487 0 : return 0;
488 : }
489 :
490 2394278 : base64 = ldb_should_b64_encode(NULL, &lv);
491 2394278 : len = min(lv.length, MAX_LENGTH);
492 2394278 : value = json_new_object();
493 2394278 : if (json_is_invalid(&value)) {
494 0 : goto failure;
495 : }
496 :
497 2394278 : if (lv.length > MAX_LENGTH) {
498 5555 : rc = json_add_bool(&value, "truncated", true);
499 5555 : if (rc != 0) {
500 0 : goto failure;
501 : }
502 : }
503 2394278 : if (base64) {
504 365970 : TALLOC_CTX *ctx = talloc_new(NULL);
505 384351 : char *encoded = ldb_base64_encode(
506 : ctx,
507 365970 : (char*) lv.data,
508 : len);
509 :
510 365970 : if (ctx == NULL) {
511 0 : goto failure;
512 : }
513 :
514 365970 : rc = json_add_bool(&value, "base64", true);
515 365970 : if (rc != 0) {
516 0 : TALLOC_FREE(ctx);
517 0 : goto failure;
518 : }
519 365970 : rc = json_add_string(&value, "value", encoded);
520 365970 : if (rc != 0) {
521 0 : TALLOC_FREE(ctx);
522 0 : goto failure;
523 : }
524 365970 : TALLOC_FREE(ctx);
525 : } else {
526 2028308 : rc = json_add_stringn(&value, "value", (char *)lv.data, len);
527 2028308 : if (rc != 0) {
528 0 : goto failure;
529 : }
530 : }
531 : /*
532 : * As array is a JSON array the element name is NULL
533 : */
534 2394278 : rc = json_add_object(array, NULL, &value);
535 2394278 : if (rc != 0) {
536 0 : goto failure;
537 : }
538 2276342 : return 0;
539 0 : failure:
540 : /*
541 : * In the event of a failure value will not have been added to array
542 : * so it needs to be freed to prevent a leak.
543 : */
544 0 : json_free(&value);
545 0 : DBG_ERR("unable to add ldb value to JSON audit message\n");
546 0 : return -1;
547 : }
548 :
549 : /*
550 : * @brief Build a JSON object containing the attributes in an ldb_message.
551 : *
552 : * Build a JSON object containing all the attributes in an ldb_message.
553 : * The attributes are keyed by attribute name, the values of "secret attributes"
554 : * are suppressed.
555 : *
556 : * {
557 : * "password":{
558 : * "redacted":true,
559 : * "action":"delete"
560 : * },
561 : * "name":{
562 : * "values": [
563 : * {
564 : * "value":"xxxxxxxx",
565 : * "base64":true
566 : * "truncated":true
567 : * },
568 : * ],
569 : * "action":"add",
570 : * }
571 : * }
572 : *
573 : * values is an array of json objects generated by add_ldb_value.
574 : * redacted indicates that the attribute is secret.
575 : * action is only set for modification operations.
576 : *
577 : * @param operation the ldb operation being performed
578 : * @param message the ldb_message to process.
579 : *
580 : * @return A populated json object.
581 : *
582 : */
583 300431 : struct json_object dsdb_audit_attributes_json(
584 : enum ldb_request_type operation,
585 : const struct ldb_message* message)
586 : {
587 :
588 9645 : unsigned int i, j;
589 300431 : struct json_object attributes = json_new_object();
590 :
591 300431 : if (json_is_invalid(&attributes)) {
592 0 : goto failure;
593 : }
594 2045617 : for (i=0;i<message->num_elements;i++) {
595 1745186 : struct json_object actions = json_empty_object;
596 1745186 : struct json_object attribute = json_empty_object;
597 1745186 : struct json_object action = json_empty_object;
598 1745186 : const char *name = message->elements[i].name;
599 1745186 : int rc = 0;
600 :
601 1745186 : action = json_new_object();
602 1745186 : if (json_is_invalid(&action)) {
603 0 : goto failure;
604 : }
605 :
606 : /*
607 : * If this is a modify operation tag the attribute with
608 : * the modification action.
609 : */
610 1745186 : if (operation == LDB_MODIFY) {
611 225503 : const char *act = NULL;
612 225503 : const int flags = message->elements[i].flags;
613 225503 : act = dsdb_audit_get_modification_action(flags);
614 225503 : rc = json_add_string(&action, "action", act);
615 225503 : if (rc != 0) {
616 0 : json_free(&action);
617 0 : goto failure;
618 : }
619 : }
620 1745186 : if (operation == LDB_ADD) {
621 1519683 : rc = json_add_string(&action, "action", "add");
622 1519683 : if (rc != 0) {
623 0 : json_free(&action);
624 0 : goto failure;
625 : }
626 : }
627 :
628 : /*
629 : * If the attribute is a secret attribute, tag it as redacted
630 : * and don't include the values
631 : */
632 1745186 : if (dsdb_audit_redact_attribute(name)) {
633 15401 : rc = json_add_bool(&action, "redacted", true);
634 15401 : if (rc != 0) {
635 0 : json_free(&action);
636 0 : goto failure;
637 : }
638 : } else {
639 82449 : struct json_object values;
640 : /*
641 : * Add the values for the action
642 : */
643 1729785 : values = json_new_array();
644 1729785 : if (json_is_invalid(&values)) {
645 0 : json_free(&action);
646 0 : goto failure;
647 : }
648 :
649 4124057 : for (j=0;j<message->elements[i].num_values;j++) {
650 2512202 : rc = dsdb_audit_add_ldb_value(
651 2394272 : &values, message->elements[i].values[j]);
652 2394272 : if (rc != 0) {
653 0 : json_free(&values);
654 0 : json_free(&action);
655 0 : goto failure;
656 : }
657 : }
658 1729785 : rc = json_add_object(&action, "values", &values);
659 1729785 : if (rc != 0) {
660 0 : json_free(&values);
661 0 : json_free(&action);
662 0 : goto failure;
663 : }
664 : }
665 1745186 : attribute = json_get_object(&attributes, name);
666 1745186 : if (json_is_invalid(&attribute)) {
667 0 : json_free(&action);
668 0 : goto failure;
669 : }
670 1745186 : actions = json_get_array(&attribute, "actions");
671 1745186 : if (json_is_invalid(&actions)) {
672 0 : json_free(&action);
673 0 : goto failure;
674 : }
675 1745186 : rc = json_add_object(&actions, NULL, &action);
676 1745186 : if (rc != 0) {
677 0 : json_free(&action);
678 0 : goto failure;
679 : }
680 1745186 : rc = json_add_object(&attribute, "actions", &actions);
681 1745186 : if (rc != 0) {
682 0 : json_free(&actions);
683 0 : goto failure;
684 : }
685 1745186 : rc = json_add_object(&attributes, name, &attribute);
686 1745186 : if (rc != 0) {
687 0 : json_free(&attribute);
688 0 : goto failure;
689 : }
690 : }
691 300431 : return attributes;
692 0 : failure:
693 0 : json_free(&attributes);
694 0 : DBG_ERR("Unable to create ldb attributes JSON audit message\n");
695 0 : return attributes;
696 : }
|