dsdb group audit tests: log_membership_changes extra tests
[metze/samba/wip.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  * Mock version of audit_log_json
68  */
69
70 #define MAX_EXPECTED_MESSAGES 16
71 static struct json_object messages[MAX_EXPECTED_MESSAGES];
72 static size_t messages_sent = 0;
73
74 void audit_message_send(
75         struct imessaging_context *msg_ctx,
76         const char *server_name,
77         uint32_t message_type,
78         struct json_object *message)
79 {
80         messages[messages_sent].root = json_deep_copy(message->root);
81         messages[messages_sent].valid = message->valid;
82         messages_sent++;
83 }
84
85 #define check_group_change_message(m, u, a)\
86         _check_group_change_message(m, u, a, __FILE__, __LINE__);
87 /*
88  * declare the internal cmocka cm_print_error so that we can output messages
89  * in sub unit format
90  */
91 void cm_print_error(const char * const format, ...);
92
93 /*
94  * Validate a group change JSON audit message
95  *
96  * It should contain 3 elements.
97  * Have a type of "groupChange"
98  * Have a groupChange element
99  *
100  * The group change element should have 10 elements.
101  *
102  * There should be a user element matching the expected value
103  * There should be an action matching the expected value
104  */
105 static void _check_group_change_message(
106         const int message,
107         const char *user,
108         const char *action,
109         const char *file,
110         const int line)
111 {
112         struct json_object json;
113         json_t *audit = NULL;
114         json_t *v = NULL;
115         const char* value;
116         json = messages[message];
117
118         /*
119          * Validate the root JSON element
120          * check the number of elements
121          */
122         if (json_object_size(json.root) != 3) {
123                 cm_print_error(
124                     "Unexpected number of elements in root %zu != %d\n",
125                     json_object_size(json.root),
126                     3);
127                 _fail(file, line);
128         }
129
130         /*
131          * Check the type element
132          */
133         v = json_object_get(json.root, "type");
134         if (v == NULL) {
135                 cm_print_error( "No \"type\" element\n");
136                 _fail(file, line);
137         }
138
139         value = json_string_value(v);
140         if (strncmp("groupChange", value, strlen("groupChange") != 0)) {
141                 cm_print_error(
142                     "Unexpected type \"%s\" != \"groupChange\"\n",
143                     value);
144                 _fail(file, line);
145         }
146
147
148         audit = json_object_get(json.root, "groupChange");
149         if (audit == NULL) {
150                 cm_print_error("No groupChange element\n");
151                 _fail(file, line);
152         }
153
154         /*
155          * Validate the groupChange element
156          */
157         if (json_object_size(audit) != 10) {
158                 cm_print_error(
159                     "Unexpected number of elements in groupChange "
160                     "%zu != %d\n",
161                     json_object_size(audit),
162                     10);
163                 _fail(file, line);
164         }
165         /*
166          * Validate the user element
167          */
168         v = json_object_get(audit, "user");
169         if (v == NULL) {
170                 cm_print_error( "No user element\n");
171                 _fail(file, line);
172         }
173
174         value = json_string_value(v);
175         if (strncmp(user, value, strlen(user) != 0)) {
176                 cm_print_error(
177                     "Unexpected user name \"%s\" != \"%s\"\n",
178                     value,
179                     user);
180                 _fail(file, line);
181         }
182
183         /*
184          * Validate the action element
185          */
186         v = json_object_get(audit, "action");
187         if (v == NULL) {
188                 cm_print_error( "No action element\n");
189                 _fail(file, line);
190         }
191
192         value = json_string_value(v);
193         if (strncmp(action, value, strlen(action) != 0)) {
194                 print_error(
195                     "Unexpected action \"%s\" != \"%s\"\n",
196                     value,
197                     action);
198                 _fail(file, line);
199         }
200 }
201
202 #define check_timestamp(b, t)\
203         _check_timestamp(b, t, __FILE__, __LINE__);
204 /*
205  * Test helper to check ISO 8601 timestamps for validity
206  */
207 static void _check_timestamp(
208         time_t before,
209         const char *timestamp,
210         const char *file,
211         const int line)
212 {
213         int rc;
214         int usec, tz;
215         char c[2];
216         struct tm tm;
217         time_t after;
218         time_t actual;
219
220
221         after = time(NULL);
222
223         /*
224          * Convert the ISO 8601 timestamp into a time_t
225          * Note for convenience we ignore the value of the microsecond
226          * part of the time stamp.
227          */
228         rc = sscanf(
229                 timestamp,
230                 "%4d-%2d-%2dT%2d:%2d:%2d.%6d%1c%4d",
231                 &tm.tm_year,
232                 &tm.tm_mon,
233                 &tm.tm_mday,
234                 &tm.tm_hour,
235                 &tm.tm_min,
236                 &tm.tm_sec,
237                 &usec,
238                 c,
239                 &tz);
240         assert_int_equal(9, rc);
241         tm.tm_year = tm.tm_year - 1900;
242         tm.tm_mon = tm.tm_mon - 1;
243         tm.tm_isdst = -1;
244         actual = mktime(&tm);
245
246         /*
247          * The time stamp should be before <= actual <= after
248          */
249         if (difftime(actual, before) < 0) {
250                 char buffer[40];
251                 strftime(buffer,
252                          sizeof(buffer)-1,
253                          "%Y-%m-%dT%T",
254                          localtime(&before));
255                 cm_print_error(
256                     "time stamp \"%s\" is before start time \"%s\"\n",
257                     timestamp,
258                     buffer);
259                 _fail(file, line);
260         }
261         if (difftime(after, actual) < 0) {
262                 char buffer[40];
263                 strftime(buffer,
264                          sizeof(buffer)-1,
265                          "%Y-%m-%dT%T",
266                          localtime(&after));
267                 cm_print_error(
268                     "time stamp \"%s\" is after finish time \"%s\"\n",
269                     timestamp,
270                     buffer);
271                 _fail(file, line);
272         }
273 }
274
275 #define check_version(v, m, n)\
276         _check_version(v, m, n, __FILE__, __LINE__);
277 /*
278  * Test helper to validate a version object.
279  */
280 static void _check_version(
281         struct json_t *version,
282         int major,
283         int minor,
284         const char* file,
285         const int line)
286 {
287         struct json_t *v = NULL;
288         int value;
289
290         if (!json_is_object(version)) {
291                 cm_print_error("version is not a JSON object\n");
292                 _fail(file, line);
293         }
294
295         if (json_object_size(version) != 2) {
296                 cm_print_error(
297                     "Unexpected number of elements in version %zu != %d\n",
298                     json_object_size(version),
299                     2);
300                 _fail(file, line);
301         }
302
303         /*
304          * Validate the major version number element
305          */
306         v = json_object_get(version, "major");
307         if (v == NULL) {
308                 cm_print_error( "No major element\n");
309                 _fail(file, line);
310         }
311
312         value = json_integer_value(v);
313         if (value != major) {
314                 print_error(
315                     "Unexpected major version number \"%d\" != \"%d\"\n",
316                     value,
317                     major);
318                 _fail(file, line);
319         }
320
321         /*
322          * Validate the minor version number element
323          */
324         v = json_object_get(version, "minor");
325         if (v == NULL) {
326                 cm_print_error( "No minor element\n");
327                 _fail(file, line);
328         }
329
330         value = json_integer_value(v);
331         if (value != minor) {
332                 print_error(
333                     "Unexpected minor version number \"%d\" != \"%d\"\n",
334                     value,
335                     minor);
336                 _fail(file, line);
337         }
338 }
339
340 /*
341  * Test helper to insert a transaction_id into a request.
342  */
343 static void add_transaction_id(struct ldb_request *req, const char *id)
344 {
345         struct GUID guid;
346         struct dsdb_control_transaction_identifier *transaction_id = NULL;
347
348         transaction_id = talloc_zero(
349                 req,
350                 struct dsdb_control_transaction_identifier);
351         assert_non_null(transaction_id);
352         GUID_from_string(id, &guid);
353         transaction_id->transaction_guid = guid;
354         ldb_request_add_control(
355                 req,
356                 DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID,
357                 false,
358                 transaction_id);
359 }
360
361 /*
362  * Test helper to add a session id and user SID
363  */
364 static void add_session_data(
365         TALLOC_CTX *ctx,
366         struct ldb_context *ldb,
367         const char *session,
368         const char *user_sid)
369 {
370         struct auth_session_info *sess = NULL;
371         struct security_token *token = NULL;
372         struct dom_sid *sid = NULL;
373         struct GUID session_id;
374         bool ok;
375
376         sess = talloc_zero(ctx, struct auth_session_info);
377         token = talloc_zero(ctx, struct security_token);
378         sid = talloc_zero(ctx, struct dom_sid);
379         ok = string_to_sid(sid, user_sid);
380         assert_true(ok);
381         token->sids = sid;
382         sess->security_token = token;
383         GUID_from_string(session, &session_id);
384         sess->unique_session_token = session_id;
385         ldb_set_opaque(ldb, DSDB_SESSION_INFO, sess);
386 }
387
388 static void test_get_transaction_id(void **state)
389 {
390         struct ldb_request *req = NULL;
391         struct GUID *guid;
392         const char * const ID = "7130cb06-2062-6a1b-409e-3514c26b1773";
393         char *guid_str = NULL;
394         struct GUID_txt_buf guid_buff;
395
396
397         TALLOC_CTX *ctx = talloc_new(NULL);
398
399
400         /*
401          * No transaction id, should return a zero guid
402          */
403         req = talloc_zero(ctx, struct ldb_request);
404         guid = get_transaction_id(req);
405         assert_null(guid);
406         TALLOC_FREE(req);
407
408         /*
409          * And now test with the transaction_id set
410          */
411         req = talloc_zero(ctx, struct ldb_request);
412         assert_non_null(req);
413         add_transaction_id(req, ID);
414
415         guid = get_transaction_id(req);
416         guid_str = GUID_buf_string(guid, &guid_buff);
417         assert_string_equal(ID, guid_str);
418         TALLOC_FREE(req);
419
420         TALLOC_FREE(ctx);
421 }
422
423 static void test_audit_group_hr(void **state)
424 {
425         struct ldb_context *ldb = NULL;
426         struct ldb_module  *module = NULL;
427         struct ldb_request *req = NULL;
428
429         struct tsocket_address *ts = NULL;
430
431         const char *const SID = "S-1-5-21-2470180966-3899876309-2637894779";
432         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
433
434         struct GUID transaction_id;
435         const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
436
437
438         char *line = NULL;
439         const char *rs = NULL;
440         regex_t regex;
441         int ret;
442
443
444         TALLOC_CTX *ctx = talloc_new(NULL);
445
446         ldb = ldb_init(ctx, NULL);
447
448         GUID_from_string(TRANSACTION, &transaction_id);
449
450         module = talloc_zero(ctx, struct ldb_module);
451         module->ldb = ldb;
452
453         tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
454         ldb_set_opaque(ldb, "remoteAddress", ts);
455
456         add_session_data(ctx, ldb, SESSION, SID);
457
458         req = talloc_zero(ctx, struct ldb_request);
459         req->operation =  LDB_ADD;
460         add_transaction_id(req, TRANSACTION);
461
462         line = audit_group_human_readable(
463                 ctx,
464                 module,
465                 req,
466                 "the-action",
467                 "the-user-name",
468                 "the-group-name",
469                 LDB_ERR_OPERATIONS_ERROR);
470         assert_non_null(line);
471
472         rs =    "\\[the-action\\] at \\["
473                 "[^]]*"
474                 "\\] status \\[Operations error\\] "
475                 "Remote host \\[ipv4:127.0.0.1:0\\] "
476                 "SID \\[S-1-5-21-2470180966-3899876309-2637894779\\] "
477                 "Group \\[the-group-name\\] "
478                 "User \\[the-user-name\\]";
479
480         ret = regcomp(&regex, rs, 0);
481         assert_int_equal(0, ret);
482
483         ret = regexec(&regex, line, 0, NULL, 0);
484         assert_int_equal(0, ret);
485
486         regfree(&regex);
487         TALLOC_FREE(ctx);
488
489 }
490
491 /*
492  * test get_parsed_dns
493  * For this test we assume Valgrind or Address Sanitizer will detect any over
494  * runs. Also we don't care that the values are DN's only that the value in the
495  * element is copied to the parsed_dns.
496  */
497 static void test_get_parsed_dns(void **state)
498 {
499         struct ldb_message_element *el = NULL;
500         struct parsed_dn *dns = NULL;
501
502         TALLOC_CTX *ctx = talloc_new(NULL);
503
504         el = talloc_zero(ctx, struct ldb_message_element);
505
506         /*
507          * empty element, zero dns
508          */
509         dns = get_parsed_dns(ctx, el);
510         assert_null(dns);
511
512         /*
513          * one entry
514          */
515         el->num_values = 1;
516         el->values = talloc_zero_array(ctx, DATA_BLOB, 1);
517         el->values[0] = data_blob_string_const("The first value");
518
519         dns = get_parsed_dns(ctx, el);
520
521         assert_ptr_equal(el->values[0].data, dns[0].v->data);
522         assert_int_equal(el->values[0].length, dns[0].v->length);
523
524         TALLOC_FREE(dns);
525         TALLOC_FREE(el);
526
527
528         /*
529          * Multiple values
530          */
531         el = talloc_zero(ctx, struct ldb_message_element);
532         el->num_values = 2;
533         el->values = talloc_zero_array(ctx, DATA_BLOB, 2);
534         el->values[0] = data_blob_string_const("The first value");
535         el->values[0] = data_blob_string_const("The second value");
536
537         dns = get_parsed_dns(ctx, el);
538
539         assert_ptr_equal(el->values[0].data, dns[0].v->data);
540         assert_int_equal(el->values[0].length, dns[0].v->length);
541
542         assert_ptr_equal(el->values[1].data, dns[1].v->data);
543         assert_int_equal(el->values[1].length, dns[1].v->length);
544
545         TALLOC_FREE(ctx);
546 }
547
548 static void test_dn_compare(void **state)
549 {
550
551         struct ldb_context *ldb = NULL;
552         struct parsed_dn *a;
553         DATA_BLOB ab;
554
555         struct parsed_dn *b;
556         DATA_BLOB bb;
557
558         int res;
559
560         TALLOC_CTX *ctx = talloc_new(NULL);
561         const struct GUID *ZERO_GUID = talloc_zero(ctx, struct GUID);
562
563         ldb = ldb_init(ctx, NULL);
564         ldb_register_samba_handlers(ldb);
565
566
567         /*
568          * Identical binary DN's
569          */
570         ab = data_blob_string_const(
571                 "<GUID=fbee08fd-6f75-4bd4-af3f-e4f063a6379e>;"
572                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
573         a = talloc_zero(ctx, struct parsed_dn);
574         a->v = &ab;
575
576         bb = data_blob_string_const(
577                 "<GUID=fbee08fd-6f75-4bd4-af3f-e4f063a6379e>;"
578                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
579         b = talloc_zero(ctx, struct parsed_dn);
580         b->v = &bb;
581
582         res = dn_compare(ctx, ldb, a, b);
583         assert_int_equal(BINARY_EQUAL, res);
584         /*
585          * DN's should not have been parsed
586          */
587         assert_null(a->dsdb_dn);
588         assert_memory_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
589         assert_null(b->dsdb_dn);
590         assert_memory_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
591
592         TALLOC_FREE(a);
593         TALLOC_FREE(b);
594
595         /*
596          * differing binary DN's but equal GUID's
597          */
598         ab = data_blob_string_const(
599                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651e>;"
600                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=com");
601         a = talloc_zero(ctx, struct parsed_dn);
602         a->v = &ab;
603
604         bb = data_blob_string_const(
605                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651e>;"
606                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
607         b = talloc_zero(ctx, struct parsed_dn);
608         b->v = &bb;
609
610         res = dn_compare(ctx, ldb, a, b);
611         assert_int_equal(EQUAL, res);
612         /*
613          * DN's should have been parsed
614          */
615         assert_non_null(a->dsdb_dn);
616         assert_memory_not_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
617         assert_non_null(b->dsdb_dn);
618         assert_memory_not_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
619
620         TALLOC_FREE(a);
621         TALLOC_FREE(b);
622
623         /*
624          * differing binary DN's but and second guid greater
625          */
626         ab = data_blob_string_const(
627                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651d>;"
628                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=com");
629         a = talloc_zero(ctx, struct parsed_dn);
630         a->v = &ab;
631
632         bb = data_blob_string_const(
633                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651e>;"
634                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
635         b = talloc_zero(ctx, struct parsed_dn);
636         b->v = &bb;
637
638         res = dn_compare(ctx, ldb, a, b);
639         assert_int_equal(LESS_THAN, res);
640         /*
641          * DN's should have been parsed
642          */
643         assert_non_null(a->dsdb_dn);
644         assert_memory_not_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
645         assert_non_null(b->dsdb_dn);
646         assert_memory_not_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
647
648         TALLOC_FREE(a);
649         TALLOC_FREE(b);
650
651         /*
652          * differing binary DN's but and second guid less
653          */
654         ab = data_blob_string_const(
655                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651d>;"
656                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=com");
657         a = talloc_zero(ctx, struct parsed_dn);
658         a->v = &ab;
659
660         bb = data_blob_string_const(
661                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651c>;"
662                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
663         b = talloc_zero(ctx, struct parsed_dn);
664         b->v = &bb;
665
666         res = dn_compare(ctx, ldb, a, b);
667         assert_int_equal(GREATER_THAN, res);
668         /*
669          * DN's should have been parsed
670          */
671         assert_non_null(a->dsdb_dn);
672         assert_memory_not_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
673         assert_non_null(b->dsdb_dn);
674         assert_memory_not_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
675
676         TALLOC_FREE(a);
677         TALLOC_FREE(b);
678
679         TALLOC_FREE(ctx);
680 }
681
682 static void test_get_primary_group_dn(void **state)
683 {
684
685         struct ldb_context *ldb = NULL;
686         struct ldb_module *module = NULL;
687         const uint32_t RID = 71;
688         struct dom_sid sid;
689         const char *SID = "S-1-5-21-2470180966-3899876309-2637894779";
690         const char *DN = "OU=Things,DC=ad,DC=testing,DC=samba,DC=org";
691         const char *dn;
692
693         TALLOC_CTX *ctx = talloc_new(NULL);
694
695         ldb = ldb_init(ctx, NULL);
696         ldb_register_samba_handlers(ldb);
697
698         module = talloc_zero(ctx, struct ldb_module);
699         module->ldb = ldb;
700
701         /*
702          * Pass an empty dom sid this will cause dom_sid_split_rid to fail;
703          * assign to sid.num_auths to suppress a valgrind warning.
704          */
705         sid.num_auths = 0;
706         dn = get_primary_group_dn(ctx, module, &sid, RID);
707         assert_null(dn);
708
709         /*
710          * A valid dom sid
711          */
712         assert_true(string_to_sid(&sid, SID));
713         g_dn = DN;
714         dn = get_primary_group_dn(ctx, module, &sid, RID);
715         assert_non_null(dn);
716         assert_string_equal(DN, dn);
717         assert_int_equal(LDB_SCOPE_BASE, g_scope);
718         assert_int_equal(0, g_dsdb_flags);
719         assert_null(g_attrs);
720         assert_null(g_exp_fmt);
721         assert_string_equal
722                 ("<SID=S-1-5-21-2470180966-3899876309-71>",
723                 ldb_dn_get_extended_linearized(ctx, g_basedn, 1));
724
725         /*
726          * Test dsdb search failure
727          */
728         g_status = LDB_ERR_NO_SUCH_OBJECT;
729         dn = get_primary_group_dn(ctx, module, &sid, RID);
730         assert_null(dn);
731
732         TALLOC_FREE(ldb);
733         TALLOC_FREE(ctx);
734 }
735
736 static void test_audit_group_json(void **state)
737 {
738         struct ldb_context *ldb = NULL;
739         struct ldb_module  *module = NULL;
740         struct ldb_request *req = NULL;
741
742         struct tsocket_address *ts = NULL;
743
744         const char *const SID = "S-1-5-21-2470180966-3899876309-2637894779";
745         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
746
747         struct GUID transaction_id;
748         const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
749
750
751         struct json_object json;
752         json_t *audit = NULL;
753         json_t *v = NULL;
754         json_t *o = NULL;
755         time_t before;
756
757
758         TALLOC_CTX *ctx = talloc_new(NULL);
759
760         ldb = ldb_init(ctx, NULL);
761
762         GUID_from_string(TRANSACTION, &transaction_id);
763
764         module = talloc_zero(ctx, struct ldb_module);
765         module->ldb = ldb;
766
767         tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
768         ldb_set_opaque(ldb, "remoteAddress", ts);
769
770         add_session_data(ctx, ldb, SESSION, SID);
771
772         req = talloc_zero(ctx, struct ldb_request);
773         req->operation =  LDB_ADD;
774         add_transaction_id(req, TRANSACTION);
775
776         before = time(NULL);
777         json = audit_group_json(
778                 module,
779                 req,
780                 "the-action",
781                 "the-user-name",
782                 "the-group-name",
783                 LDB_ERR_OPERATIONS_ERROR);
784         assert_int_equal(3, json_object_size(json.root));
785
786         v = json_object_get(json.root, "type");
787         assert_non_null(v);
788         assert_string_equal("groupChange", json_string_value(v));
789
790         v = json_object_get(json.root, "timestamp");
791         assert_non_null(v);
792         assert_true(json_is_string(v));
793         check_timestamp(before, json_string_value(v));
794
795         audit = json_object_get(json.root, "groupChange");
796         assert_non_null(audit);
797         assert_true(json_is_object(audit));
798         assert_int_equal(10, json_object_size(audit));
799
800         o = json_object_get(audit, "version");
801         assert_non_null(o);
802         check_version(o, AUDIT_MAJOR, AUDIT_MINOR);
803
804         v = json_object_get(audit, "statusCode");
805         assert_non_null(v);
806         assert_true(json_is_integer(v));
807         assert_int_equal(LDB_ERR_OPERATIONS_ERROR, json_integer_value(v));
808
809         v = json_object_get(audit, "status");
810         assert_non_null(v);
811         assert_true(json_is_string(v));
812         assert_string_equal("Operations error", json_string_value(v));
813
814         v = json_object_get(audit, "user");
815         assert_non_null(v);
816         assert_true(json_is_string(v));
817         assert_string_equal("the-user-name", json_string_value(v));
818
819         v = json_object_get(audit, "group");
820         assert_non_null(v);
821         assert_true(json_is_string(v));
822         assert_string_equal("the-group-name", json_string_value(v));
823
824         v = json_object_get(audit, "action");
825         assert_non_null(v);
826         assert_true(json_is_string(v));
827         assert_string_equal("the-action", json_string_value(v));
828
829         json_free(&json);
830         TALLOC_FREE(ctx);
831 }
832
833 static void setup_ldb(
834         TALLOC_CTX *ctx,
835         struct ldb_context **ldb,
836         struct ldb_module **module,
837         const char *ip,
838         const char *session,
839         const char *sid)
840 {
841         struct tsocket_address *ts = NULL;
842         struct audit_context *context = NULL;
843
844         *ldb = ldb_init(ctx, NULL);
845         ldb_register_samba_handlers(*ldb);
846
847
848         *module = talloc_zero(ctx, struct ldb_module);
849         (*module)->ldb = *ldb;
850
851         context = talloc_zero(*module, struct audit_context);
852         context->send_events = true;
853         context->msg_ctx = (struct imessaging_context *) 0x01;
854
855         ldb_module_set_private(*module, context);
856
857         tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
858         ldb_set_opaque(*ldb, "remoteAddress", ts);
859
860         add_session_data(ctx, *ldb, session, sid);
861 }
862
863 /*
864  * Test the removal of a user from a group.
865  *
866  * The new element contains one group member
867  * The old element contains two group member
868  *
869  * Expect to see the removed entry logged.
870  *
871  * This test confirms bug 13664
872  * https://bugzilla.samba.org/show_bug.cgi?id=13664
873  */
874 static void test_log_membership_changes_removed(void **state)
875 {
876         struct ldb_context *ldb = NULL;
877         struct ldb_module  *module = NULL;
878         const char * const SID = "S-1-5-21-2470180966-3899876309-2637894779";
879         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
880         const char * const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
881         const char * const IP = "127.0.0.1";
882         struct ldb_request *req = NULL;
883         struct ldb_message_element *new_el = NULL;
884         struct ldb_message_element *old_el = NULL;
885         int status = 0;
886         TALLOC_CTX *ctx = talloc_new(NULL);
887
888         setup_ldb(ctx, &ldb, &module, IP, SESSION, SID);
889
890         /*
891          * Build the ldb_request
892          */
893         req = talloc_zero(ctx, struct ldb_request);
894         req->operation =  LDB_ADD;
895         add_transaction_id(req, TRANSACTION);
896
897         /*
898          * Populate the new elements, containing one entry.
899          * Indicating that one element has been removed
900          */
901         new_el = talloc_zero(ctx, struct ldb_message_element);
902         new_el->num_values = 1;
903         new_el->values = talloc_zero_array(ctx, DATA_BLOB, 1);
904         new_el->values[0] = data_blob_string_const(
905                 "<GUID=081519b5-a709-44a0-bc95-dd4bfe809bf8>;"
906                 "CN=testuser131953,CN=Users,DC=addom,DC=samba,"
907                 "DC=example,DC=com");
908
909         /*
910          * Populate the old elements, with two elements
911          * The first is the same as the one in new elements.
912          */
913         old_el = talloc_zero(ctx, struct ldb_message_element);
914         old_el->num_values = 2;
915         old_el->values = talloc_zero_array(ctx, DATA_BLOB, 2);
916         old_el->values[0] = data_blob_string_const(
917                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681b>;"
918                 "cn=grpadttstuser01,cn=users,DC=addom,"
919                 "DC=samba,DC=example,DC=com");
920         old_el->values[1] = data_blob_string_const(
921                 "<GUID=081519b5-a709-44a0-bc95-dd4bfe809bf8>;"
922                 "CN=testuser131953,CN=Users,DC=addom,DC=samba,"
923                 "DC=example,DC=com");
924
925         /*
926          * call log_membership_changes
927          */
928         messages_sent = 0;
929         log_membership_changes(module, req, new_el, old_el, status);
930
931         /*
932          * Check the results
933          */
934         assert_int_equal(1, messages_sent);
935
936         check_group_change_message(
937             0,
938             "cn=grpadttstuser01,cn=users,DC=addom,DC=samba,DC=example,DC=com",
939             "Removed");
940
941         /*
942          * Clean up
943          */
944         json_free(&messages[0]);
945         TALLOC_FREE(ctx);
946 }
947
948 /* test log_membership_changes
949  *
950  * old contains 2 user dn's
951  * new contains 0 user dn's
952  *
953  * Expect to see both dn's logged as deleted.
954  */
955 static void test_log_membership_changes_remove_all(void **state)
956 {
957         struct ldb_context *ldb = NULL;
958         struct ldb_module  *module = NULL;
959         const char * const SID = "S-1-5-21-2470180966-3899876309-2637894779";
960         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
961         const char * const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
962         const char * const IP = "127.0.0.1";
963         struct ldb_request *req = NULL;
964         struct ldb_message_element *new_el = NULL;
965         struct ldb_message_element *old_el = NULL;
966         int status = 0;
967         TALLOC_CTX *ctx = talloc_new(NULL);
968
969         setup_ldb(ctx, &ldb, &module, IP, SESSION, SID);
970
971         /*
972          * Build the ldb_request
973          */
974         req = talloc_zero(ctx, struct ldb_request);
975         req->operation =  LDB_ADD;
976         add_transaction_id(req, TRANSACTION);
977
978         /*
979          * Populate the new elements, containing no entries.
980          * Indicating that all elements have been removed
981          */
982         new_el = talloc_zero(ctx, struct ldb_message_element);
983         new_el->num_values = 0;
984         new_el->values = NULL;
985
986         /*
987          * Populate the old elements, with two elements
988          */
989         old_el = talloc_zero(ctx, struct ldb_message_element);
990         old_el->num_values = 2;
991         old_el->values = talloc_zero_array(ctx, DATA_BLOB, 2);
992         old_el->values[0] = data_blob_string_const(
993                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681b>;"
994                 "cn=grpadttstuser01,cn=users,DC=addom,"
995                 "DC=samba,DC=example,DC=com");
996         old_el->values[1] = data_blob_string_const(
997                 "<GUID=081519b5-a709-44a0-bc95-dd4bfe809bf8>;"
998                 "CN=testuser131953,CN=Users,DC=addom,DC=samba,"
999                 "DC=example,DC=com");
1000
1001         /*
1002          * call log_membership_changes
1003          */
1004         messages_sent = 0;
1005         log_membership_changes( module, req, new_el, old_el, status);
1006
1007         /*
1008          * Check the results
1009          */
1010         assert_int_equal(2, messages_sent);
1011
1012         check_group_change_message(
1013             0,
1014             "cn=grpadttstuser01,cn=users,DC=addom,DC=samba,DC=example,DC=com",
1015             "Removed");
1016
1017         check_group_change_message(
1018             1,
1019             "CN=testuser131953,CN=Users,DC=addom,DC=samba,DC=example,DC=com",
1020             "Removed");
1021
1022         /*
1023          * Clean up
1024          */
1025         json_free(&messages[0]);
1026         json_free(&messages[1]);
1027         TALLOC_FREE(ctx);
1028 }
1029
1030 /* test log_membership_changes
1031  *
1032  * Add an entry.
1033  *
1034  * Old entries contains a single user dn
1035  * New entries contains 2 user dn's, one matching the dn in old entries
1036  *
1037  * Should see a single new entry logged.
1038  */
1039 static void test_log_membership_changes_added(void **state)
1040 {
1041         struct ldb_context *ldb = NULL;
1042         struct ldb_module  *module = NULL;
1043         const char * const SID = "S-1-5-21-2470180966-3899876309-2637894779";
1044         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
1045         const char * const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
1046         const char * const IP = "127.0.0.1";
1047         struct ldb_request *req = NULL;
1048         struct ldb_message_element *new_el = NULL;
1049         struct ldb_message_element *old_el = NULL;
1050         int status = 0;
1051         TALLOC_CTX *ctx = talloc_new(NULL);
1052
1053         setup_ldb(ctx, &ldb, &module, IP, SESSION, SID);
1054
1055         /*
1056          * Build the ldb_request
1057          */
1058         req = talloc_zero(ctx, struct ldb_request);
1059         req->operation =  LDB_ADD;
1060         add_transaction_id(req, TRANSACTION);
1061
1062         /*
1063          * Populate the old elements adding a single entry.
1064          */
1065         old_el = talloc_zero(ctx, struct ldb_message_element);
1066         old_el->num_values = 1;
1067         old_el->values = talloc_zero_array(ctx, DATA_BLOB, 1);
1068         old_el->values[0] = data_blob_string_const(
1069                 "<GUID=081519b5-a709-44a0-bc95-dd4bfe809bf8>;"
1070                 "CN=testuser131953,CN=Users,DC=addom,DC=samba,"
1071                 "DC=example,DC=com");
1072
1073         /*
1074          * Populate the new elements adding two entries. One matches the entry
1075          * in old elements. We expect to see the other element logged as Added
1076          */
1077         new_el = talloc_zero(ctx, struct ldb_message_element);
1078         new_el->num_values = 2;
1079         new_el->values = talloc_zero_array(ctx, DATA_BLOB, 2);
1080         new_el->values[0] = data_blob_string_const(
1081                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681b>;"
1082                 "cn=grpadttstuser01,cn=users,DC=addom,"
1083                 "DC=samba,DC=example,DC=com");
1084         new_el->values[1] = data_blob_string_const(
1085                 "<GUID=081519b5-a709-44a0-bc95-dd4bfe809bf8>;"
1086                 "CN=testuser131953,CN=Users,DC=addom,DC=samba,"
1087                 "DC=example,DC=com");
1088
1089         /*
1090          * call log_membership_changes
1091          */
1092         messages_sent = 0;
1093         log_membership_changes( module, req, new_el, old_el, status);
1094
1095         /*
1096          * Check the results
1097          */
1098         assert_int_equal(1, messages_sent);
1099
1100         check_group_change_message(
1101             0,
1102             "cn=grpadttstuser01,cn=users,DC=addom,DC=samba,DC=example,DC=com",
1103             "Added");
1104
1105         /*
1106          * Clean up
1107          */
1108         json_free(&messages[0]);
1109         TALLOC_FREE(ctx);
1110 }
1111
1112 /*
1113  * test log_membership_changes.
1114  *
1115  * Old entries is empty
1116  * New entries contains 2 user dn's
1117  *
1118  * Expect to see log messages for two added users
1119  */
1120 static void test_log_membership_changes_add_to_empty(void **state)
1121 {
1122         struct ldb_context *ldb = NULL;
1123         struct ldb_module  *module = NULL;
1124         const char * const SID = "S-1-5-21-2470180966-3899876309-2637894779";
1125         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
1126         const char * const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
1127         const char * const IP = "127.0.0.1";
1128         struct ldb_request *req = NULL;
1129         struct ldb_message_element *new_el = NULL;
1130         struct ldb_message_element *old_el = NULL;
1131         int status = 0;
1132         TALLOC_CTX *ctx = talloc_new(NULL);
1133
1134         /*
1135          * Set up the ldb and module structures
1136          */
1137         setup_ldb(ctx, &ldb, &module, IP, SESSION, SID);
1138
1139         /*
1140          * Build the request structure
1141          */
1142         req = talloc_zero(ctx, struct ldb_request);
1143         req->operation =  LDB_ADD;
1144         add_transaction_id(req, TRANSACTION);
1145
1146         /*
1147          * Build the element containing the old values
1148          */
1149         old_el = talloc_zero(ctx, struct ldb_message_element);
1150         old_el->num_values = 0;
1151         old_el->values = NULL;
1152
1153         /*
1154          * Build the element containing the new values
1155          */
1156         new_el = talloc_zero(ctx, struct ldb_message_element);
1157         new_el->num_values = 2;
1158         new_el->values = talloc_zero_array(ctx, DATA_BLOB, 2);
1159         new_el->values[0] = data_blob_string_const(
1160                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681b>;"
1161                 "cn=grpadttstuser01,cn=users,DC=addom,"
1162                 "DC=samba,DC=example,DC=com");
1163         new_el->values[1] = data_blob_string_const(
1164                 "<GUID=081519b5-a709-44a0-bc95-dd4bfe809bf8>;"
1165                 "CN=testuser131953,CN=Users,DC=addom,DC=samba,"
1166                 "DC=example,DC=com");
1167
1168         /*
1169          * Run log membership changes
1170          */
1171         messages_sent = 0;
1172         log_membership_changes( module, req, new_el, old_el, status);
1173         assert_int_equal(2, messages_sent);
1174
1175         check_group_change_message(
1176             0,
1177             "cn=grpadttstuser01,cn=users,DC=addom,DC=samba,DC=example,DC=com",
1178             "Added");
1179
1180         check_group_change_message(
1181             1,
1182             "CN=testuser131953,CN=Users,DC=addom,DC=samba,DC=example,DC=com",
1183             "Added");
1184
1185         json_free(&messages[0]);
1186         json_free(&messages[1]);
1187         TALLOC_FREE(ctx);
1188 }
1189
1190 /* test log_membership_changes
1191  *
1192  * Test Replication Meta Data flag handling.
1193  *
1194  * 4 entries in old and new entries with their RMD_FLAGS set as below:
1195  *    old   new
1196  * 1)  0     0    Not logged
1197  * 2)  1     1    Both deleted, no change not logged
1198  * 3)  0     1    New tagged as deleted, log as deleted
1199  * 4)  1     0    Has been undeleted, log as an add
1200  *
1201  * Should see a single new entry logged.
1202  */
1203 static void test_log_membership_changes_rmd_flags(void **state)
1204 {
1205         struct ldb_context *ldb = NULL;
1206         struct ldb_module  *module = NULL;
1207         const char * const SID = "S-1-5-21-2470180966-3899876309-2637894779";
1208         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
1209         const char * const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
1210         const char * const IP = "127.0.0.1";
1211         struct ldb_request *req = NULL;
1212         struct ldb_message_element *new_el = NULL;
1213         struct ldb_message_element *old_el = NULL;
1214         int status = 0;
1215         TALLOC_CTX *ctx = talloc_new(NULL);
1216
1217         setup_ldb(ctx, &ldb, &module, IP, SESSION, SID);
1218
1219         /*
1220          * Build the ldb_request
1221          */
1222         req = talloc_zero(ctx, struct ldb_request);
1223         req->operation =  LDB_ADD;
1224         add_transaction_id(req, TRANSACTION);
1225
1226         /*
1227          * Populate the old elements.
1228          */
1229         old_el = talloc_zero(ctx, struct ldb_message_element);
1230         old_el->num_values = 4;
1231         old_el->values = talloc_zero_array(ctx, DATA_BLOB, 4);
1232         old_el->values[0] = data_blob_string_const(
1233                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681b>;"
1234                 "<RMD_FLAGS=0>;"
1235                 "cn=grpadttstuser01,cn=users,DC=addom,"
1236                 "DC=samba,DC=example,DC=com");
1237         old_el->values[1] = data_blob_string_const(
1238                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681c>;"
1239                 "<RMD_FLAGS=1>;"
1240                 "cn=grpadttstuser02,cn=users,DC=addom,"
1241                 "DC=samba,DC=example,DC=com");
1242         old_el->values[2] = data_blob_string_const(
1243                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681d>;"
1244                 "<RMD_FLAGS=0>;"
1245                 "cn=grpadttstuser03,cn=users,DC=addom,"
1246                 "DC=samba,DC=example,DC=com");
1247         old_el->values[3] = data_blob_string_const(
1248                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681e>;"
1249                 "<RMD_FLAGS=1>;"
1250                 "cn=grpadttstuser04,cn=users,DC=addom,"
1251                 "DC=samba,DC=example,DC=com");
1252
1253         /*
1254          * Populate the new elements.
1255          */
1256         new_el = talloc_zero(ctx, struct ldb_message_element);
1257         new_el->num_values = 4;
1258         new_el->values = talloc_zero_array(ctx, DATA_BLOB, 4);
1259         new_el->values[0] = data_blob_string_const(
1260                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681b>;"
1261                 "<RMD_FLAGS=0>;"
1262                 "cn=grpadttstuser01,cn=users,DC=addom,"
1263                 "DC=samba,DC=example,DC=com");
1264         new_el->values[1] = data_blob_string_const(
1265                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681c>;"
1266                 "<RMD_FLAGS=1>;"
1267                 "cn=grpadttstuser02,cn=users,DC=addom,"
1268                 "DC=samba,DC=example,DC=com");
1269         new_el->values[2] = data_blob_string_const(
1270                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681d>;"
1271                 "<RMD_FLAGS=1>;"
1272                 "cn=grpadttstuser03,cn=users,DC=addom,"
1273                 "DC=samba,DC=example,DC=com");
1274         new_el->values[3] = data_blob_string_const(
1275                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681e>;"
1276                 "<RMD_FLAGS=0>;"
1277                 "cn=grpadttstuser04,cn=users,DC=addom,"
1278                 "DC=samba,DC=example,DC=com");
1279
1280         /*
1281          * call log_membership_changes
1282          */
1283         messages_sent = 0;
1284         log_membership_changes( module, req, new_el, old_el, status);
1285
1286         /*
1287          * Check the results
1288          */
1289         assert_int_equal(2, messages_sent);
1290
1291         check_group_change_message(
1292             0,
1293             "cn=grpadttstuser03,cn=users,DC=addom,DC=samba,DC=example,DC=com",
1294             "Removed");
1295         check_group_change_message(
1296             1,
1297             "cn=grpadttstuser04,cn=users,DC=addom,DC=samba,DC=example,DC=com",
1298             "Added");
1299
1300         /*
1301          * Clean up
1302          */
1303         json_free(&messages[0]);
1304         json_free(&messages[1]);
1305         TALLOC_FREE(ctx);
1306 }
1307
1308 /*
1309  * Note: to run under valgrind us:
1310  *       valgrind --suppressions=test_group_audit.valgrind bin/test_group_audit
1311  *       This suppresses the errors generated because the ldb_modules are not
1312  *       de-registered.
1313  *
1314  */
1315 int main(void) {
1316         const struct CMUnitTest tests[] = {
1317                 cmocka_unit_test(test_audit_group_json),
1318                 cmocka_unit_test(test_get_transaction_id),
1319                 cmocka_unit_test(test_audit_group_hr),
1320                 cmocka_unit_test(test_get_parsed_dns),
1321                 cmocka_unit_test(test_dn_compare),
1322                 cmocka_unit_test(test_get_primary_group_dn),
1323                 cmocka_unit_test(test_log_membership_changes_removed),
1324                 cmocka_unit_test(test_log_membership_changes_remove_all),
1325                 cmocka_unit_test(test_log_membership_changes_added),
1326                 cmocka_unit_test(test_log_membership_changes_add_to_empty),
1327                 cmocka_unit_test(test_log_membership_changes_rmd_flags),
1328         };
1329
1330         cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
1331         return cmocka_run_group_tests(tests, NULL, NULL);
1332 }