dfc6d49cfa7b4294302d248b10bccccfbcf7d04b
[metze/samba/wip.git] / source4 / dsdb / samdb / ldb_modules / tests / test_unique_object_sids.c
1 /*
2    Unit tests for the unique objectSID code in unique_object_sids.c
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017
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_unique_object_sids_init(const char *version);
27 #include "../unique_object_sids.c"
28
29 #include "../libcli/security/dom_sid.h"
30 #include "librpc/gen_ndr/ndr_security.h"
31
32 #define TEST_BE "tdb"
33
34 #define DOMAIN_SID  "S-1-5-21-2470180966-3899876309-2637894779"
35 #define LOCAL_SID   "S-1-5-21-2470180966-3899876309-2637894779-1000"
36 #define FOREIGN_SID "S-1-5-21-2470180966-3899876309-2637894778-1000"
37
38 static struct ldb_request *last_request;
39
40 /*
41  * ldb_next_request mock, records the request passed in last_request
42  * so it can be examined in the test cases.
43  */
44 int ldb_next_request(
45         struct ldb_module *module,
46         struct ldb_request *request)
47 {
48         last_request = request;
49         return ldb_module_done(request, NULL, NULL, LDB_SUCCESS);
50 }
51
52 /*
53  * Test context
54  */
55 struct ldbtest_ctx {
56         struct tevent_context *ev;
57         struct ldb_context *ldb;
58         struct ldb_module *module;
59
60         const char *dbfile;
61         const char *lockfile;   /* lockfile is separate */
62
63         const char *dbpath;
64         struct dom_sid *domain_sid;
65 };
66
67 /*
68  * Remove any database files created by the tests
69  */
70 static void unlink_old_db(struct ldbtest_ctx *test_ctx)
71 {
72         int ret;
73
74         errno = 0;
75         ret = unlink(test_ctx->lockfile);
76         if (ret == -1 && errno != ENOENT) {
77                 fail();
78         }
79
80         errno = 0;
81         ret = unlink(test_ctx->dbfile);
82         if (ret == -1 && errno != ENOENT) {
83                 fail();
84         }
85 }
86
87 /*
88  * Empty module to signal the end of the module list
89  */
90 static const struct ldb_module_ops eol_ops = {
91         .name              = "eol",
92         .search            = NULL,
93         .add               = NULL,
94         .modify            = NULL,
95         .del               = NULL,
96         .rename            = NULL,
97         .init_context      = NULL
98 };
99
100 /*
101  * Test set up
102  */
103 static int setup(void **state)
104 {
105         struct ldbtest_ctx *test_ctx    = NULL;
106         struct ldb_module *eol          = NULL;
107         int rc;
108
109         test_ctx = talloc_zero(NULL, struct ldbtest_ctx);
110         assert_non_null(test_ctx);
111
112         test_ctx->ev = tevent_context_init(test_ctx);
113         assert_non_null(test_ctx->ev);
114
115         test_ctx->ldb = ldb_init(test_ctx, test_ctx->ev);
116         assert_non_null(test_ctx->ldb);
117
118         test_ctx->domain_sid = talloc_zero(test_ctx, struct dom_sid);
119         assert_non_null(test_ctx->domain_sid);
120         assert_true(string_to_sid(test_ctx->domain_sid, DOMAIN_SID));
121         ldb_set_opaque(test_ctx->ldb, "cache.domain_sid", test_ctx->domain_sid);
122
123         test_ctx->module = ldb_module_new(
124                 test_ctx,
125                 test_ctx->ldb,
126                 "unique_object_sids",
127                 &ldb_unique_object_sids_module_ops);
128         assert_non_null(test_ctx->module);
129         eol = ldb_module_new(test_ctx, test_ctx->ldb, "eol", &eol_ops);
130         assert_non_null(eol);
131         ldb_module_set_next(test_ctx->module, eol);
132
133         test_ctx->dbfile = talloc_strdup(test_ctx, "duptest.ldb");
134         assert_non_null(test_ctx->dbfile);
135
136         test_ctx->lockfile = talloc_asprintf(test_ctx, "%s-lock",
137                                              test_ctx->dbfile);
138         assert_non_null(test_ctx->lockfile);
139
140         test_ctx->dbpath = talloc_asprintf(test_ctx,
141                         TEST_BE"://%s", test_ctx->dbfile);
142         assert_non_null(test_ctx->dbpath);
143
144         unlink_old_db(test_ctx);
145
146         rc = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
147         assert_int_equal(rc, LDB_SUCCESS);
148
149         rc = unique_object_sids_init(test_ctx->module);
150         assert_int_equal(rc, LDB_SUCCESS);
151
152         *state = test_ctx;
153
154         last_request = NULL;
155         return 0;
156 }
157
158 /*
159  * Test clean up
160  */
161 static int teardown(void **state)
162 {
163         struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
164                                                         struct ldbtest_ctx);
165
166         unlink_old_db(test_ctx);
167         talloc_free(test_ctx);
168         return 0;
169 }
170
171 /*
172  * Add an objectSID in string form to the supplied message
173  *
174  *
175  */
176 static void add_sid(
177         struct ldb_message *msg,
178         const char *sid_str)
179 {
180         struct ldb_val v;
181         enum ndr_err_code ndr_err;
182         struct dom_sid *sid = NULL;
183
184         sid = talloc_zero(msg, struct dom_sid);
185         assert_non_null(sid);
186         assert_true(string_to_sid(sid, sid_str));
187         ndr_err = ndr_push_struct_blob(&v, msg, sid,
188                                        (ndr_push_flags_fn_t)ndr_push_dom_sid);
189         assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
190         assert_int_equal(0, ldb_msg_add_value(msg, "objectSID", &v, NULL));
191 }
192
193 /*
194  * The object is in the current local domain so it should have
195  * DB_FLAG_INTERNAL_UNIQUE_VALUE set
196  */
197 static void test_objectSID_in_domain(void **state)
198 {
199         struct ldbtest_ctx *test_ctx =
200                 talloc_get_type_abort(*state, struct ldbtest_ctx);
201         struct ldb_context *ldb                 = test_ctx->ldb;
202         struct ldb_message *msg                 = ldb_msg_new(test_ctx);
203         struct ldb_message_element *el          = NULL;
204         struct ldb_request *request             = NULL;
205         struct ldb_request *original_request    = NULL;
206         int rc;
207
208         msg->dn = ldb_dn_new(msg, ldb, "dc=test");
209         add_sid(msg, LOCAL_SID);
210
211         rc = ldb_build_add_req(
212                 &request,
213                 test_ctx->ldb,
214                 test_ctx,
215                 msg,
216                 NULL,
217                 NULL,
218                 ldb_op_default_callback,
219                 NULL);
220
221         assert_int_equal(rc, LDB_SUCCESS);
222         assert_non_null(request);
223         original_request = request;
224
225         rc = unique_object_sids_add(test_ctx->module, request);
226         assert_int_equal(rc, LDB_SUCCESS);
227
228         /*
229          * Check that a copy of the request was passed to the next module
230          * and not the original request
231          */
232         assert_ptr_not_equal(last_request, original_request);
233
234         /*
235          * Check the flag was set on the request passed to the next
236          * module
237          */
238         el = ldb_msg_find_element(last_request->op.add.message, "objectSID");
239         assert_non_null(el);
240         assert_true(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
241
242         /*
243          * Check the flag was not  set on the original request
244          */
245         el = ldb_msg_find_element(request->op.add.message, "objectSID");
246         assert_non_null(el);
247         assert_false(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
248
249 }
250
251 /*
252  * The object is not in the current local domain so it should NOT have
253  * DB_FLAG_INTERNAL_UNIQUE_VALUE set
254  */
255 static void test_objectSID_not_in_domain(void **state)
256 {
257         struct ldbtest_ctx *test_ctx =
258                 talloc_get_type_abort(*state, struct ldbtest_ctx);
259         struct ldb_context *ldb                 = test_ctx->ldb;
260         struct ldb_message *msg                 = ldb_msg_new(test_ctx);
261         struct ldb_message_element *el          = NULL;
262         struct ldb_request *request             = NULL;
263         struct ldb_request *original_request    = NULL;
264         int rc;
265
266         msg->dn = ldb_dn_new(msg, ldb, "dc=test");
267         add_sid(msg, FOREIGN_SID);
268
269         rc = ldb_build_add_req(
270                 &request,
271                 test_ctx->ldb,
272                 test_ctx,
273                 msg,
274                 NULL,
275                 NULL,
276                 ldb_op_default_callback,
277                 NULL);
278
279         assert_int_equal(rc, LDB_SUCCESS);
280         assert_non_null(request);
281         original_request = request;
282
283         rc = unique_object_sids_add(test_ctx->module, request);
284         assert_int_equal(rc, LDB_SUCCESS);
285
286         /*
287          * Check that the original request was passed to the next module
288          * and not a copy
289          */
290         assert_ptr_equal(last_request, original_request);
291
292         /*
293          * Check that the flag was not set on the objectSID element
294          */
295         el = ldb_msg_find_element(msg, "objectSID");
296         assert_non_null(el);
297         assert_false(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
298 }
299
300 /*
301  * No objectSID on the record so it should pass through the module untouched
302  *
303  */
304 static void test_no_objectSID(void **state)
305 {
306         struct ldbtest_ctx *test_ctx =
307                 talloc_get_type_abort(*state, struct ldbtest_ctx);
308         struct ldb_context *ldb                 = test_ctx->ldb;
309         struct ldb_message *msg                 = ldb_msg_new(test_ctx);
310         struct ldb_request *request             = NULL;
311         struct ldb_request *original_request    = NULL;
312         int rc;
313
314         msg->dn = ldb_dn_new(msg, ldb, "dc=test");
315         assert_int_equal(LDB_SUCCESS, ldb_msg_add_string(msg, "cn", "test"));
316
317         rc = ldb_build_add_req(
318                 &request,
319                 test_ctx->ldb,
320                 test_ctx,
321                 msg,
322                 NULL,
323                 NULL,
324                 ldb_op_default_callback,
325                 NULL);
326
327         assert_int_equal(rc, LDB_SUCCESS);
328         assert_non_null(request);
329         original_request = request;
330
331         rc = unique_object_sids_add(test_ctx->module, request);
332         assert_int_equal(rc, LDB_SUCCESS);
333
334         /*
335          * Check that the original request was passed to the next module
336          * and not a copy
337          */
338         assert_ptr_equal(last_request, original_request);
339
340 }
341
342 /*
343  * Attempt to modify an objectSID DSDB_CONTROL_REPLICATED_UPDATE_OID not set
344  * this should fail with LDB_ERR_UNWILLING_TO_PERFORM
345  */
346 static void test_modify_of_objectSID_not_replicated(void **state)
347 {
348         struct ldbtest_ctx *test_ctx =
349                 talloc_get_type_abort(*state, struct ldbtest_ctx);
350         struct ldb_context *ldb                 = test_ctx->ldb;
351         struct ldb_message *msg                 = ldb_msg_new(test_ctx);
352         struct ldb_request *request             = NULL;
353         int rc;
354
355         msg->dn = ldb_dn_new(msg, ldb, "dc=test");
356         add_sid(msg, LOCAL_SID);
357
358         rc = ldb_build_mod_req(
359                 &request,
360                 test_ctx->ldb,
361                 test_ctx,
362                 msg,
363                 NULL,
364                 NULL,
365                 ldb_op_default_callback,
366                 NULL);
367
368         assert_int_equal(rc, LDB_SUCCESS);
369         assert_non_null(request);
370
371         rc = unique_object_sids_modify(test_ctx->module, request);
372
373         assert_int_equal(rc, LDB_ERR_UNWILLING_TO_PERFORM);
374 }
375
376
377 /*
378  * Attempt to modify an objectSID DSDB_CONTROL_REPLICATED_UPDATE_OID set
379  * this should succeed
380  */
381 static void test_modify_of_objectSID_replicated(void **state)
382 {
383         struct ldbtest_ctx *test_ctx =
384                 talloc_get_type_abort(*state, struct ldbtest_ctx);
385         struct ldb_context *ldb                 = test_ctx->ldb;
386         struct ldb_message *msg                 = ldb_msg_new(test_ctx);
387         struct ldb_message_element *el          = NULL;
388         struct ldb_request *request             = NULL;
389         struct ldb_request *original_request    = NULL;
390         int rc;
391
392         msg->dn = ldb_dn_new(msg, ldb, "dc=test");
393         add_sid(msg, LOCAL_SID);
394
395         rc = ldb_build_mod_req(
396                 &request,
397                 test_ctx->ldb,
398                 test_ctx,
399                 msg,
400                 NULL,
401                 NULL,
402                 ldb_op_default_callback,
403                 NULL);
404         assert_int_equal(rc, LDB_SUCCESS);
405         assert_non_null(request);
406         original_request = request;
407
408         ldb_request_add_control(
409                 request,
410                 DSDB_CONTROL_REPLICATED_UPDATE_OID,
411                 false,
412                 NULL);
413
414         rc = unique_object_sids_modify(test_ctx->module, request);
415
416         assert_int_equal(rc, LDB_SUCCESS);
417
418         /*
419          * Check that a copy of the request was passed to the next module
420          * and not the original request
421          */
422         assert_ptr_not_equal(last_request, original_request);
423
424         /*
425          * Check the flag was set on the request passed to the next
426          * module
427          */
428         el = ldb_msg_find_element(last_request->op.add.message, "objectSID");
429         assert_non_null(el);
430         assert_true(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
431
432         /*
433          * Check the flag was not  set on the original request
434          */
435         el = ldb_msg_find_element(request->op.add.message, "objectSID");
436         assert_non_null(el);
437         assert_false(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
438
439 }
440
441 /*
442  * Test the a modify with no object SID is passed through correctly
443  *
444  */
445 static void test_modify_no_objectSID(void **state)
446 {
447         struct ldbtest_ctx *test_ctx =
448                 talloc_get_type_abort(*state, struct ldbtest_ctx);
449         struct ldb_context *ldb                 = test_ctx->ldb;
450         struct ldb_message *msg                 = ldb_msg_new(test_ctx);
451         struct ldb_request *request             = NULL;
452         struct ldb_request *original_request    = NULL;
453         int rc;
454
455         msg->dn = ldb_dn_new(msg, ldb, "dc=test");
456         assert_int_equal(LDB_SUCCESS, ldb_msg_add_string(msg, "cn", "test"));
457
458         rc = ldb_build_mod_req(
459                 &request,
460                 test_ctx->ldb,
461                 test_ctx,
462                 msg,
463                 NULL,
464                 NULL,
465                 ldb_op_default_callback,
466                 NULL);
467
468         assert_int_equal(rc, LDB_SUCCESS);
469         assert_non_null(request);
470         original_request = request;
471
472         rc = unique_object_sids_modify(test_ctx->module, request);
473         assert_int_equal(rc, LDB_SUCCESS);
474
475         /*
476          * Check that the original request was passed to the next module
477          * and not a copy
478          */
479         assert_ptr_equal(last_request, original_request);
480
481 }
482
483 int main(void) {
484         const struct CMUnitTest tests[] = {
485                 cmocka_unit_test_setup_teardown(
486                         test_objectSID_in_domain,
487                         setup,
488                         teardown),
489                 cmocka_unit_test_setup_teardown(
490                         test_objectSID_not_in_domain,
491                         setup,
492                         teardown),
493                 cmocka_unit_test_setup_teardown(
494                         test_no_objectSID,
495                         setup,
496                         teardown),
497                 cmocka_unit_test_setup_teardown(
498                         test_modify_no_objectSID,
499                         setup,
500                         teardown),
501                 cmocka_unit_test_setup_teardown(
502                         test_modify_of_objectSID_not_replicated,
503                         setup,
504                         teardown),
505                 cmocka_unit_test_setup_teardown(
506                         test_modify_of_objectSID_replicated,
507                         setup,
508                         teardown),
509         };
510
511         cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
512         return cmocka_run_group_tests(tests, NULL, NULL);
513 }