dsdb group audit tests: check_timestamp improve diagnostics
[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 /*
276  * Test helper to validate a version object.
277  */
278 static void check_version(struct json_t *version, int major, int minor)
279 {
280         struct json_t *v = NULL;
281
282         assert_true(json_is_object(version));
283         assert_int_equal(2, json_object_size(version));
284
285         v = json_object_get(version, "major");
286         assert_non_null(v);
287         assert_int_equal(major, json_integer_value(v));
288
289         v = json_object_get(version, "minor");
290         assert_non_null(v);
291         assert_int_equal(minor, json_integer_value(v));
292 }
293
294 /*
295  * Test helper to insert a transaction_id into a request.
296  */
297 static void add_transaction_id(struct ldb_request *req, const char *id)
298 {
299         struct GUID guid;
300         struct dsdb_control_transaction_identifier *transaction_id = NULL;
301
302         transaction_id = talloc_zero(
303                 req,
304                 struct dsdb_control_transaction_identifier);
305         assert_non_null(transaction_id);
306         GUID_from_string(id, &guid);
307         transaction_id->transaction_guid = guid;
308         ldb_request_add_control(
309                 req,
310                 DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID,
311                 false,
312                 transaction_id);
313 }
314
315 /*
316  * Test helper to add a session id and user SID
317  */
318 static void add_session_data(
319         TALLOC_CTX *ctx,
320         struct ldb_context *ldb,
321         const char *session,
322         const char *user_sid)
323 {
324         struct auth_session_info *sess = NULL;
325         struct security_token *token = NULL;
326         struct dom_sid *sid = NULL;
327         struct GUID session_id;
328         bool ok;
329
330         sess = talloc_zero(ctx, struct auth_session_info);
331         token = talloc_zero(ctx, struct security_token);
332         sid = talloc_zero(ctx, struct dom_sid);
333         ok = string_to_sid(sid, user_sid);
334         assert_true(ok);
335         token->sids = sid;
336         sess->security_token = token;
337         GUID_from_string(session, &session_id);
338         sess->unique_session_token = session_id;
339         ldb_set_opaque(ldb, DSDB_SESSION_INFO, sess);
340 }
341
342 static void test_get_transaction_id(void **state)
343 {
344         struct ldb_request *req = NULL;
345         struct GUID *guid;
346         const char * const ID = "7130cb06-2062-6a1b-409e-3514c26b1773";
347         char *guid_str = NULL;
348         struct GUID_txt_buf guid_buff;
349
350
351         TALLOC_CTX *ctx = talloc_new(NULL);
352
353
354         /*
355          * No transaction id, should return a zero guid
356          */
357         req = talloc_zero(ctx, struct ldb_request);
358         guid = get_transaction_id(req);
359         assert_null(guid);
360         TALLOC_FREE(req);
361
362         /*
363          * And now test with the transaction_id set
364          */
365         req = talloc_zero(ctx, struct ldb_request);
366         assert_non_null(req);
367         add_transaction_id(req, ID);
368
369         guid = get_transaction_id(req);
370         guid_str = GUID_buf_string(guid, &guid_buff);
371         assert_string_equal(ID, guid_str);
372         TALLOC_FREE(req);
373
374         TALLOC_FREE(ctx);
375 }
376
377 static void test_audit_group_hr(void **state)
378 {
379         struct ldb_context *ldb = NULL;
380         struct ldb_module  *module = NULL;
381         struct ldb_request *req = NULL;
382
383         struct tsocket_address *ts = NULL;
384
385         const char *const SID = "S-1-5-21-2470180966-3899876309-2637894779";
386         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
387
388         struct GUID transaction_id;
389         const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
390
391
392         char *line = NULL;
393         const char *rs = NULL;
394         regex_t regex;
395         int ret;
396
397
398         TALLOC_CTX *ctx = talloc_new(NULL);
399
400         ldb = ldb_init(ctx, NULL);
401
402         GUID_from_string(TRANSACTION, &transaction_id);
403
404         module = talloc_zero(ctx, struct ldb_module);
405         module->ldb = ldb;
406
407         tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
408         ldb_set_opaque(ldb, "remoteAddress", ts);
409
410         add_session_data(ctx, ldb, SESSION, SID);
411
412         req = talloc_zero(ctx, struct ldb_request);
413         req->operation =  LDB_ADD;
414         add_transaction_id(req, TRANSACTION);
415
416         line = audit_group_human_readable(
417                 ctx,
418                 module,
419                 req,
420                 "the-action",
421                 "the-user-name",
422                 "the-group-name",
423                 LDB_ERR_OPERATIONS_ERROR);
424         assert_non_null(line);
425
426         rs =    "\\[the-action\\] at \\["
427                 "[^]]*"
428                 "\\] status \\[Operations error\\] "
429                 "Remote host \\[ipv4:127.0.0.1:0\\] "
430                 "SID \\[S-1-5-21-2470180966-3899876309-2637894779\\] "
431                 "Group \\[the-group-name\\] "
432                 "User \\[the-user-name\\]";
433
434         ret = regcomp(&regex, rs, 0);
435         assert_int_equal(0, ret);
436
437         ret = regexec(&regex, line, 0, NULL, 0);
438         assert_int_equal(0, ret);
439
440         regfree(&regex);
441         TALLOC_FREE(ctx);
442
443 }
444
445 /*
446  * test get_parsed_dns
447  * For this test we assume Valgrind or Address Sanitizer will detect any over
448  * runs. Also we don't care that the values are DN's only that the value in the
449  * element is copied to the parsed_dns.
450  */
451 static void test_get_parsed_dns(void **state)
452 {
453         struct ldb_message_element *el = NULL;
454         struct parsed_dn *dns = NULL;
455
456         TALLOC_CTX *ctx = talloc_new(NULL);
457
458         el = talloc_zero(ctx, struct ldb_message_element);
459
460         /*
461          * empty element, zero dns
462          */
463         dns = get_parsed_dns(ctx, el);
464         assert_null(dns);
465
466         /*
467          * one entry
468          */
469         el->num_values = 1;
470         el->values = talloc_zero_array(ctx, DATA_BLOB, 1);
471         el->values[0] = data_blob_string_const("The first value");
472
473         dns = get_parsed_dns(ctx, el);
474
475         assert_ptr_equal(el->values[0].data, dns[0].v->data);
476         assert_int_equal(el->values[0].length, dns[0].v->length);
477
478         TALLOC_FREE(dns);
479         TALLOC_FREE(el);
480
481
482         /*
483          * Multiple values
484          */
485         el = talloc_zero(ctx, struct ldb_message_element);
486         el->num_values = 2;
487         el->values = talloc_zero_array(ctx, DATA_BLOB, 2);
488         el->values[0] = data_blob_string_const("The first value");
489         el->values[0] = data_blob_string_const("The second value");
490
491         dns = get_parsed_dns(ctx, el);
492
493         assert_ptr_equal(el->values[0].data, dns[0].v->data);
494         assert_int_equal(el->values[0].length, dns[0].v->length);
495
496         assert_ptr_equal(el->values[1].data, dns[1].v->data);
497         assert_int_equal(el->values[1].length, dns[1].v->length);
498
499         TALLOC_FREE(ctx);
500 }
501
502 static void test_dn_compare(void **state)
503 {
504
505         struct ldb_context *ldb = NULL;
506         struct parsed_dn *a;
507         DATA_BLOB ab;
508
509         struct parsed_dn *b;
510         DATA_BLOB bb;
511
512         int res;
513
514         TALLOC_CTX *ctx = talloc_new(NULL);
515         const struct GUID *ZERO_GUID = talloc_zero(ctx, struct GUID);
516
517         ldb = ldb_init(ctx, NULL);
518         ldb_register_samba_handlers(ldb);
519
520
521         /*
522          * Identical binary DN's
523          */
524         ab = data_blob_string_const(
525                 "<GUID=fbee08fd-6f75-4bd4-af3f-e4f063a6379e>;"
526                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
527         a = talloc_zero(ctx, struct parsed_dn);
528         a->v = &ab;
529
530         bb = data_blob_string_const(
531                 "<GUID=fbee08fd-6f75-4bd4-af3f-e4f063a6379e>;"
532                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
533         b = talloc_zero(ctx, struct parsed_dn);
534         b->v = &bb;
535
536         res = dn_compare(ctx, ldb, a, b);
537         assert_int_equal(BINARY_EQUAL, res);
538         /*
539          * DN's should not have been parsed
540          */
541         assert_null(a->dsdb_dn);
542         assert_memory_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
543         assert_null(b->dsdb_dn);
544         assert_memory_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
545
546         TALLOC_FREE(a);
547         TALLOC_FREE(b);
548
549         /*
550          * differing binary DN's but equal GUID's
551          */
552         ab = data_blob_string_const(
553                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651e>;"
554                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=com");
555         a = talloc_zero(ctx, struct parsed_dn);
556         a->v = &ab;
557
558         bb = data_blob_string_const(
559                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651e>;"
560                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
561         b = talloc_zero(ctx, struct parsed_dn);
562         b->v = &bb;
563
564         res = dn_compare(ctx, ldb, a, b);
565         assert_int_equal(EQUAL, res);
566         /*
567          * DN's should have been parsed
568          */
569         assert_non_null(a->dsdb_dn);
570         assert_memory_not_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
571         assert_non_null(b->dsdb_dn);
572         assert_memory_not_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
573
574         TALLOC_FREE(a);
575         TALLOC_FREE(b);
576
577         /*
578          * differing binary DN's but and second guid greater
579          */
580         ab = data_blob_string_const(
581                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651d>;"
582                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=com");
583         a = talloc_zero(ctx, struct parsed_dn);
584         a->v = &ab;
585
586         bb = data_blob_string_const(
587                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651e>;"
588                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
589         b = talloc_zero(ctx, struct parsed_dn);
590         b->v = &bb;
591
592         res = dn_compare(ctx, ldb, a, b);
593         assert_int_equal(LESS_THAN, res);
594         /*
595          * DN's should have been parsed
596          */
597         assert_non_null(a->dsdb_dn);
598         assert_memory_not_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
599         assert_non_null(b->dsdb_dn);
600         assert_memory_not_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
601
602         TALLOC_FREE(a);
603         TALLOC_FREE(b);
604
605         /*
606          * differing binary DN's but and second guid less
607          */
608         ab = data_blob_string_const(
609                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651d>;"
610                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=com");
611         a = talloc_zero(ctx, struct parsed_dn);
612         a->v = &ab;
613
614         bb = data_blob_string_const(
615                 "<GUID=efdc91e5-5a5a-493e-9606-166ed0c2651c>;"
616                 "OU=Domain Controllers,DC=ad,DC=testing,DC=samba,DC=org");
617         b = talloc_zero(ctx, struct parsed_dn);
618         b->v = &bb;
619
620         res = dn_compare(ctx, ldb, a, b);
621         assert_int_equal(GREATER_THAN, res);
622         /*
623          * DN's should have been parsed
624          */
625         assert_non_null(a->dsdb_dn);
626         assert_memory_not_equal(ZERO_GUID, &a->guid, sizeof(struct GUID));
627         assert_non_null(b->dsdb_dn);
628         assert_memory_not_equal(ZERO_GUID, &b->guid, sizeof(struct GUID));
629
630         TALLOC_FREE(a);
631         TALLOC_FREE(b);
632
633         TALLOC_FREE(ctx);
634 }
635
636 static void test_get_primary_group_dn(void **state)
637 {
638
639         struct ldb_context *ldb = NULL;
640         struct ldb_module *module = NULL;
641         const uint32_t RID = 71;
642         struct dom_sid sid;
643         const char *SID = "S-1-5-21-2470180966-3899876309-2637894779";
644         const char *DN = "OU=Things,DC=ad,DC=testing,DC=samba,DC=org";
645         const char *dn;
646
647         TALLOC_CTX *ctx = talloc_new(NULL);
648
649         ldb = ldb_init(ctx, NULL);
650         ldb_register_samba_handlers(ldb);
651
652         module = talloc_zero(ctx, struct ldb_module);
653         module->ldb = ldb;
654
655         /*
656          * Pass an empty dom sid this will cause dom_sid_split_rid to fail;
657          * assign to sid.num_auths to suppress a valgrind warning.
658          */
659         sid.num_auths = 0;
660         dn = get_primary_group_dn(ctx, module, &sid, RID);
661         assert_null(dn);
662
663         /*
664          * A valid dom sid
665          */
666         assert_true(string_to_sid(&sid, SID));
667         g_dn = DN;
668         dn = get_primary_group_dn(ctx, module, &sid, RID);
669         assert_non_null(dn);
670         assert_string_equal(DN, dn);
671         assert_int_equal(LDB_SCOPE_BASE, g_scope);
672         assert_int_equal(0, g_dsdb_flags);
673         assert_null(g_attrs);
674         assert_null(g_exp_fmt);
675         assert_string_equal
676                 ("<SID=S-1-5-21-2470180966-3899876309-71>",
677                 ldb_dn_get_extended_linearized(ctx, g_basedn, 1));
678
679         /*
680          * Test dsdb search failure
681          */
682         g_status = LDB_ERR_NO_SUCH_OBJECT;
683         dn = get_primary_group_dn(ctx, module, &sid, RID);
684         assert_null(dn);
685
686         TALLOC_FREE(ldb);
687         TALLOC_FREE(ctx);
688 }
689
690 static void test_audit_group_json(void **state)
691 {
692         struct ldb_context *ldb = NULL;
693         struct ldb_module  *module = NULL;
694         struct ldb_request *req = NULL;
695
696         struct tsocket_address *ts = NULL;
697
698         const char *const SID = "S-1-5-21-2470180966-3899876309-2637894779";
699         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
700
701         struct GUID transaction_id;
702         const char *const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
703
704
705         struct json_object json;
706         json_t *audit = NULL;
707         json_t *v = NULL;
708         json_t *o = NULL;
709         time_t before;
710
711
712         TALLOC_CTX *ctx = talloc_new(NULL);
713
714         ldb = ldb_init(ctx, NULL);
715
716         GUID_from_string(TRANSACTION, &transaction_id);
717
718         module = talloc_zero(ctx, struct ldb_module);
719         module->ldb = ldb;
720
721         tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
722         ldb_set_opaque(ldb, "remoteAddress", ts);
723
724         add_session_data(ctx, ldb, SESSION, SID);
725
726         req = talloc_zero(ctx, struct ldb_request);
727         req->operation =  LDB_ADD;
728         add_transaction_id(req, TRANSACTION);
729
730         before = time(NULL);
731         json = audit_group_json(
732                 module,
733                 req,
734                 "the-action",
735                 "the-user-name",
736                 "the-group-name",
737                 LDB_ERR_OPERATIONS_ERROR);
738         assert_int_equal(3, json_object_size(json.root));
739
740         v = json_object_get(json.root, "type");
741         assert_non_null(v);
742         assert_string_equal("groupChange", json_string_value(v));
743
744         v = json_object_get(json.root, "timestamp");
745         assert_non_null(v);
746         assert_true(json_is_string(v));
747         check_timestamp(before, json_string_value(v));
748
749         audit = json_object_get(json.root, "groupChange");
750         assert_non_null(audit);
751         assert_true(json_is_object(audit));
752         assert_int_equal(10, json_object_size(audit));
753
754         o = json_object_get(audit, "version");
755         assert_non_null(o);
756         check_version(o, AUDIT_MAJOR, AUDIT_MINOR);
757
758         v = json_object_get(audit, "statusCode");
759         assert_non_null(v);
760         assert_true(json_is_integer(v));
761         assert_int_equal(LDB_ERR_OPERATIONS_ERROR, json_integer_value(v));
762
763         v = json_object_get(audit, "status");
764         assert_non_null(v);
765         assert_true(json_is_string(v));
766         assert_string_equal("Operations error", json_string_value(v));
767
768         v = json_object_get(audit, "user");
769         assert_non_null(v);
770         assert_true(json_is_string(v));
771         assert_string_equal("the-user-name", json_string_value(v));
772
773         v = json_object_get(audit, "group");
774         assert_non_null(v);
775         assert_true(json_is_string(v));
776         assert_string_equal("the-group-name", json_string_value(v));
777
778         v = json_object_get(audit, "action");
779         assert_non_null(v);
780         assert_true(json_is_string(v));
781         assert_string_equal("the-action", json_string_value(v));
782
783         json_free(&json);
784         TALLOC_FREE(ctx);
785 }
786
787 static void setup_ldb(
788         TALLOC_CTX *ctx,
789         struct ldb_context **ldb,
790         struct ldb_module **module,
791         const char *ip,
792         const char *session,
793         const char *sid)
794 {
795         struct tsocket_address *ts = NULL;
796         struct audit_context *context = NULL;
797
798         *ldb = ldb_init(ctx, NULL);
799         ldb_register_samba_handlers(*ldb);
800
801
802         *module = talloc_zero(ctx, struct ldb_module);
803         (*module)->ldb = *ldb;
804
805         context = talloc_zero(*module, struct audit_context);
806         context->send_events = true;
807         context->msg_ctx = (struct imessaging_context *) 0x01;
808
809         ldb_module_set_private(*module, context);
810
811         tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &ts);
812         ldb_set_opaque(*ldb, "remoteAddress", ts);
813
814         add_session_data(ctx, *ldb, session, sid);
815 }
816
817 /*
818  * Test the removal of a user from a group.
819  *
820  * The new element contains one group member
821  * The old element contains two group member
822  *
823  * Expect to see the removed entry logged.
824  *
825  * This test confirms bug 13664
826  * https://bugzilla.samba.org/show_bug.cgi?id=13664
827  */
828 static void test_log_membership_changes_removed(void **state)
829 {
830         struct ldb_context *ldb = NULL;
831         struct ldb_module  *module = NULL;
832         const char * const SID = "S-1-5-21-2470180966-3899876309-2637894779";
833         const char * const SESSION = "7130cb06-2062-6a1b-409e-3514c26b1773";
834         const char * const TRANSACTION = "7130cb06-2062-6a1b-409e-3514c26b1773";
835         const char * const IP = "127.0.0.1";
836         struct ldb_request *req = NULL;
837         struct ldb_message_element *new_el = NULL;
838         struct ldb_message_element *old_el = NULL;
839         int status = 0;
840         TALLOC_CTX *ctx = talloc_new(NULL);
841
842         setup_ldb(ctx, &ldb, &module, IP, SESSION, SID);
843
844         /*
845          * Build the ldb_request
846          */
847         req = talloc_zero(ctx, struct ldb_request);
848         req->operation =  LDB_ADD;
849         add_transaction_id(req, TRANSACTION);
850
851         /*
852          * Populate the new elements, containing one entry.
853          * Indicating that one element has been removed
854          */
855         new_el = talloc_zero(ctx, struct ldb_message_element);
856         new_el->num_values = 1;
857         new_el->values = talloc_zero_array(ctx, DATA_BLOB, 1);
858         new_el->values[0] = data_blob_string_const(
859                 "<GUID=081519b5-a709-44a0-bc95-dd4bfe809bf8>;"
860                 "CN=testuser131953,CN=Users,DC=addom,DC=samba,"
861                 "DC=example,DC=com");
862
863         /*
864          * Populate the old elements, with two elements
865          * The first is the same as the one in new elements.
866          */
867         old_el = talloc_zero(ctx, struct ldb_message_element);
868         old_el->num_values = 2;
869         old_el->values = talloc_zero_array(ctx, DATA_BLOB, 2);
870         old_el->values[0] = data_blob_string_const(
871                 "<GUID=cb8c2777-dcf5-419c-ab57-f645dbdf681b>;"
872                 "cn=grpadttstuser01,cn=users,DC=addom,"
873                 "DC=samba,DC=example,DC=com");
874         old_el->values[1] = data_blob_string_const(
875                 "<GUID=081519b5-a709-44a0-bc95-dd4bfe809bf8>;"
876                 "CN=testuser131953,CN=Users,DC=addom,DC=samba,"
877                 "DC=example,DC=com");
878
879         /*
880          * call log_membership_changes
881          */
882         messages_sent = 0;
883         log_membership_changes(module, req, new_el, old_el, status);
884
885         /*
886          * Check the results
887          */
888         assert_int_equal(1, messages_sent);
889
890         check_group_change_message(
891             0,
892             "cn=grpadttstuser01,cn=users,DC=addom,DC=samba,DC=example,DC=com",
893             "Removed");
894
895         /*
896          * Clean up
897          */
898         json_free(&messages[0]);
899         TALLOC_FREE(ctx);
900 }
901
902 /*
903  * Note: to run under valgrind us:
904  *       valgrind --suppressions=test_group_audit.valgrind bin/test_group_audit
905  *       This suppresses the errors generated because the ldb_modules are not
906  *       de-registered.
907  *
908  */
909 int main(void) {
910         const struct CMUnitTest tests[] = {
911                 cmocka_unit_test(test_audit_group_json),
912                 cmocka_unit_test(test_get_transaction_id),
913                 cmocka_unit_test(test_audit_group_hr),
914                 cmocka_unit_test(test_get_parsed_dns),
915                 cmocka_unit_test(test_dn_compare),
916                 cmocka_unit_test(test_get_primary_group_dn),
917                 cmocka_unit_test(test_log_membership_changes_removed),
918         };
919
920         cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
921         return cmocka_run_group_tests(tests, NULL, NULL);
922 }