s4-drs: accept zero revision in drs selftest
[samba.git] / source4 / torture / drs / unit / schemainfo_tests.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    DRSUAPI schemaInfo unit tests
5
6    Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2010
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "torture/smbtorture.h"
25 #include "dsdb/samdb/samdb.h"
26 #include "dsdb/samdb/ldb_modules/util.h"
27 #include "lib/ldb_wrap.h"
28 #include "lib/ldb/include/ldb_module.h"
29 #include "torture/rpc/drsuapi.h"
30 #include "librpc/ndr/libndr.h"
31 #include "param/param.h"
32
33
34 /**
35  * schemaInfo to init ldb context with
36  *   Rev:  01
37  *   GUID: 071c82fd-45c7-4351-a3db-51f75a630a7f
38  */
39 #define SCHEMA_INFO_INIT_STR            "FF0000000100000000000000000000000000000000"
40
41 /**
42  * Default schema_info string to be used for testing
43  *   Rev:  01
44  *   GUID: 071c82fd-45c7-4351-a3db-51f75a630a7f
45  */
46 #define SCHEMA_INFO_DEFAULT_STR         "FF00000001FD821C07C7455143A3DB51F75A630A7F"
47
48 /**
49  * Schema info data to test with
50  */
51 struct schemainfo_data {
52         DATA_BLOB       ndr_blob;
53         struct dsdb_schema_info schi;
54         WERROR          werr_expected;
55         bool            test_both_ways;
56 };
57
58 /**
59  * Schema info test data in human-readable format (... kind of)
60  */
61 static const struct {
62         const char      *schema_info_str;
63         uint32_t        revision;
64         const char      *guid_str;
65         WERROR          werr_expected;
66         bool            test_both_ways;
67 } _schemainfo_test_data[] = {
68         {
69                 .schema_info_str = "FF00000001FD821C07C7455143A3DB51F75A630A7F",
70                 .revision = 1,
71                 .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f",
72                 .werr_expected = WERR_OK,
73                 .test_both_ways = true
74         },
75         {
76                 .schema_info_str = "FFFFFFFFFFFD821C07C7455143A3DB51F75A630A7F",
77                 .revision = 0xFFFFFFFF,
78                 .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f",
79                 .werr_expected = WERR_OK,
80                 .test_both_ways = true
81         },
82 #if 0
83         /* removed until kamen can take a look - revision 0 is sent by
84          * w2k8r2, and we need to accept it, possibly only when the
85          * other fields are zero */
86         { /* revision > 0 */
87                 .schema_info_str = "FF00000000FD821C07C7455143A3DB51F75A630A7F",
88                 .revision = 0,
89                 .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f",
90                 .werr_expected = WERR_INVALID_PARAMETER,
91                 .test_both_ways = true
92         },
93 #endif
94         { /* len == 21 */
95                 .schema_info_str = "FF00000001FD821C07C7455143A3DB51F75A630A7F00",
96                 .revision = 1,
97                 .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f",
98                 .werr_expected = WERR_INVALID_PARAMETER,
99                 .test_both_ways = false
100         },
101         { /* marker == FF */
102                 .schema_info_str = "AA00000001FD821C07C7455143A3DB51F75A630A7F",
103                 .revision = 1,
104                 .guid_str = "071c82fd-45c7-4351-a3db-51f75a630a7f",
105                 .werr_expected = WERR_INVALID_PARAMETER,
106                 .test_both_ways = false
107         }
108 };
109
110 /**
111  * Private data to be shared among all test in Test case
112  */
113 struct drsut_schemainfo_data {
114         struct ldb_context *ldb;
115         struct ldb_module  *ldb_module;
116         struct dsdb_schema *schema;
117
118         /* Initial schemaInfo set in ldb to test with */
119         struct dsdb_schema_info *schema_info;
120
121         uint32_t test_data_count;
122         struct schemainfo_data *test_data;
123 };
124
125 /**
126  * torture macro to assert for equal dsdb_schema_info's
127  */
128 #define torture_assert_schema_info_equal(torture_ctx,got,expected,cmt)\
129         do { const struct dsdb_schema_info *__got = (got), *__expected = (expected); \
130         if (__got->revision != __expected->revision) { \
131                 torture_result(torture_ctx, TORTURE_FAIL, \
132                                __location__": "#got".revision %d did not match "#expected".revision %d: %s", \
133                                (int)__got->revision, (int)__expected->revision, cmt); \
134                 return false; \
135         } \
136         if (!GUID_equal(&__got->invocation_id, &__expected->invocation_id)) { \
137                 torture_result(torture_ctx, TORTURE_FAIL, \
138                                __location__": "#got".invocation_id did not match "#expected".invocation_id: %s", cmt); \
139                 return false; \
140         } \
141         } while(0)
142
143 /*
144  * forward declaration for internal functions
145  */
146 static bool _drsut_ldb_schema_info_reset(struct torture_context *tctx,
147                                          struct ldb_context *ldb,
148                                          const char *schema_info_str,
149                                          bool in_setup);
150
151
152 /**
153  * Creates dsdb_schema_info object based on NDR data
154  * passed as hex string
155  */
156 static bool _drsut_schemainfo_new(struct torture_context *tctx,
157                                   const char *schema_info_str, struct dsdb_schema_info **_si)
158 {
159         WERROR werr;
160         DATA_BLOB blob;
161
162         blob = strhex_to_data_blob(tctx, schema_info_str);
163         if (!blob.data) {
164                 torture_comment(tctx, "Not enough memory!\n");
165                 return false;
166         }
167
168         werr = dsdb_schema_info_from_blob(&blob, tctx, _si);
169         if (!W_ERROR_IS_OK(werr)) {
170                 torture_comment(tctx,
171                                 "Failed to create dsdb_schema_info object for %s: %s",
172                                 schema_info_str,
173                                 win_errstr(werr));
174                 return false;
175         }
176
177         data_blob_free(&blob);
178
179         return true;
180 }
181
182 /**
183  * Creates dsdb_schema_info object based on predefined data
184  * Function is public as it is intended to be used by other
185  * tests (e.g. prefixMap tests)
186  */
187 bool drsut_schemainfo_new(struct torture_context *tctx, struct dsdb_schema_info **_si)
188 {
189         return _drsut_schemainfo_new(tctx, SCHEMA_INFO_DEFAULT_STR, _si);
190 }
191
192
193 /*
194  * Tests dsdb_schema_info_from_blob()
195  */
196 static bool test_dsdb_schema_info_from_blob(struct torture_context *tctx,
197                                             struct drsut_schemainfo_data *priv)
198 {
199         int i;
200         WERROR werr;
201         char *msg;
202         struct dsdb_schema_info *schema_info;
203         TALLOC_CTX *mem_ctx;
204
205         mem_ctx = talloc_new(priv);
206         torture_assert(tctx, mem_ctx, "Not enough memory!");
207
208         for (i = 0; i < priv->test_data_count; i++) {
209                 struct schemainfo_data *data = &priv->test_data[i];
210
211                 msg = talloc_asprintf(tctx, "dsdb_schema_info_from_blob() [%d]-[%s]",
212                                       i, _schemainfo_test_data[i].schema_info_str);
213
214                 werr = dsdb_schema_info_from_blob(&data->ndr_blob, mem_ctx, &schema_info);
215                 torture_assert_werr_equal(tctx, werr, data->werr_expected, msg);
216
217                 /* test returned data */
218                 if (W_ERROR_IS_OK(werr)) {
219                         torture_assert_schema_info_equal(tctx,
220                                                          schema_info, &data->schi,
221                                                          "after dsdb_schema_info_from_blob() call");
222                 }
223         }
224
225         talloc_free(mem_ctx);
226
227         return true;
228 }
229
230 /*
231  * Tests dsdb_blob_from_schema_info()
232  */
233 static bool test_dsdb_blob_from_schema_info(struct torture_context *tctx,
234                                             struct drsut_schemainfo_data *priv)
235 {
236         int i;
237         WERROR werr;
238         char *msg;
239         DATA_BLOB ndr_blob;
240         TALLOC_CTX *mem_ctx;
241
242         mem_ctx = talloc_new(priv);
243         torture_assert(tctx, mem_ctx, "Not enough memory!");
244
245         for (i = 0; i < priv->test_data_count; i++) {
246                 struct schemainfo_data *data = &priv->test_data[i];
247
248                 /* not all test are valid reverse type of conversion */
249                 if (!data->test_both_ways) {
250                         continue;
251                 }
252
253                 msg = talloc_asprintf(tctx, "dsdb_blob_from_schema_info() [%d]-[%s]",
254                                       i, _schemainfo_test_data[i].schema_info_str);
255
256                 werr = dsdb_blob_from_schema_info(&data->schi, mem_ctx, &ndr_blob);
257                 torture_assert_werr_equal(tctx, werr, data->werr_expected, msg);
258
259                 /* test returned data */
260                 if (W_ERROR_IS_OK(werr)) {
261                         torture_assert_data_blob_equal(tctx,
262                                                        ndr_blob, data->ndr_blob,
263                                                        "dsdb_blob_from_schema_info()");
264                 }
265         }
266
267         talloc_free(mem_ctx);
268
269         return true;
270 }
271
272 /*
273  * Tests dsdb_module_schema_info_blob_read()
274  *   and dsdb_module_schema_info_blob_write()
275  */
276 static bool test_dsdb_module_schema_info_blob_rw(struct torture_context *tctx,
277                                                 struct drsut_schemainfo_data *priv)
278 {
279         WERROR werr;
280         DATA_BLOB blob_write;
281         DATA_BLOB blob_read;
282
283         /* reset schmeInfo to know value */
284         torture_assert(tctx,
285                        _drsut_ldb_schema_info_reset(tctx, priv->ldb, SCHEMA_INFO_INIT_STR, false),
286                        "_drsut_ldb_schema_info_reset() failed");
287
288         /* write tests' default schemaInfo */
289         blob_write = strhex_to_data_blob(priv, SCHEMA_INFO_DEFAULT_STR);
290         torture_assert(tctx, blob_write.data, "Not enough memory!");
291
292         werr = dsdb_module_schema_info_blob_write(priv->ldb_module,
293                                                   DSDB_FLAG_TOP_MODULE,
294                                                   &blob_write);
295         torture_assert_werr_ok(tctx, werr, "dsdb_module_schema_info_blob_write() failed");
296
297         werr = dsdb_module_schema_info_blob_read(priv->ldb_module, DSDB_FLAG_TOP_MODULE,
298                                                  priv, &blob_read);
299         torture_assert_werr_ok(tctx, werr, "dsdb_module_schema_info_blob_read() failed");
300
301         /* check if we get what we wrote */
302         torture_assert_data_blob_equal(tctx, blob_read, blob_write,
303                                        "Write/Read of schemeInfo blob failed");
304
305         return true;
306 }
307
308 /*
309  * Tests dsdb_schema_update_schema_info()
310  */
311 static bool test_dsdb_module_schema_info_update(struct torture_context *tctx,
312                                                 struct drsut_schemainfo_data *priv)
313 {
314         WERROR werr;
315         DATA_BLOB blob;
316         struct dsdb_schema_info *schema_info;
317
318         /* reset schmeInfo to know value */
319         torture_assert(tctx,
320                        _drsut_ldb_schema_info_reset(tctx, priv->ldb, SCHEMA_INFO_INIT_STR, false),
321                        "_drsut_ldb_schema_info_reset() failed");
322
323         werr = dsdb_module_schema_info_update(priv->ldb_module,
324                                               priv->schema,
325                                               DSDB_FLAG_TOP_MODULE | DSDB_FLAG_AS_SYSTEM);
326         torture_assert_werr_ok(tctx, werr, "dsdb_module_schema_info_update() failed");
327
328         /* get updated schemaInfo */
329         werr = dsdb_module_schema_info_blob_read(priv->ldb_module, DSDB_FLAG_TOP_MODULE,
330                                                  priv, &blob);
331         torture_assert_werr_ok(tctx, werr, "dsdb_module_schema_info_blob_read() failed");
332
333         werr = dsdb_schema_info_from_blob(&blob, priv, &schema_info);
334         torture_assert_werr_ok(tctx, werr, "dsdb_schema_info_from_blob() failed");
335
336         /* decrement revision to be able to compare
337          * against default schemaInfo later */
338         schema_info->revision--;
339
340         /* check against default schema_info */
341         torture_assert_schema_info_equal(tctx, schema_info, priv->schema_info,
342                                           "schemaInfo attribute no updated correctly");
343
344         return true;
345 }
346
347 /*
348  * Tests dsdb_schema_info_create()
349  */
350 static bool test_dsdb_schema_info_create(struct torture_context *tctx,
351                                          struct drsut_schemainfo_data *priv)
352 {
353         WERROR werr;
354         struct dsdb_schema_info *schema_info = NULL;
355
356         werr = dsdb_schema_info_create(priv->ldb, true, priv, &schema_info);
357         torture_assert_werr_ok(tctx, werr, "dsdb_schema_info_create() failed");
358
359         torture_assert(tctx, schema_info, "schema_info is NULL after dsdb_schema_info_create()");
360         torture_assert_int_equal(tctx, schema_info->revision, 1, "Invalid schemaInfo revision");
361         torture_assert(tctx,
362                        GUID_equal(&schema_info->invocation_id, &priv->schema_info->invocation_id),
363                        "Invalid invocationId returned");
364
365         talloc_free(schema_info);
366
367         return true;
368 }
369
370
371 /**
372  * Reset schemaInfo record to know value
373  */
374 static bool _drsut_ldb_schema_info_reset(struct torture_context *tctx,
375                                          struct ldb_context *ldb,
376                                          const char *schema_info_str,
377                                          bool in_setup)
378 {
379         bool bret = true;
380         int ldb_err;
381         DATA_BLOB blob;
382         struct ldb_message *msg;
383         TALLOC_CTX *mem_ctx = talloc_new(tctx);
384
385         blob = strhex_to_data_blob(mem_ctx, schema_info_str);
386         torture_assert_goto(tctx, blob.data, bret, DONE, "Not enough memory!");
387
388         msg = ldb_msg_new(mem_ctx);
389         torture_assert_goto(tctx, msg, bret, DONE, "Not enough memory!");
390
391         msg->dn = ldb_get_schema_basedn(ldb);
392         ldb_err = ldb_msg_add_value(msg, "schemaInfo", &blob, NULL);
393         torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE,
394                                       "ldb_msg_add_value() failed");
395
396         if (in_setup) {
397                 ldb_err = ldb_add(ldb, msg);
398         } else {
399                 ldb_err = dsdb_replace(ldb, msg, DSDB_MODIFY_PERMISSIVE);
400         }
401         torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE,
402                                       "dsdb_replace() failed");
403
404 DONE:
405         talloc_free(mem_ctx);
406         return bret;
407 }
408
409 /**
410  * Prepare temporary LDB and opens it
411  */
412 static bool _drsut_ldb_setup(struct torture_context *tctx, struct drsut_schemainfo_data *priv)
413 {
414         int ldb_err;
415         char *ldb_url;
416         bool bret = true;
417         char *tempdir = NULL;
418         NTSTATUS status;
419         TALLOC_CTX* mem_ctx;
420
421         mem_ctx = talloc_new(priv);
422         torture_assert(tctx, mem_ctx, "Not enough memory!");
423
424         status = torture_temp_dir(tctx, "drs_", &tempdir);
425         torture_assert_ntstatus_ok_goto(tctx, status, bret, DONE, "creating temp dir");
426
427         ldb_url = talloc_asprintf(priv, "%s/drs_schemainfo.ldb", tempdir);
428         torture_assert_goto(tctx, ldb_url, bret, DONE, "Not enough memory!");
429
430         /* create LDB */
431         priv->ldb = ldb_wrap_connect(priv, tctx->ev, tctx->lp_ctx,
432                                      ldb_url, NULL, NULL, 0);
433         torture_assert_goto(tctx, priv->ldb, bret, DONE,  "ldb_wrap_connect() failed");
434
435         /* set some schemaNamingContext */
436         ldb_err = ldb_set_opaque(priv->ldb,
437                                  "schemaNamingContext",
438                                  ldb_dn_new(priv->ldb, priv->ldb, "CN=Schema,CN=Config"));
439         torture_assert_int_equal_goto(tctx, ldb_err, LDB_SUCCESS, bret, DONE,
440                                       "ldb_set_opaque() failed");
441
442         /* add schemaInfo attribute so tested layer could work properly */
443         torture_assert_goto(tctx,
444                             _drsut_ldb_schema_info_reset(tctx, priv->ldb, SCHEMA_INFO_INIT_STR, true),
445                             bret, DONE,
446                             "_drsut_ldb_schema_info_reset() failed");
447
448 DONE:
449         talloc_free(tempdir);
450         talloc_free(mem_ctx);
451         return bret;
452 }
453
454 /*
455  * Setup/Teardown for test case
456  */
457 static bool torture_drs_unit_schemainfo_setup(struct torture_context *tctx,
458                                               struct drsut_schemainfo_data **_priv)
459 {
460         int i;
461         int ldb_err;
462         NTSTATUS status;
463         DATA_BLOB ndr_blob;
464         struct GUID guid;
465         struct drsut_schemainfo_data *priv;
466
467         priv = talloc_zero(tctx, struct drsut_schemainfo_data);
468         torture_assert(tctx, priv, "Not enough memory!");
469
470         /* returned allocated pointer here
471          * teardown() will be called even in case of failure,
472          * so we'll get a changes to clean up  */
473         *_priv = priv;
474
475         /* create initial schemaInfo */
476         torture_assert(tctx,
477                        _drsut_schemainfo_new(tctx, SCHEMA_INFO_DEFAULT_STR, &priv->schema_info),
478                        "Failed to create schema_info test object");
479
480         /* create data to test with */
481         priv->test_data_count = ARRAY_SIZE(_schemainfo_test_data);
482         priv->test_data = talloc_array(tctx, struct schemainfo_data, priv->test_data_count);
483
484         for (i = 0; i < ARRAY_SIZE(_schemainfo_test_data); i++) {
485                 struct schemainfo_data *data = &priv->test_data[i];
486
487                 ndr_blob = strhex_to_data_blob(priv,
488                                                _schemainfo_test_data[i].schema_info_str);
489                 torture_assert(tctx, ndr_blob.data, "Not enough memory!");
490
491                 status = GUID_from_string(_schemainfo_test_data[i].guid_str, &guid);
492                 torture_assert_ntstatus_ok(tctx, status,
493                                            talloc_asprintf(tctx,
494                                                            "GUID_from_string() failed for %s",
495                                                            _schemainfo_test_data[i].guid_str));
496
497                 data->ndr_blob           = ndr_blob;
498                 data->schi.invocation_id = guid;
499                 data->schi.revision      = _schemainfo_test_data[i].revision;
500                 data->werr_expected      = _schemainfo_test_data[i].werr_expected;
501                 data->test_both_ways     = _schemainfo_test_data[i].test_both_ways;
502
503         }
504
505         /* create temporary LDB and populate with data */
506         if (!_drsut_ldb_setup(tctx, priv)) {
507                 return false;
508         }
509
510         /* create ldb_module mockup object */
511         priv->ldb_module = ldb_module_new(priv, priv->ldb, "schemaInfo_test_module", NULL);
512         torture_assert(tctx, priv->ldb_module, "Not enough memory!");
513
514         /* create schema mockup object */
515         priv->schema = dsdb_new_schema(priv, lp_iconv_convenience(tctx->lp_ctx));
516
517         /* pre-cache invocationId for samdb_ntds_invocation_id()
518          * to work with our mock ldb */
519         ldb_err = ldb_set_opaque(priv->ldb, "cache.invocation_id",
520                                  &priv->schema_info->invocation_id);
521         torture_assert_int_equal(tctx, ldb_err, LDB_SUCCESS, "ldb_set_opaque() failed");
522
523         /* Perform all tests in transactions so that
524          * underlying modify calls not to fail */
525         ldb_err = ldb_transaction_start(priv->ldb);
526         torture_assert_int_equal(tctx,
527                                  ldb_err,
528                                  LDB_SUCCESS,
529                                  "ldb_transaction_start() failed");
530
531         return true;
532 }
533
534 static bool torture_drs_unit_schemainfo_teardown(struct torture_context *tctx,
535                                                  struct drsut_schemainfo_data *priv)
536 {
537         int ldb_err;
538
539         /* commit pending transaction so we will
540          * be able to check what LDB state is */
541         ldb_err = ldb_transaction_commit(priv->ldb);
542         if (ldb_err != LDB_SUCCESS) {
543                 torture_comment(tctx, "ldb_transaction_commit() - %s (%s)",
544                                 ldb_strerror(ldb_err),
545                                 ldb_errstring(priv->ldb));
546         }
547
548         talloc_free(priv);
549
550         return true;
551 }
552
553 /**
554  * Test case initialization for
555  * DRS-UNIT.schemaInfo
556  */
557 struct torture_tcase * torture_drs_unit_schemainfo(struct torture_suite *suite)
558 {
559         typedef bool (*pfn_setup)(struct torture_context *, void **);
560         typedef bool (*pfn_teardown)(struct torture_context *, void *);
561         typedef bool (*pfn_run)(struct torture_context *, void *);
562
563         struct torture_tcase * tc = torture_suite_add_tcase(suite, "schemaInfo");
564
565         torture_tcase_set_fixture(tc,
566                                   (pfn_setup)torture_drs_unit_schemainfo_setup,
567                                   (pfn_teardown)torture_drs_unit_schemainfo_teardown);
568
569         tc->description = talloc_strdup(tc, "Unit tests for DRSUAPI::schemaInfo implementation");
570
571         torture_tcase_add_simple_test(tc, "dsdb_schema_info_from_blob",
572                                       (pfn_run)test_dsdb_schema_info_from_blob);
573         torture_tcase_add_simple_test(tc, "dsdb_blob_from_schema_info",
574                                       (pfn_run)test_dsdb_blob_from_schema_info);
575         torture_tcase_add_simple_test(tc, "dsdb_module_schema_info_blob read|write",
576                                       (pfn_run)test_dsdb_module_schema_info_blob_rw);
577         torture_tcase_add_simple_test(tc, "dsdb_schema_info_create",
578                                       (pfn_run)test_dsdb_schema_info_create);
579         torture_tcase_add_simple_test(tc, "dsdb_module_schema_info_update",
580                                       (pfn_run)test_dsdb_module_schema_info_update);
581
582
583         return tc;
584 }