Line data Source code
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 17 : int ldb_next_request(
45 : struct ldb_module *module,
46 : struct ldb_request *request)
47 : {
48 17 : last_request = request;
49 17 : 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 12 : static void unlink_old_db(struct ldbtest_ctx *test_ctx)
71 : {
72 12 : int ret;
73 :
74 12 : errno = 0;
75 12 : ret = unlink(test_ctx->lockfile);
76 12 : if (ret == -1 && errno != ENOENT) {
77 0 : fail();
78 : }
79 :
80 12 : errno = 0;
81 12 : ret = unlink(test_ctx->dbfile);
82 12 : if (ret == -1 && errno != ENOENT) {
83 0 : fail();
84 : }
85 12 : }
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 6 : static int setup(void **state)
104 : {
105 6 : struct ldbtest_ctx *test_ctx = NULL;
106 6 : struct ldb_module *eol = NULL;
107 6 : int rc;
108 :
109 6 : test_ctx = talloc_zero(NULL, struct ldbtest_ctx);
110 6 : assert_non_null(test_ctx);
111 :
112 6 : test_ctx->ev = tevent_context_init(test_ctx);
113 6 : assert_non_null(test_ctx->ev);
114 :
115 6 : test_ctx->ldb = ldb_init(test_ctx, test_ctx->ev);
116 6 : assert_non_null(test_ctx->ldb);
117 :
118 6 : test_ctx->domain_sid = talloc_zero(test_ctx, struct dom_sid);
119 6 : assert_non_null(test_ctx->domain_sid);
120 6 : assert_true(string_to_sid(test_ctx->domain_sid, DOMAIN_SID));
121 6 : ldb_set_opaque(test_ctx->ldb, "cache.domain_sid", test_ctx->domain_sid);
122 :
123 6 : 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 6 : assert_non_null(test_ctx->module);
129 6 : eol = ldb_module_new(test_ctx, test_ctx->ldb, "eol", &eol_ops);
130 6 : assert_non_null(eol);
131 6 : ldb_module_set_next(test_ctx->module, eol);
132 :
133 6 : test_ctx->dbfile = talloc_strdup(test_ctx, "duptest.ldb");
134 6 : assert_non_null(test_ctx->dbfile);
135 :
136 6 : test_ctx->lockfile = talloc_asprintf(test_ctx, "%s-lock",
137 : test_ctx->dbfile);
138 6 : assert_non_null(test_ctx->lockfile);
139 :
140 6 : test_ctx->dbpath = talloc_asprintf(test_ctx,
141 : TEST_BE"://%s", test_ctx->dbfile);
142 6 : assert_non_null(test_ctx->dbpath);
143 :
144 6 : unlink_old_db(test_ctx);
145 :
146 6 : rc = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
147 6 : assert_int_equal(rc, LDB_SUCCESS);
148 :
149 6 : rc = unique_object_sids_init(test_ctx->module);
150 6 : assert_int_equal(rc, LDB_SUCCESS);
151 :
152 6 : *state = test_ctx;
153 :
154 6 : last_request = NULL;
155 6 : return 0;
156 : }
157 :
158 : /*
159 : * Test clean up
160 : */
161 6 : static int teardown(void **state)
162 : {
163 6 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
164 : struct ldbtest_ctx);
165 :
166 6 : unlink_old_db(test_ctx);
167 6 : talloc_free(test_ctx);
168 6 : return 0;
169 : }
170 :
171 : /*
172 : * Add an objectSID in string form to the supplied message
173 : *
174 : *
175 : */
176 4 : static void add_sid(
177 : struct ldb_message *msg,
178 : const char *sid_str)
179 : {
180 4 : struct ldb_val v;
181 4 : enum ndr_err_code ndr_err;
182 4 : struct dom_sid *sid = NULL;
183 :
184 4 : sid = talloc_zero(msg, struct dom_sid);
185 4 : assert_non_null(sid);
186 4 : assert_true(string_to_sid(sid, sid_str));
187 4 : ndr_err = ndr_push_struct_blob(&v, msg, sid,
188 : (ndr_push_flags_fn_t)ndr_push_dom_sid);
189 4 : assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
190 4 : assert_int_equal(0, ldb_msg_add_value(msg, "objectSID", &v, NULL));
191 4 : }
192 :
193 : /*
194 : * The object is in the current local domain so it should have
195 : * DB_FLAG_INTERNAL_UNIQUE_VALUE set
196 : */
197 1 : static void test_objectSID_in_domain(void **state)
198 : {
199 1 : struct ldbtest_ctx *test_ctx =
200 1 : talloc_get_type_abort(*state, struct ldbtest_ctx);
201 1 : struct ldb_context *ldb = test_ctx->ldb;
202 1 : struct ldb_message *msg = ldb_msg_new(test_ctx);
203 1 : struct ldb_message_element *el = NULL;
204 1 : struct ldb_request *request = NULL;
205 1 : struct ldb_request *original_request = NULL;
206 1 : int rc;
207 :
208 1 : msg->dn = ldb_dn_new(msg, ldb, "dc=test");
209 1 : add_sid(msg, LOCAL_SID);
210 :
211 1 : 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 1 : assert_int_equal(rc, LDB_SUCCESS);
222 1 : assert_non_null(request);
223 1 : original_request = request;
224 :
225 1 : rc = unique_object_sids_add(test_ctx->module, request);
226 1 : 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 1 : 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 1 : el = ldb_msg_find_element(last_request->op.add.message, "objectSID");
239 1 : assert_non_null(el);
240 1 : 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 1 : el = ldb_msg_find_element(request->op.add.message, "objectSID");
246 1 : assert_non_null(el);
247 1 : assert_false(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
248 :
249 1 : }
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 1 : static void test_objectSID_not_in_domain(void **state)
256 : {
257 1 : struct ldbtest_ctx *test_ctx =
258 1 : talloc_get_type_abort(*state, struct ldbtest_ctx);
259 1 : struct ldb_context *ldb = test_ctx->ldb;
260 1 : struct ldb_message *msg = ldb_msg_new(test_ctx);
261 1 : struct ldb_message_element *el = NULL;
262 1 : struct ldb_request *request = NULL;
263 1 : struct ldb_request *original_request = NULL;
264 1 : int rc;
265 :
266 1 : msg->dn = ldb_dn_new(msg, ldb, "dc=test");
267 1 : add_sid(msg, FOREIGN_SID);
268 :
269 1 : 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 1 : assert_int_equal(rc, LDB_SUCCESS);
280 1 : assert_non_null(request);
281 1 : original_request = request;
282 :
283 1 : rc = unique_object_sids_add(test_ctx->module, request);
284 1 : 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 1 : assert_ptr_equal(last_request, original_request);
291 :
292 : /*
293 : * Check that the flag was not set on the objectSID element
294 : */
295 1 : el = ldb_msg_find_element(msg, "objectSID");
296 1 : assert_non_null(el);
297 1 : assert_false(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
298 1 : }
299 :
300 : /*
301 : * No objectSID on the record so it should pass through the module untouched
302 : *
303 : */
304 1 : static void test_no_objectSID(void **state)
305 : {
306 1 : struct ldbtest_ctx *test_ctx =
307 1 : talloc_get_type_abort(*state, struct ldbtest_ctx);
308 1 : struct ldb_context *ldb = test_ctx->ldb;
309 1 : struct ldb_message *msg = ldb_msg_new(test_ctx);
310 1 : struct ldb_request *request = NULL;
311 1 : struct ldb_request *original_request = NULL;
312 1 : int rc;
313 :
314 1 : msg->dn = ldb_dn_new(msg, ldb, "dc=test");
315 1 : assert_int_equal(LDB_SUCCESS, ldb_msg_add_string(msg, "cn", "test"));
316 :
317 1 : 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 1 : assert_int_equal(rc, LDB_SUCCESS);
328 1 : assert_non_null(request);
329 1 : original_request = request;
330 :
331 1 : rc = unique_object_sids_add(test_ctx->module, request);
332 1 : 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 1 : assert_ptr_equal(last_request, original_request);
339 :
340 1 : }
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 1 : static void test_modify_of_objectSID_not_replicated(void **state)
347 : {
348 1 : struct ldbtest_ctx *test_ctx =
349 1 : talloc_get_type_abort(*state, struct ldbtest_ctx);
350 1 : struct ldb_context *ldb = test_ctx->ldb;
351 1 : struct ldb_message *msg = ldb_msg_new(test_ctx);
352 1 : struct ldb_request *request = NULL;
353 1 : int rc;
354 :
355 1 : msg->dn = ldb_dn_new(msg, ldb, "dc=test");
356 1 : add_sid(msg, LOCAL_SID);
357 :
358 1 : 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 1 : assert_int_equal(rc, LDB_SUCCESS);
369 1 : assert_non_null(request);
370 :
371 1 : rc = unique_object_sids_modify(test_ctx->module, request);
372 :
373 1 : assert_int_equal(rc, LDB_ERR_UNWILLING_TO_PERFORM);
374 1 : }
375 :
376 :
377 : /*
378 : * Attempt to modify an objectSID DSDB_CONTROL_REPLICATED_UPDATE_OID set
379 : * this should succeed
380 : */
381 1 : static void test_modify_of_objectSID_replicated(void **state)
382 : {
383 1 : struct ldbtest_ctx *test_ctx =
384 1 : talloc_get_type_abort(*state, struct ldbtest_ctx);
385 1 : struct ldb_context *ldb = test_ctx->ldb;
386 1 : struct ldb_message *msg = ldb_msg_new(test_ctx);
387 1 : struct ldb_message_element *el = NULL;
388 1 : struct ldb_request *request = NULL;
389 1 : struct ldb_request *original_request = NULL;
390 1 : int rc;
391 :
392 1 : msg->dn = ldb_dn_new(msg, ldb, "dc=test");
393 1 : add_sid(msg, LOCAL_SID);
394 :
395 1 : 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 1 : assert_int_equal(rc, LDB_SUCCESS);
405 1 : assert_non_null(request);
406 1 : original_request = request;
407 :
408 1 : rc = ldb_request_add_control(
409 : request,
410 : DSDB_CONTROL_REPLICATED_UPDATE_OID,
411 : false,
412 : NULL);
413 1 : assert_int_equal(rc, LDB_SUCCESS);
414 :
415 1 : rc = unique_object_sids_modify(test_ctx->module, request);
416 :
417 1 : assert_int_equal(rc, LDB_SUCCESS);
418 :
419 : /*
420 : * Check that a copy of the request was passed to the next module
421 : * and not the original request
422 : */
423 1 : assert_ptr_not_equal(last_request, original_request);
424 :
425 : /*
426 : * Check the flag was set on the request passed to the next
427 : * module
428 : */
429 1 : el = ldb_msg_find_element(last_request->op.add.message, "objectSID");
430 1 : assert_non_null(el);
431 1 : assert_true(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
432 :
433 : /*
434 : * Check the flag was not set on the original request
435 : */
436 1 : el = ldb_msg_find_element(request->op.add.message, "objectSID");
437 1 : assert_non_null(el);
438 1 : assert_false(el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX);
439 :
440 1 : }
441 :
442 : /*
443 : * Test that a modify with no object SID is passed through correctly
444 : *
445 : */
446 1 : static void test_modify_no_objectSID(void **state)
447 : {
448 1 : struct ldbtest_ctx *test_ctx =
449 1 : talloc_get_type_abort(*state, struct ldbtest_ctx);
450 1 : struct ldb_context *ldb = test_ctx->ldb;
451 1 : struct ldb_message *msg = ldb_msg_new(test_ctx);
452 1 : struct ldb_request *request = NULL;
453 1 : struct ldb_request *original_request = NULL;
454 1 : int rc;
455 :
456 1 : msg->dn = ldb_dn_new(msg, ldb, "dc=test");
457 1 : assert_int_equal(LDB_SUCCESS, ldb_msg_add_string(msg, "cn", "test"));
458 :
459 1 : rc = ldb_build_mod_req(
460 : &request,
461 : test_ctx->ldb,
462 : test_ctx,
463 : msg,
464 : NULL,
465 : NULL,
466 : ldb_op_default_callback,
467 : NULL);
468 :
469 1 : assert_int_equal(rc, LDB_SUCCESS);
470 1 : assert_non_null(request);
471 1 : original_request = request;
472 :
473 1 : rc = unique_object_sids_modify(test_ctx->module, request);
474 1 : assert_int_equal(rc, LDB_SUCCESS);
475 :
476 : /*
477 : * Check that the original request was passed to the next module
478 : * and not a copy
479 : */
480 1 : assert_ptr_equal(last_request, original_request);
481 :
482 1 : }
483 :
484 1 : int main(void) {
485 1 : const struct CMUnitTest tests[] = {
486 : cmocka_unit_test_setup_teardown(
487 : test_objectSID_in_domain,
488 : setup,
489 : teardown),
490 : cmocka_unit_test_setup_teardown(
491 : test_objectSID_not_in_domain,
492 : setup,
493 : teardown),
494 : cmocka_unit_test_setup_teardown(
495 : test_no_objectSID,
496 : setup,
497 : teardown),
498 : cmocka_unit_test_setup_teardown(
499 : test_modify_no_objectSID,
500 : setup,
501 : teardown),
502 : cmocka_unit_test_setup_teardown(
503 : test_modify_of_objectSID_not_replicated,
504 : setup,
505 : teardown),
506 : cmocka_unit_test_setup_teardown(
507 : test_modify_of_objectSID_replicated,
508 : setup,
509 : teardown),
510 : };
511 :
512 1 : cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
513 1 : return cmocka_run_group_tests(tests, NULL, NULL);
514 : }
|