r23798: updated old Temple Place FSF addresses to new URL
[Samba.git] / source / lib / ldb / tools / ad2oLschema.c
blobfc51cb12d86c0bc003541ca87af80334acd8e573
1 /*
2 ldb database library
4 Copyright (C) Andrew Bartlett 2006
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
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.
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.
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/>.
25 * Name: ldb
27 * Component: ad2oLschema
29 * Description: utility to convert an AD schema into the format required by OpenLDAP
31 * Author: Andrew Tridgell
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"
40 struct schema_conv {
41 int count;
42 int skipped;
43 int failures;
46 enum convert_target {
47 TARGET_OPENLDAP,
48 TARGET_FEDORA_DS
52 static void usage(void)
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);
67 static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
68 TALLOC_CTX *mem_ctx,
69 struct ldb_result **attrs_res)
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
82 if (!local_ctx) {
83 return LDB_ERR_OPERATIONS_ERROR;
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;
95 return ret;
98 static const char *oc_attrs[] = {
99 "lDAPDisplayName",
100 "mayContain",
101 "mustContain",
102 "systemMayContain",
103 "systemMustContain",
104 "objectClassCategory",
105 "governsID",
106 "description",
107 "subClassOf",
108 NULL
111 static 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)
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);
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;
134 talloc_steal(mem_ctx, res);
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;
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;
146 if (res->count > 0) {
147 ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list);
149 if (ret != LDB_SUCCESS) {
150 return ret;
153 return ret;
156 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
157 TALLOC_CTX *mem_ctx,
158 struct ldb_result **objectclasses_res)
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;
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;
176 talloc_steal(local_ctx, top_res);
178 if (top_res->count != 1) {
179 return LDB_ERR_OPERATIONS_ERROR;
182 ret_res = talloc_zero(local_ctx, struct ldb_result);
183 if (!ret_res) {
184 return LDB_ERR_OPERATIONS_ERROR;
187 ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res);
189 if (ret != LDB_SUCCESS) {
190 printf("Search failed: %s\n", ldb_errstring(ldb));
191 return LDB_ERR_OPERATIONS_ERROR;
194 *objectclasses_res = talloc_move(mem_ctx, &ret_res);
195 return ret;
198 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
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;
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;
216 talloc_steal(mem_ctx, rootdse_res);
218 if (rootdse_res->count != 1) {
219 printf("Failed to find rootDSE");
220 return NULL;
223 /* Locate schema */
224 schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
225 if (!schemadn) {
226 return NULL;
229 talloc_free(rootdse_res);
230 return schemadn;
233 #define IF_NULL_FAIL_RET(x) do { \
234 if (!x) { \
235 ret.failures++; \
236 return ret; \
238 } while (0)
241 static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out)
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;
257 int ldb_ret, i;
259 ret.count = 0;
260 ret.skipped = 0;
261 ret.failures = 0;
263 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
264 /* Blank Line */
265 if (line[0] == '\0') {
266 continue;
268 /* Comment */
269 if (line[0] == '#') {
270 continue;
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;
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;
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;
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;
311 switch (target) {
312 case TARGET_OPENLDAP:
313 break;
314 case TARGET_FEDORA_DS:
315 fprintf(out, "dn: cn=schema\n");
316 break;
319 for (i=0; i < attrs_res->count; i++) {
320 struct ldb_message *msg = attrs_res->msgs[i];
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;
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;
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;
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;
357 IF_NULL_FAIL_RET(schema_entry);
359 schema_entry = talloc_asprintf_append(schema_entry,
360 " NAME '%s'\n", name);
361 IF_NULL_FAIL_RET(schema_entry);
363 if (description) {
364 schema_entry = talloc_asprintf_append(schema_entry,
365 " DESC %s\n", description);
366 IF_NULL_FAIL_RET(schema_entry);
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);
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);
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;
391 schema_entry = talloc_asprintf_append(schema_entry,
392 " SYNTAX %s\n", syntax_oid);
393 IF_NULL_FAIL_RET(schema_entry);
396 if (single_value) {
397 schema_entry = talloc_asprintf_append(schema_entry,
398 " SINGLE-VALUE\n");
399 IF_NULL_FAIL_RET(schema_entry);
402 schema_entry = talloc_asprintf_append(schema_entry,
403 " )");
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;
413 ret.count++;
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;
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;
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;
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;
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;
463 IF_NULL_FAIL_RET(schema_entry);
464 if (!schema_entry) {
465 ret.failures++;
466 break;
469 schema_entry = talloc_asprintf_append(schema_entry,
470 " NAME '%s'\n", name);
471 IF_NULL_FAIL_RET(schema_entry);
473 if (!schema_entry) return ret;
475 if (description) {
476 schema_entry = talloc_asprintf_append(schema_entry,
477 " DESC %s\n", description);
478 IF_NULL_FAIL_RET(schema_entry);
481 if (subClassOf) {
482 schema_entry = talloc_asprintf_append(schema_entry,
483 " SUP %s\n", subClassOf);
484 IF_NULL_FAIL_RET(schema_entry);
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;
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); \
524 } while (0)
526 if (must || sys_must) {
527 schema_entry = talloc_asprintf_append(schema_entry,
528 " MUST (");
529 IF_NULL_FAIL_RET(schema_entry);
531 APPEND_ATTRS(must);
532 if (must && sys_must) {
533 schema_entry = talloc_asprintf_append(schema_entry, \
534 " $"); \
536 APPEND_ATTRS(sys_must);
538 schema_entry = talloc_asprintf_append(schema_entry,
539 " )\n");
540 IF_NULL_FAIL_RET(schema_entry);
543 if (may || sys_may) {
544 schema_entry = talloc_asprintf_append(schema_entry,
545 " MAY (");
546 IF_NULL_FAIL_RET(schema_entry);
548 APPEND_ATTRS(may);
549 if (may && sys_may) {
550 schema_entry = talloc_asprintf_append(schema_entry, \
551 " $"); \
553 APPEND_ATTRS(sys_may);
555 schema_entry = talloc_asprintf_append(schema_entry,
556 " )\n");
557 IF_NULL_FAIL_RET(schema_entry);
560 schema_entry = talloc_asprintf_append(schema_entry,
561 " )");
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;
571 ret.count++;
574 return ret;
577 int main(int argc, const char **argv)
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;
588 ldb_global_init();
590 ctx = talloc_new(NULL);
591 ldb = ldb_init(ctx);
593 options = ldb_cmdline_process(ldb, argc, argv, usage);
595 if (options->input) {
596 in = fopen(options->input, "r");
597 if (!in) {
598 perror(options->input);
599 exit(1);
602 if (options->output) {
603 out = fopen(options->output, "w");
604 if (!out) {
605 perror(options->output);
606 exit(1);
610 target_str = lp_parm_string(-1, "convert", "target");
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);
621 ret = process_convert(ldb, target, in, out);
623 fclose(in);
624 fclose(out);
626 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);
628 return 0;