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