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