major upgrade to the ldb attribute handling
[metze/samba/wip.git] / source4 / torture / ldap / schema.c
1 /* 
2    Unix SMB/CIFS mplementation.
3    LDAP schema tests
4    
5    Copyright (C) Stefan Metzmacher 2006
6     
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19    
20 */
21
22 #include "includes.h"
23 #include "libcli/ldap/ldap_client.h"
24 #include "lib/cmdline/popt_common.h"
25 #include "ldb_wrap.h"
26 #include "lib/ldb/include/ldb.h"
27 #include "lib/ldb/include/ldb_errors.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "../lib/util/dlinklist.h"
30
31 #include "torture/torture.h"
32 #include "torture/ldap/proto.h"
33
34 #include "param/param.h"
35
36 struct test_rootDSE {
37         const char *defaultdn;
38         const char *rootdn;
39         const char *configdn;
40         const char *schemadn;
41 };
42
43 struct test_schema_ctx {
44         struct ldb_context *ldb;
45
46         struct ldb_paged_control *ctrl;
47         uint32_t count;
48         bool pending;
49
50         int (*callback)(void *, struct ldb_context *ldb, struct ldb_message *);
51         void *private_data;
52 };
53
54 static bool test_search_rootDSE(struct ldb_context *ldb, struct test_rootDSE *root)
55 {
56         int ret;
57         struct ldb_message *msg;
58         struct ldb_result *r;
59
60         d_printf("Testing RootDSE Search\n");
61
62         ret = ldb_search(ldb, ldb, &r, ldb_dn_new(ldb, ldb, NULL),
63                          LDB_SCOPE_BASE, NULL, NULL);
64         if (ret != LDB_SUCCESS) {
65                 return false;
66         } else if (r->count != 1) {
67                 talloc_free(r);
68                 return false;
69         }
70
71         msg = r->msgs[0];
72
73         root->defaultdn = ldb_msg_find_attr_as_string(msg, "defaultNamingContext", NULL);
74         talloc_steal(ldb, root->defaultdn);
75         root->rootdn    = ldb_msg_find_attr_as_string(msg, "rootDomainNamingContext", NULL);
76         talloc_steal(ldb, root->rootdn);
77         root->configdn  = ldb_msg_find_attr_as_string(msg, "configurationNamingContext", NULL);
78         talloc_steal(ldb, root->configdn);
79         root->schemadn  = ldb_msg_find_attr_as_string(msg, "schemaNamingContext", NULL);
80         talloc_steal(ldb, root->schemadn);
81
82         talloc_free(r);
83
84         return true;
85 }
86
87 static int test_schema_search_callback(struct ldb_request *req, struct ldb_reply *ares)
88 {
89         struct test_schema_ctx *actx;
90         int ret = LDB_SUCCESS;
91
92         actx = talloc_get_type(req->context, struct test_schema_ctx);
93
94         if (!ares) {
95                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
96         }
97         if (ares->error != LDB_SUCCESS) {
98                 return ldb_request_done(req, ares->error);
99         }
100
101         switch (ares->type) {
102         case LDB_REPLY_ENTRY:
103                 actx->count++;
104                 ret = actx->callback(actx->private_data, actx->ldb, ares->message);
105                 break;
106
107         case LDB_REPLY_REFERRAL:
108                 break;
109
110         case LDB_REPLY_DONE:
111                 if (ares->controls) {
112                         struct ldb_paged_control *ctrl = NULL;
113                         int i;
114
115                         for (i=0; ares->controls[i]; i++) {
116                                 if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[i]->oid) == 0) {
117                                         ctrl = talloc_get_type(ares->controls[i]->data, struct ldb_paged_control);
118                                         break;
119                                 }
120                         }
121
122                         if (!ctrl) break;
123
124                         talloc_free(actx->ctrl->cookie);
125                         actx->ctrl->cookie = talloc_steal(actx->ctrl->cookie, ctrl->cookie);
126                         actx->ctrl->cookie_len = ctrl->cookie_len;
127
128                         if (actx->ctrl->cookie_len > 0) {
129                                 actx->pending = true;
130                         }
131                 }
132                 talloc_free(ares);
133                 return ldb_request_done(req, LDB_SUCCESS);
134
135         default:
136                 d_printf("%s: unknown Reply Type %u\n", __location__, ares->type);
137                 return ldb_request_done(req, LDB_ERR_OTHER);
138         }
139
140         if (talloc_free(ares) == -1) {
141                 d_printf("talloc_free failed\n");
142                 actx->pending = 0;
143                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
144         }
145
146         if (ret) {
147                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
148         }
149
150         return LDB_SUCCESS;
151 }
152
153 static bool test_create_schema_type(struct ldb_context *ldb, struct test_rootDSE *root,
154                                     const char *filter,
155                                     int (*callback)(void *, struct ldb_context *ldb, struct ldb_message *),
156                                     void *private_data)
157 {
158         struct ldb_control **ctrl;
159         struct ldb_paged_control *control;
160         struct ldb_request *req;
161         int ret;
162         struct test_schema_ctx *actx;
163
164         actx = talloc(ldb, struct test_schema_ctx);
165         actx->ldb = ldb;
166         actx->private_data = private_data;
167         actx->callback= callback;
168
169         ctrl = talloc_array(actx, struct ldb_control *, 2);
170         ctrl[0] = talloc(ctrl, struct ldb_control);
171         ctrl[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
172         ctrl[0]->critical = true;
173         control = talloc(ctrl[0], struct ldb_paged_control);
174         control->size = 1000;
175         control->cookie = NULL;
176         control->cookie_len = 0;
177         ctrl[0]->data = control;
178         ctrl[1] = NULL;
179
180         ret = ldb_build_search_req(&req, ldb, actx,
181                                    ldb_dn_new(actx, ldb, root->schemadn),
182                                    LDB_SCOPE_SUBTREE,
183                                    filter, NULL,
184                                    ctrl,
185                                    actx, test_schema_search_callback,
186                                    NULL);
187
188         actx->ctrl = control;
189         actx->count = 0;
190 again:
191         actx->pending           = false;
192
193         ret = ldb_request(ldb, req);
194         if (ret != LDB_SUCCESS) {
195                 d_printf("search failed - %s\n", ldb_errstring(ldb));
196                 talloc_free(actx);
197                 return false;
198         }
199
200         ret = ldb_wait(req->handle, LDB_WAIT_ALL);
201         if (ret != LDB_SUCCESS) {
202                 d_printf("search error - %s\n", ldb_errstring(ldb));
203                 talloc_free(actx);
204                 return false;
205         }
206
207         if (actx->pending)
208                 goto again;
209
210         d_printf("filter[%s] count[%u]\n", filter, actx->count);
211         talloc_free(actx);
212         return true;
213 }
214
215 static int test_add_attribute(void *ptr, struct ldb_context *ldb, struct ldb_message *msg)
216 {
217         struct dsdb_schema *schema = talloc_get_type(ptr, struct dsdb_schema);
218         struct dsdb_attribute *attr = NULL;
219         WERROR status;
220
221         attr = talloc_zero(schema, struct dsdb_attribute);
222         if (!attr) {
223                 goto failed;
224         }
225
226         status = dsdb_attribute_from_ldb(ldb, schema, msg, attr, attr);
227         if (!W_ERROR_IS_OK(status)) {
228                 goto failed;
229         }
230
231         DLIST_ADD_END(schema->attributes, attr, struct dsdb_attribute *);
232         return LDB_SUCCESS;
233 failed:
234         talloc_free(attr);
235         return LDB_ERR_OTHER;
236 }
237
238 static int test_add_class(void *ptr, struct ldb_context *ldb, struct ldb_message *msg)
239 {
240         struct dsdb_schema *schema = talloc_get_type(ptr, struct dsdb_schema);
241         struct dsdb_class *obj;
242         WERROR status;
243
244         obj = talloc_zero(schema, struct dsdb_class);
245         if (!obj) {
246                 goto failed;
247         }
248
249         status = dsdb_class_from_ldb(schema, msg, obj, obj);
250         if (!W_ERROR_IS_OK(status)) {
251                 goto failed;
252         }
253
254         DLIST_ADD_END(schema->classes, obj, struct dsdb_class *);
255         return LDB_SUCCESS;
256 failed:
257         return LDB_ERR_OTHER;
258 }
259
260 static bool test_create_schema(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema **_schema)
261 {
262         bool ret = true;
263         struct dsdb_schema *schema;
264
265         schema = talloc_zero(ldb, struct dsdb_schema);
266
267         d_printf("Fetching attributes...\n");
268         ret &= test_create_schema_type(ldb, root, "(objectClass=attributeSchema)",
269                                        test_add_attribute, schema);
270         d_printf("Fetching objectClasses...\n");
271         ret &= test_create_schema_type(ldb, root, "(objectClass=classSchema)",
272                                        test_add_class, schema);
273
274         if (ret == true) {
275                 *_schema = schema;
276         }
277         return ret;
278 }
279
280 static bool test_dump_not_replicated(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
281 {
282         struct dsdb_attribute *a;
283         uint32_t a_i = 1;
284
285         d_printf("Dumping not replicated attributes\n");
286
287         for (a=schema->attributes; a; a = a->next) {
288                 if (!(a->systemFlags & 0x00000001)) continue;
289                 d_printf("attr[%4u]: '%s'\n", a_i++,
290                          a->lDAPDisplayName);
291         }
292
293         return true;
294 }
295
296 static bool test_dump_partial(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
297 {
298         struct dsdb_attribute *a;
299         uint32_t a_i = 1;
300
301         d_printf("Dumping attributes which are provided by the global catalog\n");
302
303         for (a=schema->attributes; a; a = a->next) {
304                 if (!(a->systemFlags & 0x00000002) && !a->isMemberOfPartialAttributeSet) continue;
305                 d_printf("attr[%4u]:  %u %u '%s'\n", a_i++,
306                          a->systemFlags & 0x00000002, a->isMemberOfPartialAttributeSet,
307                          a->lDAPDisplayName);
308         }
309
310         return true;
311 }
312
313 static bool test_dump_contructed(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
314 {
315         struct dsdb_attribute *a;
316         uint32_t a_i = 1;
317
318         d_printf("Dumping constructed attributes\n");
319
320         for (a=schema->attributes; a; a = a->next) {
321                 if (!(a->systemFlags & 0x00000004)) continue;
322                 d_printf("attr[%4u]: '%s'\n", a_i++,
323                          a->lDAPDisplayName);
324         }
325
326         return true;
327 }
328
329 static bool test_dump_sorted_syntax(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
330 {
331         struct dsdb_attribute *a;
332         uint32_t a_i = 1;
333         uint32_t i;
334         const char *syntaxes[] = {
335                 "2.5.5.0",
336                 "2.5.5.1",
337                 "2.5.5.2",
338                 "2.5.5.3",
339                 "2.5.5.4",
340                 "2.5.5.5",
341                 "2.5.5.6",
342                 "2.5.5.7",
343                 "2.5.5.8",
344                 "2.5.5.9",
345                 "2.5.5.10",
346                 "2.5.5.11",
347                 "2.5.5.12",
348                 "2.5.5.13",
349                 "2.5.5.14",
350                 "2.5.5.15",
351                 "2.5.5.16",
352                 "2.5.5.17"
353         };
354
355         d_printf("Dumping attribute syntaxes\n");
356
357         for (i=0; i < ARRAY_SIZE(syntaxes); i++) {
358                 for (a=schema->attributes; a; a = a->next) {
359                         char *om_hex;
360
361                         if (strcmp(syntaxes[i], a->attributeSyntax_oid) != 0) continue;
362
363                         om_hex = data_blob_hex_string(ldb, &a->oMObjectClass);
364                         if (!om_hex) {
365                                 return false;
366                         }
367
368                         d_printf("attr[%4u]: %s %u '%s' '%s'\n", a_i++,
369                                  a->attributeSyntax_oid, a->oMSyntax,
370                                  om_hex, a->lDAPDisplayName);
371                         talloc_free(om_hex);
372                 }
373         }
374
375         return true;
376 }
377
378 bool torture_ldap_schema(struct torture_context *torture)
379 {
380         struct ldb_context *ldb;
381         bool ret = true;
382         const char *host = torture_setting_string(torture, "host", NULL);
383         char *url;
384         struct test_rootDSE rootDSE;
385         struct dsdb_schema *schema = NULL;
386
387         ZERO_STRUCT(rootDSE);
388
389         url = talloc_asprintf(torture, "ldap://%s/", host);
390
391         ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, url,
392                                NULL,
393                                cmdline_credentials,
394                                0, NULL);
395         if (!ldb) goto failed;
396
397         ret &= test_search_rootDSE(ldb, &rootDSE);
398         if (!ret) goto failed;
399         ret &= test_create_schema(ldb, &rootDSE, &schema);
400         if (!ret) goto failed;
401
402         ret &= test_dump_not_replicated(ldb, &rootDSE, schema);
403         ret &= test_dump_partial(ldb, &rootDSE, schema);
404         ret &= test_dump_contructed(ldb, &rootDSE, schema);
405         ret &= test_dump_sorted_syntax(ldb, &rootDSE, schema);
406
407 failed:
408         return ret;
409 }