f754880d0bda42afc418c3d3086e2b3ac8cdc956
[jelmer/samba4-debian.git] / source / 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 "db_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 struct test_rootDSE {
35         const char *defaultdn;
36         const char *rootdn;
37         const char *configdn;
38         const char *schemadn;
39 };
40
41 struct test_schema_ctx {
42         struct ldb_paged_control *ctrl;
43         uint32_t count;
44         BOOL pending;
45
46         int (*callback)(void *, struct ldb_context *ldb, struct ldb_message *);
47         void *private_data;
48 };
49
50 static BOOL test_search_rootDSE(struct ldb_context *ldb, struct test_rootDSE *root)
51 {
52         int ret;
53         struct ldb_message *msg;
54         struct ldb_result *r;
55
56         d_printf("Testing RootDSE Search\n");
57
58         ret = ldb_search(ldb, ldb_dn_new(ldb, ldb, NULL), LDB_SCOPE_BASE, 
59                          NULL, NULL, &r);
60         if (ret != LDB_SUCCESS) {
61                 return False;
62         } else if (r->count != 1) {
63                 talloc_free(r);
64                 return False;
65         }
66
67         msg = r->msgs[0];
68
69         root->defaultdn = ldb_msg_find_attr_as_string(msg, "defaultNamingContext", NULL);
70         talloc_steal(ldb, root->defaultdn);
71         root->rootdn    = ldb_msg_find_attr_as_string(msg, "rootDomainNamingContext", NULL);
72         talloc_steal(ldb, root->rootdn);
73         root->configdn  = ldb_msg_find_attr_as_string(msg, "configurationNamingContext", NULL);
74         talloc_steal(ldb, root->configdn);
75         root->schemadn  = ldb_msg_find_attr_as_string(msg, "schemaNamingContext", NULL);
76         talloc_steal(ldb, root->schemadn);
77
78         talloc_free(r);
79
80         return True;
81 }
82
83 static int test_schema_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
84 {
85         struct test_schema_ctx *actx = talloc_get_type(context, struct test_schema_ctx);
86         int ret = LDB_SUCCESS;
87
88         switch (ares->type) {
89         case LDB_REPLY_ENTRY:
90                 actx->count++;
91                 ret = actx->callback(actx->private_data, ldb, ares->message);
92                 break;
93
94         case LDB_REPLY_REFERRAL:
95                 break;
96
97         case LDB_REPLY_DONE:
98                 if (ares->controls) {
99                         struct ldb_paged_control *ctrl = NULL;
100                         int i;
101
102                         for (i=0; ares->controls[i]; i++) {
103                                 if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[i]->oid) == 0) {
104                                         ctrl = talloc_get_type(ares->controls[i]->data, struct ldb_paged_control);
105                                         break;
106                                 }
107                         }
108
109                         if (!ctrl) break;
110
111                         talloc_free(actx->ctrl->cookie);
112                         actx->ctrl->cookie = talloc_steal(actx->ctrl->cookie, ctrl->cookie);
113                         actx->ctrl->cookie_len = ctrl->cookie_len;
114
115                         if (actx->ctrl->cookie_len > 0) {
116                                 actx->pending = True;
117                         }
118                 }
119                 break;
120                 
121         default:
122                 d_printf("%s: unknown Reply Type %u\n", __location__, ares->type);
123                 return LDB_ERR_OTHER;
124         }
125
126         if (talloc_free(ares) == -1) {
127                 d_printf("talloc_free failed\n");
128                 actx->pending = 0;
129                 return LDB_ERR_OPERATIONS_ERROR;
130         }
131
132         if (ret) {
133                 return LDB_ERR_OPERATIONS_ERROR;
134         }
135
136         return LDB_SUCCESS;
137 }
138
139 static BOOL test_create_schema_type(struct ldb_context *ldb, struct test_rootDSE *root,
140                                     const char *filter,
141                                     int (*callback)(void *, struct ldb_context *ldb, struct ldb_message *),
142                                     void *private_data)
143 {
144         struct ldb_control **ctrl;
145         struct ldb_paged_control *control;
146         struct ldb_request *req;
147         int ret;
148         struct test_schema_ctx *actx;
149
150         req = talloc(ldb, struct ldb_request);
151         actx = talloc(req, struct test_schema_ctx);
152
153         ctrl = talloc_array(req, struct ldb_control *, 2);
154         ctrl[0] = talloc(ctrl, struct ldb_control);
155         ctrl[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
156         ctrl[0]->critical = True;
157         control = talloc(ctrl[0], struct ldb_paged_control);
158         control->size = 1000;
159         control->cookie = NULL;
160         control->cookie_len = 0;
161         ctrl[0]->data = control;
162         ctrl[1] = NULL;
163
164         req->operation = LDB_SEARCH;
165         req->op.search.base = ldb_dn_new(req, ldb, root->schemadn);
166         req->op.search.scope = LDB_SCOPE_SUBTREE;
167         req->op.search.tree = ldb_parse_tree(req, filter);
168         if (req->op.search.tree == NULL) return -1;
169         req->op.search.attrs = NULL;
170         req->controls = ctrl;
171         req->context = actx;
172         req->callback = test_schema_search_callback;
173         ldb_set_timeout(ldb, req, 0);
174
175         actx->count             = 0;
176         actx->ctrl              = control;
177         actx->callback          = callback;
178         actx->private_data      = private_data;
179 again:
180         actx->pending           = False;
181
182         ret = ldb_request(ldb, req);
183         if (ret != LDB_SUCCESS) {
184                 d_printf("search failed - %s\n", ldb_errstring(ldb));
185                 return False;
186         }
187
188         ret = ldb_wait(req->handle, LDB_WAIT_ALL);
189         if (ret != LDB_SUCCESS) {
190                 d_printf("search error - %s\n", ldb_errstring(ldb));
191                 return False;
192         }
193
194         if (actx->pending)
195                 goto again;
196
197         d_printf("filter[%s] count[%u]\n", filter, actx->count);
198         return True;
199 }
200
201 static int test_add_attribute(void *ptr, struct ldb_context *ldb, struct ldb_message *msg)
202 {
203         struct dsdb_schema *schema = talloc_get_type(ptr, struct dsdb_schema);
204         struct dsdb_attribute *attr = NULL;
205         WERROR status;
206
207         attr = talloc_zero(schema, struct dsdb_attribute);
208         if (!attr) {
209                 goto failed;
210         }
211
212         status = dsdb_attribute_from_ldb(schema, msg, attr, attr);
213         if (!W_ERROR_IS_OK(status)) {
214                 goto failed;
215         }
216
217         DLIST_ADD_END(schema->attributes, attr, struct dsdb_attribute *);
218         return LDB_SUCCESS;
219 failed:
220         talloc_free(attr);
221         return LDB_ERR_OTHER;
222 }
223
224 static int test_add_class(void *ptr, struct ldb_context *ldb, struct ldb_message *msg)
225 {
226         struct dsdb_schema *schema = talloc_get_type(ptr, struct dsdb_schema);
227         struct dsdb_class *obj;
228         WERROR status;
229
230         obj = talloc_zero(schema, struct dsdb_class);
231         if (!obj) {
232                 goto failed;
233         }
234
235         status = dsdb_class_from_ldb(schema, msg, obj, obj);
236         if (!W_ERROR_IS_OK(status)) {
237                 goto failed;
238         }
239
240         DLIST_ADD_END(schema->classes, obj, struct dsdb_class *);
241         return LDB_SUCCESS;
242 failed:
243         return LDB_ERR_OTHER;
244 }
245
246 static BOOL test_create_schema(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema **_schema)
247 {
248         BOOL ret = True;
249         struct dsdb_schema *schema;
250
251         schema = talloc_zero(ldb, struct dsdb_schema);
252
253         d_printf("Fetching attributes...\n");
254         ret &= test_create_schema_type(ldb, root, "(objectClass=attributeSchema)",
255                                        test_add_attribute, schema);
256         d_printf("Fetching objectClasses...\n");
257         ret &= test_create_schema_type(ldb, root, "(objectClass=classSchema)",
258                                        test_add_class, schema);
259
260         if (ret == True) {
261                 *_schema = schema;
262         }
263         return ret;
264 }
265
266 static BOOL test_dump_not_replicated(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
267 {
268         struct dsdb_attribute *a;
269         uint32_t a_i = 1;
270
271         d_printf("Dumping not replicated attributes\n");
272
273         for (a=schema->attributes; a; a = a->next) {
274                 if (!(a->systemFlags & 0x00000001)) continue;
275                 d_printf("attr[%4u]: '%s'\n", a_i++,
276                          a->lDAPDisplayName);
277         }
278
279         return True;
280 }
281
282 static BOOL test_dump_partial(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
283 {
284         struct dsdb_attribute *a;
285         uint32_t a_i = 1;
286
287         d_printf("Dumping attributes which are provided by the global catalog\n");
288
289         for (a=schema->attributes; a; a = a->next) {
290                 if (!(a->systemFlags & 0x00000002) && !a->isMemberOfPartialAttributeSet) continue;
291                 d_printf("attr[%4u]:  %u %u '%s'\n", a_i++,
292                          a->systemFlags & 0x00000002, a->isMemberOfPartialAttributeSet,
293                          a->lDAPDisplayName);
294         }
295
296         return True;
297 }
298
299 static BOOL test_dump_contructed(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
300 {
301         struct dsdb_attribute *a;
302         uint32_t a_i = 1;
303
304         d_printf("Dumping constructed attributes\n");
305
306         for (a=schema->attributes; a; a = a->next) {
307                 if (!(a->systemFlags & 0x00000004)) continue;
308                 d_printf("attr[%4u]: '%s'\n", a_i++,
309                          a->lDAPDisplayName);
310         }
311
312         return True;
313 }
314
315 static BOOL test_dump_sorted_syntax(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
316 {
317         struct dsdb_attribute *a;
318         uint32_t a_i = 1;
319         uint32_t i;
320         const char *syntaxes[] = {
321                 "2.5.5.0",
322                 "2.5.5.1",
323                 "2.5.5.2",
324                 "2.5.5.3",
325                 "2.5.5.4",
326                 "2.5.5.5",
327                 "2.5.5.6",
328                 "2.5.5.7",
329                 "2.5.5.8",
330                 "2.5.5.9",
331                 "2.5.5.10",
332                 "2.5.5.11",
333                 "2.5.5.12",
334                 "2.5.5.13",
335                 "2.5.5.14",
336                 "2.5.5.15",
337                 "2.5.5.16",
338                 "2.5.5.17"
339         };
340
341         d_printf("Dumping attribute syntaxes\n");
342
343         for (i=0; i < ARRAY_SIZE(syntaxes); i++) {
344                 for (a=schema->attributes; a; a = a->next) {
345                         char *om_hex;
346
347                         if (strcmp(syntaxes[i], a->attributeSyntax_oid) != 0) continue;
348
349                         om_hex = data_blob_hex_string(ldb, &a->oMObjectClass);
350                         if (!om_hex) {
351                                 return False;
352                         }
353
354                         d_printf("attr[%4u]: %s %u '%s' '%s'\n", a_i++,
355                                  a->attributeSyntax_oid, a->oMSyntax,
356                                  om_hex, a->lDAPDisplayName);
357                         talloc_free(om_hex);
358                 }
359         }
360
361         return True;
362 }
363
364 BOOL torture_ldap_schema(struct torture_context *torture)
365 {
366         struct ldb_context *ldb;
367         BOOL ret = True;
368         const char *host = torture_setting_string(torture, "host", NULL);
369         char *url;
370         struct test_rootDSE rootDSE;
371         struct dsdb_schema *schema = NULL;
372
373         ZERO_STRUCT(rootDSE);
374
375         url = talloc_asprintf(torture, "ldap://%s/", host);
376
377         ldb = ldb_wrap_connect(torture, url,
378                                NULL,
379                                cmdline_credentials,
380                                0, NULL);
381         if (!ldb) goto failed;
382
383         ret &= test_search_rootDSE(ldb, &rootDSE);
384         if (!ret) goto failed;
385         ret &= test_create_schema(ldb, &rootDSE, &schema);
386         if (!ret) goto failed;
387
388         ret &= test_dump_not_replicated(ldb, &rootDSE, schema);
389         ret &= test_dump_partial(ldb, &rootDSE, schema);
390         ret &= test_dump_contructed(ldb, &rootDSE, schema);
391         ret &= test_dump_sorted_syntax(ldb, &rootDSE, schema);
392
393 failed:
394         return ret;
395 }