dsdb group_audit_test: Remove redundant mocking code
[samba.git] / source4 / dsdb / samdb / ldb_modules / tests / test_group_audit.c
1 /*
2    Unit tests for the dsdb group auditing code in group_audit.c
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 #include <stdarg.h>
21 #include <stddef.h>
22 #include <setjmp.h>
23 #include <unistd.h>
24 #include <cmocka.h>
25
26 int ldb_group_audit_log_module_init(const char *version);
27 #include "../group_audit.c"
28
29 #include "lib/ldb/include/ldb_private.h"
30 #include <regex.h>
31
32 /*
33  * Mock version of dsdb_search_one
34  */
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;
42
43 int dsdb_search_one(struct ldb_context *ldb,
44                     TALLOC_CTX *mem_ctx,
45                     struct ldb_message **msg,
46                     struct ldb_dn *basedn,
47                     enum ldb_scope scope,
48                     const char * const *attrs,
49                     uint32_t dsdb_flags,
50                     const char *exp_fmt, ...) _PRINTF_ATTRIBUTE(8, 9)
51 {
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);
54         m->dn = dn;
55         *msg = m;
56
57         g_basedn = basedn;
58         g_scope = scope;
59         g_attrs = attrs;
60         g_dsdb_flags = dsdb_flags;
61         g_exp_fmt = exp_fmt;
62
63         return g_status;
64 }
65
66 /*
67  * Test helper to check ISO 8601 timestamps for validity
68  */
69 static void check_timestamp(time_t before, const char *timestamp)
70 {
71         int rc;
72         int usec, tz;
73         char c[2];
74         struct tm tm;
75         time_t after;
76         time_t actual;
77
78
79         after = time(NULL);
80
81         /*
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.
85          */
86         rc = sscanf(
87                 timestamp,
88                 "%4d-%2d-%2dT%2d:%2d:%2d.%6d%1c%4d",
89                 &tm.tm_year,
90                 &tm.tm_mon,
91                 &tm.tm_mday,
92                 &tm.tm_hour,
93                 &tm.tm_min,
94                 &tm.tm_sec,
95                 &usec,
96                 c,
97                 &tz);
98         assert_int_equal(9, rc);
99         tm.tm_year = tm.tm_year - 1900;
100         tm.tm_mon = tm.tm_mon - 1;
101         tm.tm_isdst = -1;
102         actual = mktime(&tm);
103
104         /*
105          * The timestamp should be before <= actual <= after
106          */
107         assert_true(difftime(actual, before) >= 0);
108         assert_true(difftime(after, actual) >= 0);
109 }
110
111 /*
112  * Test helper to validate a version object.
113  */
114 static void check_version(struct json_t *version, int major, int minor)
115 {
116         struct json_t *v = NULL;
117
118         assert_true(json_is_object(version));
119         assert_int_equal(2, json_object_size(version));
120
121         v = json_object_get(version, "major");
122         assert_non_null(v);
123         assert_int_equal(major, json_integer_value(v));
124
125         v = json_object_get(version, "minor");
126         assert_non_null(v);
127         assert_int_equal(minor, json_integer_value(v));
128 }
129
130 /*
131  * Test helper to insert a transaction_id into a request.
132  */
133 static void add_transaction_id(struct ldb_request *req, const char *id)
134 {
135         struct GUID guid;
136         struct dsdb_control_transaction_identifier *transaction_id = NULL;
137
138         transaction_id = talloc_zero(
139                 req,
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(
145                 req,
146                 DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID,
147                 false,
148                 transaction_id);
149 }
150
151 /*
152  * Test helper to add a session id and user SID
153  */
154 static void add_session_data(
155         TALLOC_CTX *ctx,
156         struct ldb_context *ldb,
157         const char *session,
158         const char *user_sid)
159 {
160         struct auth_session_info *sess = NULL;
161         struct security_token *token = NULL;
162         struct dom_sid *sid = NULL;
163         struct GUID session_id;
164         bool ok;
165
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);
170         assert_true(ok);
171         token->sids = 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);
176 }
177
178 static void test_get_transaction_id(void **state)
179 {
180         struct ldb_request *req = NULL;
181         struct GUID *guid;
182         const char * const ID = "7130cb06-2062-6a1b-409e-3514c26b1773";
183         char *guid_str = NULL;
184         struct GUID_txt_buf guid_buff;
185
186
187         TALLOC_CTX *ctx = talloc_new(NULL);
188
189
190         /*
191          * No transaction id, should return a zero guid
192          */
193         req = talloc_zero(ctx, struct ldb_request);
194         guid = get_transaction_id(req);
195         assert_null(guid);
196         TALLOC_FREE(req);
197
198         /*
199          * And now test with the transaction_id set
200          */
201         req = talloc_zero(ctx, struct ldb_request);
202         assert_non_null(req);
203         add_transaction_id(req, ID);
204
205         guid = get_transaction_id(req);
206         guid_str = GUID_buf_string(guid, &guid_buff);
207         assert_string_equal(ID, guid_str);
208         TALLOC_FREE(req);
209
210         TALLOC_FREE(ctx);
211 }
212
213 static void test_audit_group_hr(void **state)
214 {
215         struct ldb_context *ldb = NULL;
216         struct ldb_module  *module = NULL;
217         struct ldb_request *req = NULL;
218
219         struct tsocket_address *ts = NULL;
220
221         const char *const SID = "S-1-5-21-2470180966-3899876309-2637894779";
222         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
223
224         struct GUID transaction_id;
225         const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
226
227
228         char *line = NULL;
229         const char *rs = NULL;
230         regex_t regex;
231         int ret;
232
233
234         TALLOC_CTX *ctx = talloc_new(NULL);
235
236         ldb = ldb_init(ctx, NULL);
237
238         GUID_from_string(TRANSACTION, &transaction_id);
239
240         module = talloc_zero(ctx, struct ldb_module);
241         module->ldb = ldb;
242
243         tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
244         ldb_set_opaque(ldb, "remoteAddress", ts);
245
246         add_session_data(ctx, ldb, SESSION, SID);
247
248         req = talloc_zero(ctx, struct ldb_request);
249         req->operation =  LDB_ADD;
250         add_transaction_id(req, TRANSACTION);
251
252         line = audit_group_human_readable(
253                 ctx,
254                 module,
255                 req,
256                 "the-action",
257                 "the-user-name",
258                 "the-group-name",
259                 LDB_ERR_OPERATIONS_ERROR);
260         assert_non_null(line);
261
262         rs =    "\\[the-action\\] at \\["
263                 "[^]]*"
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\\]";
269
270         ret = regcomp(&regex, rs, 0);
271         assert_int_equal(0, ret);
272
273         ret = regexec(&regex, line, 0, NULL, 0);
274         assert_int_equal(0, ret);
275
276         regfree(&regex);
277         TALLOC_FREE(ctx);
278
279 }
280
281 /*
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.
286  */
287 static void test_get_parsed_dns(void **state)
288 {
289         struct ldb_message_element *el = NULL;
290         struct parsed_dn *dns = NULL;
291
292         TALLOC_CTX *ctx = talloc_new(NULL);
293
294         el = talloc_zero(ctx, struct ldb_message_element);
295
296         /*
297          * empty element, zero dns
298          */
299         dns = get_parsed_dns(ctx, el);
300         assert_null(dns);
301
302         /*
303          * one entry
304          */
305         el->num_values = 1;
306         el->values = talloc_zero_array(ctx, DATA_BLOB, 1);
307         el->values[0] = data_blob_string_const("The first value");
308
309         dns = get_parsed_dns(ctx, el);
310
311         assert_ptr_equal(el->values[0].data, dns[0].v->data);
312         assert_int_equal(el->values[0].length, dns[0].v->length);
313
314         TALLOC_FREE(dns);
315         TALLOC_FREE(el);
316
317
318         /*
319          * Multiple values
320          */
321         el = talloc_zero(ctx, struct ldb_message_element);
322         el->num_values = 2;
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");
326
327         dns = get_parsed_dns(ctx, el);
328
329         assert_ptr_equal(el->values[0].data, dns[0].v->data);
330         assert_int_equal(el->values[0].length, dns[0].v->length);
331
332         assert_ptr_equal(el->values[1].data, dns[1].v->data);
333         assert_int_equal(el->values[1].length, dns[1].v->length);
334
335         TALLOC_FREE(ctx);
336 }
337
338 static void test_dn_compare(void **state)
339 {
340
341         struct ldb_context *ldb = NULL;
342         struct parsed_dn *a;
343         DATA_BLOB ab;
344
345         struct parsed_dn *b;
346         DATA_BLOB bb;
347
348         int res;
349
350         TALLOC_CTX *ctx = talloc_new(NULL);
351         const struct GUID *ZERO_GUID = talloc_zero(ctx, struct GUID);
352
353         ldb = ldb_init(ctx, NULL);
354         ldb_register_samba_handlers(ldb);
355
356
357         /*
358          * Identical binary DN's
359          */
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);
364         a->v = &ab;
365
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);
370         b->v = &bb;
371
372         res = dn_compare(ctx, ldb, a, b);
373         assert_int_equal(BINARY_EQUAL, res);
374         /*
375          * DN's should not have been parsed
376          */
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));
381
382         TALLOC_FREE(a);
383         TALLOC_FREE(b);
384
385         /*
386          * differing binary DN's but equal GUID's
387          */
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);
392         a->v = &ab;
393
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);
398         b->v = &bb;
399
400         res = dn_compare(ctx, ldb, a, b);
401         assert_int_equal(EQUAL, res);
402         /*
403          * DN's should have been parsed
404          */
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));
409
410         TALLOC_FREE(a);
411         TALLOC_FREE(b);
412
413         /*
414          * differing binary DN's but and second guid greater
415          */
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);
420         a->v = &ab;
421
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);
426         b->v = &bb;
427
428         res = dn_compare(ctx, ldb, a, b);
429         assert_int_equal(GREATER_THAN, res);
430         /*
431          * DN's should have been parsed
432          */
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));
437
438         TALLOC_FREE(a);
439         TALLOC_FREE(b);
440
441         /*
442          * differing binary DN's but and second guid less
443          */
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);
448         a->v = &ab;
449
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);
454         b->v = &bb;
455
456         res = dn_compare(ctx, ldb, a, b);
457         assert_int_equal(LESS_THAN, res);
458         /*
459          * DN's should have been parsed
460          */
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));
465
466         TALLOC_FREE(a);
467         TALLOC_FREE(b);
468
469         TALLOC_FREE(ctx);
470 }
471
472 static void test_get_primary_group_dn(void **state)
473 {
474
475         struct ldb_context *ldb = NULL;
476         struct ldb_module *module = NULL;
477         const uint32_t RID = 71;
478         struct dom_sid sid;
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";
481         const char *dn;
482
483         TALLOC_CTX *ctx = talloc_new(NULL);
484
485         ldb = ldb_init(ctx, NULL);
486         ldb_register_samba_handlers(ldb);
487
488         module = talloc_zero(ctx, struct ldb_module);
489         module->ldb = ldb;
490
491         /*
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.
494          */
495         sid.num_auths = 0;
496         dn = get_primary_group_dn(ctx, module, &sid, RID);
497         assert_null(dn);
498
499         /*
500          * A valid dom sid
501          */
502         assert_true(string_to_sid(&sid, SID));
503         g_dn = DN;
504         dn = get_primary_group_dn(ctx, module, &sid, RID);
505         assert_non_null(dn);
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);
511         assert_string_equal
512                 ("<SID=S-1-5-21-2470180966-3899876309-71>",
513                 ldb_dn_get_extended_linearized(ctx, g_basedn, 1));
514
515         /*
516          * Test dsdb search failure
517          */
518         g_status = LDB_ERR_NO_SUCH_OBJECT;
519         dn = get_primary_group_dn(ctx, module, &sid, RID);
520         assert_null(dn);
521
522         TALLOC_FREE(ldb);
523         TALLOC_FREE(ctx);
524 }
525
526 static void test_audit_group_json(void **state)
527 {
528         struct ldb_context *ldb = NULL;
529         struct ldb_module  *module = NULL;
530         struct ldb_request *req = NULL;
531
532         struct tsocket_address *ts = NULL;
533
534         const char *const SID = "S-1-5-21-2470180966-3899876309-2637894779";
535         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
536
537         struct GUID transaction_id;
538         const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
539
540
541         struct json_object json;
542         json_t *audit = NULL;
543         json_t *v = NULL;
544         json_t *o = NULL;
545         time_t before;
546
547
548         TALLOC_CTX *ctx = talloc_new(NULL);
549
550         ldb = ldb_init(ctx, NULL);
551
552         GUID_from_string(TRANSACTION, &transaction_id);
553
554         module = talloc_zero(ctx, struct ldb_module);
555         module->ldb = ldb;
556
557         tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
558         ldb_set_opaque(ldb, "remoteAddress", ts);
559
560         add_session_data(ctx, ldb, SESSION, SID);
561
562         req = talloc_zero(ctx, struct ldb_request);
563         req->operation =  LDB_ADD;
564         add_transaction_id(req, TRANSACTION);
565
566         before = time(NULL);
567         json = audit_group_json(
568                 module,
569                 req,
570                 "the-action",
571                 "the-user-name",
572                 "the-group-name",
573                 LDB_ERR_OPERATIONS_ERROR);
574         assert_int_equal(3, json_object_size(json.root));
575
576         v = json_object_get(json.root, "type");
577         assert_non_null(v);
578         assert_string_equal("groupChange", json_string_value(v));
579
580         v = json_object_get(json.root, "timestamp");
581         assert_non_null(v);
582         assert_true(json_is_string(v));
583         check_timestamp(before, json_string_value(v));
584
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));
589
590         o = json_object_get(audit, "version");
591         assert_non_null(o);
592         check_version(o, AUDIT_MAJOR, AUDIT_MINOR);
593
594         v = json_object_get(audit, "statusCode");
595         assert_non_null(v);
596         assert_true(json_is_integer(v));
597         assert_int_equal(LDB_ERR_OPERATIONS_ERROR, json_integer_value(v));
598
599         v = json_object_get(audit, "status");
600         assert_non_null(v);
601         assert_true(json_is_string(v));
602         assert_string_equal("Operations error", json_string_value(v));
603
604         v = json_object_get(audit, "user");
605         assert_non_null(v);
606         assert_true(json_is_string(v));
607         assert_string_equal("the-user-name", json_string_value(v));
608
609         v = json_object_get(audit, "group");
610         assert_non_null(v);
611         assert_true(json_is_string(v));
612         assert_string_equal("the-group-name", json_string_value(v));
613
614         v = json_object_get(audit, "action");
615         assert_non_null(v);
616         assert_true(json_is_string(v));
617         assert_string_equal("the-action", json_string_value(v));
618
619         json_free(&json);
620         TALLOC_FREE(ctx);
621
622 }
623
624 /*
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
628  *       de-registered.
629  *
630  */
631 int main(void) {
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),
639
640         };
641
642         cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
643         return cmocka_run_group_tests(tests, NULL, NULL);
644 }