2 Unit tests for the dsdb group auditing code in group_audit.c
4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
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.
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.
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/>.
26 int ldb_group_audit_log_module_init(const char *version);
27 #include "../group_audit.c"
29 #include "lib/ldb/include/ldb_private.h"
33 * Mock version of dsdb_search_one
35 struct ldb_dn *g_basedn = NULL;
36 enum ldb_scope g_scope;
37 const char * const *g_attrs = NULL;
38 uint32_t g_dsdb_flags;
39 const char *g_exp_fmt;
40 const char *g_dn = NULL;
41 int g_status = LDB_SUCCESS;
43 int dsdb_search_one(struct ldb_context *ldb,
45 struct ldb_message **msg,
46 struct ldb_dn *basedn,
48 const char * const *attrs,
50 const char *exp_fmt, ...) _PRINTF_ATTRIBUTE(8, 9)
52 struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb, g_dn);
53 struct ldb_message *m = talloc_zero(mem_ctx, struct ldb_message);
60 g_dsdb_flags = dsdb_flags;
67 * Test helper to check ISO 8601 timestamps for validity
69 static void check_timestamp(time_t before, const char *timestamp)
82 * Convert the ISO 8601 timestamp into a time_t
83 * Note for convenience we ignore the value of the microsecond
84 * part of the time stamp.
88 "%4d-%2d-%2dT%2d:%2d:%2d.%6d%1c%4d",
98 assert_int_equal(9, rc);
99 tm.tm_year = tm.tm_year - 1900;
100 tm.tm_mon = tm.tm_mon - 1;
102 actual = mktime(&tm);
105 * The timestamp should be before <= actual <= after
107 assert_true(difftime(actual, before) >= 0);
108 assert_true(difftime(after, actual) >= 0);
112 * Test helper to validate a version object.
114 static void check_version(struct json_t *version, int major, int minor)
116 struct json_t *v = NULL;
118 assert_true(json_is_object(version));
119 assert_int_equal(2, json_object_size(version));
121 v = json_object_get(version, "major");
123 assert_int_equal(major, json_integer_value(v));
125 v = json_object_get(version, "minor");
127 assert_int_equal(minor, json_integer_value(v));
131 * Test helper to insert a transaction_id into a request.
133 static void add_transaction_id(struct ldb_request *req, const char *id)
136 struct dsdb_control_transaction_identifier *transaction_id = NULL;
138 transaction_id = talloc_zero(
140 struct dsdb_control_transaction_identifier);
141 assert_non_null(transaction_id);
142 GUID_from_string(id, &guid);
143 transaction_id->transaction_guid = guid;
144 ldb_request_add_control(
146 DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID,
152 * Test helper to add a session id and user SID
154 static void add_session_data(
156 struct ldb_context *ldb,
158 const char *user_sid)
160 struct auth_session_info *sess = NULL;
161 struct security_token *token = NULL;
162 struct dom_sid *sid = NULL;
163 struct GUID session_id;
166 sess = talloc_zero(ctx, struct auth_session_info);
167 token = talloc_zero(ctx, struct security_token);
168 sid = talloc_zero(ctx, struct dom_sid);
169 ok = string_to_sid(sid, user_sid);
172 sess->security_token = token;
173 GUID_from_string(session, &session_id);
174 sess->unique_session_token = session_id;
175 ldb_set_opaque(ldb, DSDB_SESSION_INFO, sess);
178 static void test_get_transaction_id(void **state)
180 struct ldb_request *req = NULL;
182 const char * const ID = "7130cb06-2062-6a1b-409e-3514c26b1773";
183 char *guid_str = NULL;
184 struct GUID_txt_buf guid_buff;
187 TALLOC_CTX *ctx = talloc_new(NULL);
191 * No transaction id, should return a zero guid
193 req = talloc_zero(ctx, struct ldb_request);
194 guid = get_transaction_id(req);
199 * And now test with the transaction_id set
201 req = talloc_zero(ctx, struct ldb_request);
202 assert_non_null(req);
203 add_transaction_id(req, ID);
205 guid = get_transaction_id(req);
206 guid_str = GUID_buf_string(guid, &guid_buff);
207 assert_string_equal(ID, guid_str);
213 static void test_audit_group_hr(void **state)
215 struct ldb_context *ldb = NULL;
216 struct ldb_module *module = NULL;
217 struct ldb_request *req = NULL;
219 struct tsocket_address *ts = NULL;
221 const char *const SID = "S-1-5-21-2470180966-3899876309-2637894779";
222 const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
224 struct GUID transaction_id;
225 const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
229 const char *rs = NULL;
234 TALLOC_CTX *ctx = talloc_new(NULL);
236 ldb = ldb_init(ctx, NULL);
238 GUID_from_string(TRANSACTION, &transaction_id);
240 module = talloc_zero(ctx, struct ldb_module);
243 tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
244 ldb_set_opaque(ldb, "remoteAddress", ts);
246 add_session_data(ctx, ldb, SESSION, SID);
248 req = talloc_zero(ctx, struct ldb_request);
249 req->operation = LDB_ADD;
250 add_transaction_id(req, TRANSACTION);
252 line = audit_group_human_readable(
259 LDB_ERR_OPERATIONS_ERROR);
260 assert_non_null(line);
262 rs = "\\[the-action\\] at \\["
264 "\\] status \\[Operations error\\] "
265 "Remote host \\[ipv4:127.0.0.1:0\\] "
266 "SID \\[S-1-5-21-2470180966-3899876309-2637894779\\] "
267 "Group \\[the-group-name\\] "
268 "User \\[the-user-name\\]";
270 ret = regcomp(®ex, rs, 0);
271 assert_int_equal(0, ret);
273 ret = regexec(®ex, line, 0, NULL, 0);
274 assert_int_equal(0, ret);
282 * test get_parsed_dns
283 * For this test we assume Valgrind or Address Sanitizer will detect any over
284 * runs. Also we don't care that the values are DN's only that the value in the
285 * element is copied to the parsed_dns.
287 static void test_get_parsed_dns(void **state)
289 struct ldb_message_element *el = NULL;
290 struct parsed_dn *dns = NULL;
292 TALLOC_CTX *ctx = talloc_new(NULL);
294 el = talloc_zero(ctx, struct ldb_message_element);
297 * empty element, zero dns
299 dns = get_parsed_dns(ctx, el);
306 el->values = talloc_zero_array(ctx, DATA_BLOB, 1);
307 el->values[0] = data_blob_string_const("The first value");
309 dns = get_parsed_dns(ctx, el);
311 assert_ptr_equal(el->values[0].data, dns[0].v->data);
312 assert_int_equal(el->values[0].length, dns[0].v->length);
321 el = talloc_zero(ctx, struct ldb_message_element);
323 el->values = talloc_zero_array(ctx, DATA_BLOB, 2);
324 el->values[0] = data_blob_string_const("The first value");
325 el->values[0] = data_blob_string_const("The second value");
327 dns = get_parsed_dns(ctx, el);
329 assert_ptr_equal(el->values[0].data, dns[0].v->data);
330 assert_int_equal(el->values[0].length, dns[0].v->length);
332 assert_ptr_equal(el->values[1].data, dns[1].v->data);
333 assert_int_equal(el->values[1].length, dns[1].v->length);
338 static void test_dn_compare(void **state)
341 struct ldb_context *ldb = NULL;
350 TALLOC_CTX *ctx = talloc_new(NULL);
351 const struct GUID *ZERO_GUID = talloc_zero(ctx, struct GUID);
353 ldb = ldb_init(ctx, NULL);
354 ldb_register_samba_handlers(ldb);
358 * Identical binary DN's
360 ab = data_blob_string_const(
361 "<GUID=fbee08fd-6f75-4bd4-af3f-e4f063a6379e>;"
362 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
363 a = talloc_zero(ctx, struct parsed_dn);
366 bb = data_blob_string_const(
367 "<GUID=fbee08fd-6f75-4bd4-af3f-e4f063a6379e>;"
368 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
369 b = talloc_zero(ctx, struct parsed_dn);
372 res = dn_compare(ctx, ldb, a, b);
373 assert_int_equal(BINARY_EQUAL, res);
375 * DN's should not have been parsed
377 assert_null(a->dsdb_dn);
378 assert_memory_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
379 assert_null(b->dsdb_dn);
380 assert_memory_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
386 * differing binary DN's but equal GUID's
388 ab = data_blob_string_const(
389 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651e>;"
390 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=com");
391 a = talloc_zero(ctx, struct parsed_dn);
394 bb = data_blob_string_const(
395 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651e>;"
396 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
397 b = talloc_zero(ctx, struct parsed_dn);
400 res = dn_compare(ctx, ldb, a, b);
401 assert_int_equal(EQUAL, res);
403 * DN's should have been parsed
405 assert_non_null(a->dsdb_dn);
406 assert_memory_not_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
407 assert_non_null(b->dsdb_dn);
408 assert_memory_not_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
414 * differing binary DN's but and second guid greater
416 ab = data_blob_string_const(
417 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651d>;"
418 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=com");
419 a = talloc_zero(ctx, struct parsed_dn);
422 bb = data_blob_string_const(
423 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651e>;"
424 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
425 b = talloc_zero(ctx, struct parsed_dn);
428 res = dn_compare(ctx, ldb, a, b);
429 assert_int_equal(GREATER_THAN, res);
431 * DN's should have been parsed
433 assert_non_null(a->dsdb_dn);
434 assert_memory_not_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
435 assert_non_null(b->dsdb_dn);
436 assert_memory_not_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
442 * differing binary DN's but and second guid less
444 ab = data_blob_string_const(
445 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651d>;"
446 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=com");
447 a = talloc_zero(ctx, struct parsed_dn);
450 bb = data_blob_string_const(
451 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651c>;"
452 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
453 b = talloc_zero(ctx, struct parsed_dn);
456 res = dn_compare(ctx, ldb, a, b);
457 assert_int_equal(LESS_THAN, res);
459 * DN's should have been parsed
461 assert_non_null(a->dsdb_dn);
462 assert_memory_not_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
463 assert_non_null(b->dsdb_dn);
464 assert_memory_not_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
472 static void test_get_primary_group_dn(void **state)
475 struct ldb_context *ldb = NULL;
476 struct ldb_module *module = NULL;
477 const uint32_t RID = 71;
479 const char *SID = "S-1-5-21-2470180966-3899876309-2637894779";
480 const char *DN = "OU=Things,DC=ad,DC=testing,DC=samba,DC=org";
483 TALLOC_CTX *ctx = talloc_new(NULL);
485 ldb = ldb_init(ctx, NULL);
486 ldb_register_samba_handlers(ldb);
488 module = talloc_zero(ctx, struct ldb_module);
492 * Pass an empty dom sid this will cause dom_sid_split_rid to fail;
493 * assign to sid.num_auths to suppress a valgrind warning.
496 dn = get_primary_group_dn(ctx, module, &sid, RID);
502 assert_true(string_to_sid(&sid, SID));
504 dn = get_primary_group_dn(ctx, module, &sid, RID);
506 assert_string_equal(DN, dn);
507 assert_int_equal(LDB_SCOPE_BASE, g_scope);
508 assert_int_equal(0, g_dsdb_flags);
509 assert_null(g_attrs);
510 assert_null(g_exp_fmt);
512 ("<SID=S-1-5-21-2470180966-3899876309-71>",
513 ldb_dn_get_extended_linearized(ctx, g_basedn, 1));
516 * Test dsdb search failure
518 g_status = LDB_ERR_NO_SUCH_OBJECT;
519 dn = get_primary_group_dn(ctx, module, &sid, RID);
526 static void test_audit_group_json(void **state)
528 struct ldb_context *ldb = NULL;
529 struct ldb_module *module = NULL;
530 struct ldb_request *req = NULL;
532 struct tsocket_address *ts = NULL;
534 const char *const SID = "S-1-5-21-2470180966-3899876309-2637894779";
535 const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
537 struct GUID transaction_id;
538 const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
541 struct json_object json;
542 json_t *audit = NULL;
548 TALLOC_CTX *ctx = talloc_new(NULL);
550 ldb = ldb_init(ctx, NULL);
552 GUID_from_string(TRANSACTION, &transaction_id);
554 module = talloc_zero(ctx, struct ldb_module);
557 tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
558 ldb_set_opaque(ldb, "remoteAddress", ts);
560 add_session_data(ctx, ldb, SESSION, SID);
562 req = talloc_zero(ctx, struct ldb_request);
563 req->operation = LDB_ADD;
564 add_transaction_id(req, TRANSACTION);
567 json = audit_group_json(
573 LDB_ERR_OPERATIONS_ERROR);
574 assert_int_equal(3, json_object_size(json.root));
576 v = json_object_get(json.root, "type");
578 assert_string_equal("groupChange", json_string_value(v));
580 v = json_object_get(json.root, "timestamp");
582 assert_true(json_is_string(v));
583 check_timestamp(before, json_string_value(v));
585 audit = json_object_get(json.root, "groupChange");
586 assert_non_null(audit);
587 assert_true(json_is_object(audit));
588 assert_int_equal(10, json_object_size(audit));
590 o = json_object_get(audit, "version");
592 check_version(o, AUDIT_MAJOR, AUDIT_MINOR);
594 v = json_object_get(audit, "statusCode");
596 assert_true(json_is_integer(v));
597 assert_int_equal(LDB_ERR_OPERATIONS_ERROR, json_integer_value(v));
599 v = json_object_get(audit, "status");
601 assert_true(json_is_string(v));
602 assert_string_equal("Operations error", json_string_value(v));
604 v = json_object_get(audit, "user");
606 assert_true(json_is_string(v));
607 assert_string_equal("the-user-name", json_string_value(v));
609 v = json_object_get(audit, "group");
611 assert_true(json_is_string(v));
612 assert_string_equal("the-group-name", json_string_value(v));
614 v = json_object_get(audit, "action");
616 assert_true(json_is_string(v));
617 assert_string_equal("the-action", json_string_value(v));
625 * Note: to run under valgrind us:
626 * valgrind --suppressions=test_group_audit.valgrind bin/test_group_audit
627 * This suppresses the errors generated because the ldb_modules are not
632 const struct CMUnitTest tests[] = {
633 cmocka_unit_test(test_audit_group_json),
634 cmocka_unit_test(test_get_transaction_id),
635 cmocka_unit_test(test_audit_group_hr),
636 cmocka_unit_test(test_get_parsed_dns),
637 cmocka_unit_test(test_dn_compare),
638 cmocka_unit_test(test_get_primary_group_dn),
642 cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
643 return cmocka_run_group_tests(tests, NULL, NULL);