source: branches/samba-3.2.x/source/lib/ldb/tools/ad2oLschema.c

Last change on this file was 133, checked in by Paul Smedley, 17 years ago

Update trunk to 3.2.0pre3

File size: 16.3 KB
Line 
1/*
2 ldb database library
3
4 Copyright (C) Andrew Bartlett 2006
5
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
8 ** under the LGPL
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 3 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, see <http://www.gnu.org/licenses/>.
22*/
23
24/*
25 * Name: ldb
26 *
27 * Component: ad2oLschema
28 *
29 * Description: utility to convert an AD schema into the format required by OpenLDAP
30 *
31 * Author: Andrew Tridgell
32 */
33
34#include "includes.h"
35#include "ldb/include/includes.h"
36#include "system/locale.h"
37#include "ldb/tools/cmdline.h"
38#include "ldb/tools/convert.h"
39
40struct schema_conv {
41 int count;
42 int skipped;
43 int failures;
44};
45
46enum convert_target {
47 TARGET_OPENLDAP,
48 TARGET_FEDORA_DS
49};
50
51
52static void usage(void)
53{
54 printf("Usage: ad2oLschema <options>\n");
55 printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
56 printf("Options:\n");
57 printf(" -I inputfile inputfile of mapped OIDs and skipped attributes/ObjectClasses");
58 printf(" -H url LDB or LDAP server to read schmea from\n");
59 printf(" -O outputfile outputfile otherwise STDOUT\n");
60 printf(" -o options pass options like modules to activate\n");
61 printf(" e.g: -o modules:timestamps\n");
62 printf("\n");
63 printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
64 exit(1);
65}
66
67static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
68 TALLOC_CTX *mem_ctx,
69 struct ldb_result **attrs_res)
70{
71 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
72 int ret;
73 const char *attrs[] = {
74 "lDAPDisplayName",
75 "isSingleValued",
76 "attributeID",
77 "attributeSyntax",
78 "description",
79 NULL
80 };
81
82 if (!local_ctx) {
83 return LDB_ERR_OPERATIONS_ERROR;
84 }
85
86 /* Downlaod schema */
87 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
88 "objectClass=attributeSchema",
89 attrs, attrs_res);
90 if (ret != LDB_SUCCESS) {
91 printf("Search failed: %s\n", ldb_errstring(ldb));
92 return LDB_ERR_OPERATIONS_ERROR;
93 }
94
95 return ret;
96}
97
98static const char *oc_attrs[] = {
99 "lDAPDisplayName",
100 "mayContain",
101 "mustContain",
102 "systemMayContain",
103 "systemMustContain",
104 "objectClassCategory",
105 "governsID",
106 "description",
107 "subClassOf",
108 NULL
109};
110
111static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn,
112 TALLOC_CTX *mem_ctx,
113 struct ldb_result *search_from,
114 struct ldb_result *res_list)
115{
116 int i;
117 int ret = 0;
118 for (i=0; i < search_from->count; i++) {
119 struct ldb_result *res;
120 const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i],
121 "lDAPDisplayname", NULL);
122 char *filter = talloc_asprintf(mem_ctx, "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
123 name, name);
124
125 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
126 filter,
127 oc_attrs, &res);
128 talloc_free(filter);
129 if (ret != LDB_SUCCESS) {
130 printf("Search failed: %s\n", ldb_errstring(ldb));
131 return ret;
132 }
133
134 talloc_steal(mem_ctx, res);
135
136 res_list->msgs = talloc_realloc(res_list, res_list->msgs,
137 struct ldb_message *, res_list->count + 2);
138 if (!res_list->msgs) {
139 return LDB_ERR_OPERATIONS_ERROR;
140 }
141 res_list->msgs[res_list->count] = talloc_move(res_list,
142 &search_from->msgs[i]);
143 res_list->count++;
144 res_list->msgs[res_list->count] = NULL;
145
146 if (res->count > 0) {
147 ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list);
148 }
149 if (ret != LDB_SUCCESS) {
150 return ret;
151 }
152 }
153 return ret;
154}
155
156static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
157 TALLOC_CTX *mem_ctx,
158 struct ldb_result **objectclasses_res)
159{
160 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
161 struct ldb_result *top_res, *ret_res;
162 int ret;
163 if (!local_ctx) {
164 return LDB_ERR_OPERATIONS_ERROR;
165 }
166
167 /* Downlaod 'top' */
168 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
169 "(&(objectClass=classSchema)(lDAPDisplayName=top))",
170 oc_attrs, &top_res);
171 if (ret != LDB_SUCCESS) {
172 printf("Search failed: %s\n", ldb_errstring(ldb));
173 return LDB_ERR_OPERATIONS_ERROR;
174 }
175
176 talloc_steal(local_ctx, top_res);
177
178 if (top_res->count != 1) {
179 return LDB_ERR_OPERATIONS_ERROR;
180 }
181
182 ret_res = talloc_zero(local_ctx, struct ldb_result);
183 if (!ret_res) {
184 return LDB_ERR_OPERATIONS_ERROR;
185 }
186
187 ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res);
188
189 if (ret != LDB_SUCCESS) {
190 printf("Search failed: %s\n", ldb_errstring(ldb));
191 return LDB_ERR_OPERATIONS_ERROR;
192 }
193
194 *objectclasses_res = talloc_move(mem_ctx, &ret_res);
195 return ret;
196}
197
198static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
199{
200 const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
201 struct ldb_dn *schemadn;
202 struct ldb_dn *basedn = ldb_dn_explode(mem_ctx, "");
203 struct ldb_result *rootdse_res;
204 int ldb_ret;
205 if (!basedn) {
206 return NULL;
207 }
208
209 /* Search for rootdse */
210 ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, rootdse_attrs, &rootdse_res);
211 if (ldb_ret != LDB_SUCCESS) {
212 printf("Search failed: %s\n", ldb_errstring(ldb));
213 return NULL;
214 }
215
216 talloc_steal(mem_ctx, rootdse_res);
217
218 if (rootdse_res->count != 1) {
219 printf("Failed to find rootDSE");
220 return NULL;
221 }
222
223 /* Locate schema */
224 schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
225 if (!schemadn) {
226 return NULL;
227 }
228
229 talloc_free(rootdse_res);
230 return schemadn;
231}
232
233#define IF_NULL_FAIL_RET(x) do { \
234 if (!x) { \
235 ret.failures++; \
236 return ret; \
237 } \
238 } while (0)
239
240
241static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out)
242{
243 /* Read list of attributes to skip, OIDs to map */
244 TALLOC_CTX *mem_ctx = talloc_new(ldb);
245 char *line;
246 const char **attrs_skip = NULL;
247 int num_skip = 0;
248 struct oid_map {
249 char *old_oid;
250 char *new_oid;
251 } *oid_map = NULL;
252 int num_maps = 0;
253 struct ldb_result *attrs_res, *objectclasses_res;
254 struct ldb_dn *schemadn;
255 struct schema_conv ret;
256
257 int ldb_ret, i;
258
259 ret.count = 0;
260 ret.skipped = 0;
261 ret.failures = 0;
262
263 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
264 /* Blank Line */
265 if (line[0] == '\0') {
266 continue;
267 }
268 /* Comment */
269 if (line[0] == '#') {
270 continue;
271 }
272 if (isdigit(line[0])) {
273 char *p = strchr(line, ':');
274 IF_NULL_FAIL_RET(p);
275 if (!p) {
276 ret.failures = 1;
277 return ret;
278 }
279 p[0] = '\0';
280 p++;
281 oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_maps + 2);
282 trim_string(line, " ", " ");
283 oid_map[num_maps].old_oid = talloc_move(oid_map, &line);
284 trim_string(p, " ", " ");
285 oid_map[num_maps].new_oid = p;
286 num_maps++;
287 oid_map[num_maps].old_oid = NULL;
288 } else {
289 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
290 trim_string(line, " ", " ");
291 attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
292 num_skip++;
293 attrs_skip[num_skip] = NULL;
294 }
295 }
296
297 schemadn = find_schema_dn(ldb, mem_ctx);
298 if (!schemadn) {
299 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
300 ret.failures = 1;
301 return ret;
302 }
303
304 ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res);
305 if (ldb_ret != LDB_SUCCESS) {
306 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb));
307 ret.failures = 1;
308 return ret;
309 }
310
311 switch (target) {
312 case TARGET_OPENLDAP:
313 break;
314 case TARGET_FEDORA_DS:
315 fprintf(out, "dn: cn=schema\n");
316 break;
317 }
318
319 for (i=0; i < attrs_res->count; i++) {
320 struct ldb_message *msg = attrs_res->msgs[i];
321
322 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
323 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
324 const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL);
325 const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL);
326 BOOL single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", False);
327 const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax);
328 char *schema_entry = NULL;
329 int j;
330
331 /* We have been asked to skip some attributes/objectClasses */
332 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
333 ret.skipped++;
334 continue;
335 }
336
337 /* We might have been asked to remap this oid, due to a conflict */
338 for (j=0; oid && oid_map[j].old_oid; j++) {
339 if (strcmp(oid, oid_map[j].old_oid) == 0) {
340 oid = oid_map[j].new_oid;
341 break;
342 }
343 }
344
345 switch (target) {
346 case TARGET_OPENLDAP:
347 schema_entry = talloc_asprintf(mem_ctx,
348 "attributetype (\n"
349 " %s\n", oid);
350 break;
351 case TARGET_FEDORA_DS:
352 schema_entry = talloc_asprintf(mem_ctx,
353 "attributeTypes: (\n"
354 " %s\n", oid);
355 break;
356 }
357 IF_NULL_FAIL_RET(schema_entry);
358
359 schema_entry = talloc_asprintf_append(schema_entry,
360 " NAME '%s'\n", name);
361 IF_NULL_FAIL_RET(schema_entry);
362
363 if (description) {
364 schema_entry = talloc_asprintf_append(schema_entry,
365 " DESC %s\n", description);
366 IF_NULL_FAIL_RET(schema_entry);
367 }
368
369 if (map) {
370 const char *syntax_oid;
371 if (map->equality) {
372 schema_entry = talloc_asprintf_append(schema_entry,
373 " EQUALITY %s\n", map->equality);
374 IF_NULL_FAIL_RET(schema_entry);
375 }
376 if (map->substring) {
377 schema_entry = talloc_asprintf_append(schema_entry,
378 " SUBSTR %s\n", map->substring);
379 IF_NULL_FAIL_RET(schema_entry);
380 }
381 syntax_oid = map->Standard_OID;
382 /* We might have been asked to remap this oid,
383 * due to a conflict, or lack of
384 * implementation */
385 for (j=0; syntax_oid && oid_map[j].old_oid; j++) {
386 if (strcmp(syntax_oid, oid_map[j].old_oid) == 0) {
387 syntax_oid = oid_map[j].new_oid;
388 break;
389 }
390 }
391 schema_entry = talloc_asprintf_append(schema_entry,
392 " SYNTAX %s\n", syntax_oid);
393 IF_NULL_FAIL_RET(schema_entry);
394 }
395
396 if (single_value) {
397 schema_entry = talloc_asprintf_append(schema_entry,
398 " SINGLE-VALUE\n");
399 IF_NULL_FAIL_RET(schema_entry);
400 }
401
402 schema_entry = talloc_asprintf_append(schema_entry,
403 " )");
404
405 switch (target) {
406 case TARGET_OPENLDAP:
407 fprintf(out, "%s\n\n", schema_entry);
408 break;
409 case TARGET_FEDORA_DS:
410 fprintf(out, "%s\n", schema_entry);
411 break;
412 }
413 ret.count++;
414 }
415
416 ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res);
417 if (ldb_ret != LDB_SUCCESS) {
418 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
419 ret.failures = 1;
420 return ret;
421 }
422
423 for (i=0; i < objectclasses_res->count; i++) {
424 struct ldb_message *msg = objectclasses_res->msgs[i];
425 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
426 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
427 const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL);
428 const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL);
429 int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0);
430 struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain");
431 struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain");
432 struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain");
433 struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain");
434 char *schema_entry = NULL;
435 int j;
436
437 /* We have been asked to skip some attributes/objectClasses */
438 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
439 ret.skipped++;
440 continue;
441 }
442
443 /* We might have been asked to remap this oid, due to a conflict */
444 for (j=0; oid_map[j].old_oid; j++) {
445 if (strcmp(oid, oid_map[j].old_oid) == 0) {
446 oid = oid_map[j].new_oid;
447 break;
448 }
449 }
450
451 switch (target) {
452 case TARGET_OPENLDAP:
453 schema_entry = talloc_asprintf(mem_ctx,
454 "objectclass (\n"
455 " %s\n", oid);
456 break;
457 case TARGET_FEDORA_DS:
458 schema_entry = talloc_asprintf(mem_ctx,
459 "objectClasses: (\n"
460 " %s\n", oid);
461 break;
462 }
463 IF_NULL_FAIL_RET(schema_entry);
464 if (!schema_entry) {
465 ret.failures++;
466 break;
467 }
468
469 schema_entry = talloc_asprintf_append(schema_entry,
470 " NAME '%s'\n", name);
471 IF_NULL_FAIL_RET(schema_entry);
472
473 if (!schema_entry) return ret;
474
475 if (description) {
476 schema_entry = talloc_asprintf_append(schema_entry,
477 " DESC %s\n", description);
478 IF_NULL_FAIL_RET(schema_entry);
479 }
480
481 if (subClassOf) {
482 schema_entry = talloc_asprintf_append(schema_entry,
483 " SUP %s\n", subClassOf);
484 IF_NULL_FAIL_RET(schema_entry);
485 }
486
487 switch (objectClassCategory) {
488 case 1:
489 schema_entry = talloc_asprintf_append(schema_entry,
490 " STRUCTURAL\n");
491 IF_NULL_FAIL_RET(schema_entry);
492 break;
493 case 2:
494 schema_entry = talloc_asprintf_append(schema_entry,
495 " ABSTRACT\n");
496 IF_NULL_FAIL_RET(schema_entry);
497 break;
498 case 3:
499 schema_entry = talloc_asprintf_append(schema_entry,
500 " AUXILIARY\n");
501 IF_NULL_FAIL_RET(schema_entry);
502 break;
503 }
504
505#define APPEND_ATTRS(attributes) \
506 do { \
507 int k; \
508 for (k=0; attributes && k < attributes->num_values; k++) { \
509 schema_entry = talloc_asprintf_append(schema_entry, \
510 " %s", \
511 (const char *)attributes->values[k].data); \
512 IF_NULL_FAIL_RET(schema_entry); \
513 if (k != (attributes->num_values - 1)) { \
514 schema_entry = talloc_asprintf_append(schema_entry, \
515 " $"); \
516 IF_NULL_FAIL_RET(schema_entry); \
517 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
518 schema_entry = talloc_asprintf_append(schema_entry, \
519 "\n "); \
520 IF_NULL_FAIL_RET(schema_entry); \
521 } \
522 } \
523 } \
524 } while (0)
525
526 if (must || sys_must) {
527 schema_entry = talloc_asprintf_append(schema_entry,
528 " MUST (");
529 IF_NULL_FAIL_RET(schema_entry);
530
531 APPEND_ATTRS(must);
532 if (must && sys_must) {
533 schema_entry = talloc_asprintf_append(schema_entry, \
534 " $"); \
535 }
536 APPEND_ATTRS(sys_must);
537
538 schema_entry = talloc_asprintf_append(schema_entry,
539 " )\n");
540 IF_NULL_FAIL_RET(schema_entry);
541 }
542
543 if (may || sys_may) {
544 schema_entry = talloc_asprintf_append(schema_entry,
545 " MAY (");
546 IF_NULL_FAIL_RET(schema_entry);
547
548 APPEND_ATTRS(may);
549 if (may && sys_may) {
550 schema_entry = talloc_asprintf_append(schema_entry, \
551 " $"); \
552 }
553 APPEND_ATTRS(sys_may);
554
555 schema_entry = talloc_asprintf_append(schema_entry,
556 " )\n");
557 IF_NULL_FAIL_RET(schema_entry);
558 }
559
560 schema_entry = talloc_asprintf_append(schema_entry,
561 " )");
562
563 switch (target) {
564 case TARGET_OPENLDAP:
565 fprintf(out, "%s\n\n", schema_entry);
566 break;
567 case TARGET_FEDORA_DS:
568 fprintf(out, "%s\n", schema_entry);
569 break;
570 }
571 ret.count++;
572 }
573
574 return ret;
575}
576
577 int main(int argc, const char **argv)
578{
579 TALLOC_CTX *ctx;
580 struct ldb_cmdline *options;
581 FILE *in = stdin;
582 FILE *out = stdout;
583 struct ldb_context *ldb;
584 struct schema_conv ret;
585 const char *target_str;
586 enum convert_target target;
587
588 ldb_global_init();
589
590 ctx = talloc_new(NULL);
591 ldb = ldb_init(ctx);
592
593 options = ldb_cmdline_process(ldb, argc, argv, usage);
594
595 if (options->input) {
596 in = fopen(options->input, "r");
597 if (!in) {
598 perror(options->input);
599 exit(1);
600 }
601 }
602 if (options->output) {
603 out = fopen(options->output, "w");
604 if (!out) {
605 perror(options->output);
606 exit(1);
607 }
608 }
609
610 target_str = lp_parm_string(-1, "convert", "target");
611
612 if (!target_str || strcasecmp(target_str, "openldap") == 0) {
613 target = TARGET_OPENLDAP;
614 } else if (strcasecmp(target_str, "fedora-ds") == 0) {
615 target = TARGET_FEDORA_DS;
616 } else {
617 printf("Unsupported target: %s\n", target_str);
618 exit(1);
619 }
620
621 ret = process_convert(ldb, target, in, out);
622
623 fclose(in);
624 fclose(out);
625
626 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);
627
628 return 0;
629}
Note: See TracBrowser for help on using the repository browser.