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