r19955: I'll leave that to others. I *knew* I should not have touchec this
[Samba.git] / source / lib / ldb / tools / ad2oLschema.c
blob62c6e01c2e277e5cf1824bf320473fbae02e36e0
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 2 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, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * Name: ldb
28 * Component: ad2oLschema
30 * Description: utility to convert an AD schema into the format required by OpenLDAP
32 * Author: Andrew Tridgell
35 #include "includes.h"
36 #include "ldb/include/includes.h"
37 #include "system/locale.h"
38 #include "ldb/tools/cmdline.h"
39 #include "ldb/tools/convert.h"
41 struct schema_conv {
42 int count;
43 int skipped;
44 int failures;
47 enum convert_target {
48 TARGET_OPENLDAP,
49 TARGET_FEDORA_DS
53 static void usage(void)
55 printf("Usage: ad2oLschema <options>\n");
56 printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
57 printf("Options:\n");
58 printf(" -I inputfile inputfile of mapped OIDs and skipped attributes/ObjectClasses");
59 printf(" -H url LDB or LDAP server to read schmea from\n");
60 printf(" -O outputfile outputfile otherwise STDOUT\n");
61 printf(" -o options pass options like modules to activate\n");
62 printf(" e.g: -o modules:timestamps\n");
63 printf("\n");
64 printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
65 exit(1);
68 static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
69 TALLOC_CTX *mem_ctx,
70 struct ldb_result **attrs_res)
72 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
73 int ret;
74 const char *attrs[] = {
75 "lDAPDisplayName",
76 "isSingleValued",
77 "attributeID",
78 "attributeSyntax",
79 "description",
80 NULL
83 if (!local_ctx) {
84 return LDB_ERR_OPERATIONS_ERROR;
87 /* Downlaod schema */
88 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
89 "objectClass=attributeSchema",
90 attrs, attrs_res);
91 if (ret != LDB_SUCCESS) {
92 printf("Search failed: %s\n", ldb_errstring(ldb));
93 return LDB_ERR_OPERATIONS_ERROR;
96 return ret;
99 static const char *oc_attrs[] = {
100 "lDAPDisplayName",
101 "mayContain",
102 "mustContain",
103 "systemMayContain",
104 "systemMustContain",
105 "objectClassCategory",
106 "governsID",
107 "description",
108 "subClassOf",
109 NULL
112 static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn,
113 TALLOC_CTX *mem_ctx,
114 struct ldb_result *search_from,
115 struct ldb_result *res_list)
117 int i;
118 int ret = 0;
119 for (i=0; i < search_from->count; i++) {
120 struct ldb_result *res;
121 const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i],
122 "lDAPDisplayname", NULL);
123 char *filter = talloc_asprintf(mem_ctx, "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
124 name, name);
126 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
127 filter,
128 oc_attrs, &res);
129 talloc_free(filter);
130 if (ret != LDB_SUCCESS) {
131 printf("Search failed: %s\n", ldb_errstring(ldb));
132 return ret;
135 talloc_steal(mem_ctx, res);
137 res_list->msgs = talloc_realloc(res_list, res_list->msgs,
138 struct ldb_message *, res_list->count + 2);
139 if (!res_list->msgs) {
140 return LDB_ERR_OPERATIONS_ERROR;
142 res_list->msgs[res_list->count] = talloc_move(res_list,
143 &search_from->msgs[i]);
144 res_list->count++;
145 res_list->msgs[res_list->count] = NULL;
147 if (res->count > 0) {
148 ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list);
150 if (ret != LDB_SUCCESS) {
151 return ret;
154 return ret;
157 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
158 TALLOC_CTX *mem_ctx,
159 struct ldb_result **objectclasses_res)
161 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
162 struct ldb_result *top_res, *ret_res;
163 int ret;
164 if (!local_ctx) {
165 return LDB_ERR_OPERATIONS_ERROR;
168 /* Downlaod 'top' */
169 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
170 "(&(objectClass=classSchema)(lDAPDisplayName=top))",
171 oc_attrs, &top_res);
172 if (ret != LDB_SUCCESS) {
173 printf("Search failed: %s\n", ldb_errstring(ldb));
174 return LDB_ERR_OPERATIONS_ERROR;
177 talloc_steal(local_ctx, top_res);
179 if (top_res->count != 1) {
180 return LDB_ERR_OPERATIONS_ERROR;
183 ret_res = talloc_zero(local_ctx, struct ldb_result);
184 if (!ret_res) {
185 return LDB_ERR_OPERATIONS_ERROR;
188 ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res);
190 if (ret != LDB_SUCCESS) {
191 printf("Search failed: %s\n", ldb_errstring(ldb));
192 return LDB_ERR_OPERATIONS_ERROR;
195 *objectclasses_res = talloc_move(mem_ctx, &ret_res);
196 return ret;
199 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
201 const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
202 struct ldb_dn *schemadn;
203 struct ldb_dn *basedn = ldb_dn_explode(mem_ctx, "");
204 struct ldb_result *rootdse_res;
205 int ldb_ret;
206 if (!basedn) {
207 return NULL;
210 /* Search for rootdse */
211 ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, rootdse_attrs, &rootdse_res);
212 if (ldb_ret != LDB_SUCCESS) {
213 printf("Search failed: %s\n", ldb_errstring(ldb));
214 return NULL;
217 talloc_steal(mem_ctx, rootdse_res);
219 if (rootdse_res->count != 1) {
220 printf("Failed to find rootDSE");
221 return NULL;
224 /* Locate schema */
225 schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
226 if (!schemadn) {
227 return NULL;
230 talloc_free(rootdse_res);
231 return schemadn;
234 #define IF_NULL_FAIL_RET(x) do { \
235 if (!x) { \
236 ret.failures++; \
237 return ret; \
239 } while (0)
242 static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out)
244 /* Read list of attributes to skip, OIDs to map */
245 TALLOC_CTX *mem_ctx = talloc_new(ldb);
246 char *line;
247 const char **attrs_skip = NULL;
248 int num_skip = 0;
249 struct oid_map {
250 char *old_oid;
251 char *new_oid;
252 } *oid_map = NULL;
253 int num_maps = 0;
254 struct ldb_result *attrs_res, *objectclasses_res;
255 struct ldb_dn *schemadn;
256 struct schema_conv ret;
258 int ldb_ret, i;
260 ret.count = 0;
261 ret.skipped = 0;
262 ret.failures = 0;
264 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
265 /* Blank Line */
266 if (line[0] == '\0') {
267 continue;
269 /* Comment */
270 if (line[0] == '#') {
271 continue;
273 if (isdigit(line[0])) {
274 char *p = strchr(line, ':');
275 IF_NULL_FAIL_RET(p);
276 if (!p) {
277 ret.failures = 1;
278 return ret;
280 p[0] = '\0';
281 p++;
282 oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_maps + 2);
283 trim_string(line, " ", " ");
284 oid_map[num_maps].old_oid = talloc_move(oid_map, &line);
285 trim_string(p, " ", " ");
286 oid_map[num_maps].new_oid = p;
287 num_maps++;
288 oid_map[num_maps].old_oid = NULL;
289 } else {
290 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
291 trim_string(line, " ", " ");
292 attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
293 num_skip++;
294 attrs_skip[num_skip] = NULL;
298 schemadn = find_schema_dn(ldb, mem_ctx);
299 if (!schemadn) {
300 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
301 ret.failures = 1;
302 return ret;
305 ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res);
306 if (ldb_ret != LDB_SUCCESS) {
307 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb));
308 ret.failures = 1;
309 return ret;
312 switch (target) {
313 case TARGET_OPENLDAP:
314 break;
315 case TARGET_FEDORA_DS:
316 fprintf(out, "dn: cn=schema\n");
317 break;
320 for (i=0; i < attrs_res->count; i++) {
321 struct ldb_message *msg = attrs_res->msgs[i];
323 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
324 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
325 const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL);
326 const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL);
327 BOOL single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", False);
328 const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax);
329 char *schema_entry = NULL;
330 int j;
332 /* We have been asked to skip some attributes/objectClasses */
333 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
334 ret.skipped++;
335 continue;
338 /* We might have been asked to remap this oid, due to a conflict */
339 for (j=0; oid && oid_map[j].old_oid; j++) {
340 if (strcmp(oid, oid_map[j].old_oid) == 0) {
341 oid = oid_map[j].new_oid;
342 break;
346 switch (target) {
347 case TARGET_OPENLDAP:
348 schema_entry = talloc_asprintf(mem_ctx,
349 "attributetype (\n"
350 " %s\n", oid);
351 break;
352 case TARGET_FEDORA_DS:
353 schema_entry = talloc_asprintf(mem_ctx,
354 "attributeTypes: (\n"
355 " %s\n", oid);
356 break;
358 IF_NULL_FAIL_RET(schema_entry);
360 schema_entry = talloc_asprintf_append(schema_entry,
361 " NAME '%s'\n", name);
362 IF_NULL_FAIL_RET(schema_entry);
364 if (description) {
365 schema_entry = talloc_asprintf_append(schema_entry,
366 " DESC %s\n", description);
367 IF_NULL_FAIL_RET(schema_entry);
370 if (map) {
371 const char *syntax_oid;
372 if (map->equality) {
373 schema_entry = talloc_asprintf_append(schema_entry,
374 " EQUALITY %s\n", map->equality);
375 IF_NULL_FAIL_RET(schema_entry);
377 if (map->substring) {
378 schema_entry = talloc_asprintf_append(schema_entry,
379 " SUBSTR %s\n", map->substring);
380 IF_NULL_FAIL_RET(schema_entry);
382 syntax_oid = map->Standard_OID;
383 /* We might have been asked to remap this oid,
384 * due to a conflict, or lack of
385 * implementation */
386 for (j=0; syntax_oid && oid_map[j].old_oid; j++) {
387 if (strcmp(syntax_oid, oid_map[j].old_oid) == 0) {
388 syntax_oid = oid_map[j].new_oid;
389 break;
392 schema_entry = talloc_asprintf_append(schema_entry,
393 " SYNTAX %s\n", syntax_oid);
394 IF_NULL_FAIL_RET(schema_entry);
397 if (single_value) {
398 schema_entry = talloc_asprintf_append(schema_entry,
399 " SINGLE-VALUE\n");
400 IF_NULL_FAIL_RET(schema_entry);
403 schema_entry = talloc_asprintf_append(schema_entry,
404 " )");
406 switch (target) {
407 case TARGET_OPENLDAP:
408 fprintf(out, "%s\n\n", schema_entry);
409 break;
410 case TARGET_FEDORA_DS:
411 fprintf(out, "%s\n", schema_entry);
412 break;
414 ret.count++;
417 ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res);
418 if (ldb_ret != LDB_SUCCESS) {
419 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
420 ret.failures = 1;
421 return ret;
424 for (i=0; i < objectclasses_res->count; i++) {
425 struct ldb_message *msg = objectclasses_res->msgs[i];
426 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
427 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
428 const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL);
429 const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL);
430 int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0);
431 struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain");
432 struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain");
433 struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain");
434 struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain");
435 char *schema_entry = NULL;
436 int j;
438 /* We have been asked to skip some attributes/objectClasses */
439 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
440 ret.skipped++;
441 continue;
444 /* We might have been asked to remap this oid, due to a conflict */
445 for (j=0; oid_map[j].old_oid; j++) {
446 if (strcmp(oid, oid_map[j].old_oid) == 0) {
447 oid = oid_map[j].new_oid;
448 break;
452 switch (target) {
453 case TARGET_OPENLDAP:
454 schema_entry = talloc_asprintf(mem_ctx,
455 "objectclass (\n"
456 " %s\n", oid);
457 break;
458 case TARGET_FEDORA_DS:
459 schema_entry = talloc_asprintf(mem_ctx,
460 "objectClasses: (\n"
461 " %s\n", oid);
462 break;
464 IF_NULL_FAIL_RET(schema_entry);
465 if (!schema_entry) {
466 ret.failures++;
467 break;
470 schema_entry = talloc_asprintf_append(schema_entry,
471 " NAME '%s'\n", name);
472 IF_NULL_FAIL_RET(schema_entry);
474 if (!schema_entry) return ret;
476 if (description) {
477 schema_entry = talloc_asprintf_append(schema_entry,
478 " DESC %s\n", description);
479 IF_NULL_FAIL_RET(schema_entry);
482 if (subClassOf) {
483 schema_entry = talloc_asprintf_append(schema_entry,
484 " SUP %s\n", subClassOf);
485 IF_NULL_FAIL_RET(schema_entry);
488 switch (objectClassCategory) {
489 case 1:
490 schema_entry = talloc_asprintf_append(schema_entry,
491 " STRUCTURAL\n");
492 IF_NULL_FAIL_RET(schema_entry);
493 break;
494 case 2:
495 schema_entry = talloc_asprintf_append(schema_entry,
496 " ABSTRACT\n");
497 IF_NULL_FAIL_RET(schema_entry);
498 break;
499 case 3:
500 schema_entry = talloc_asprintf_append(schema_entry,
501 " AUXILIARY\n");
502 IF_NULL_FAIL_RET(schema_entry);
503 break;
506 #define APPEND_ATTRS(attributes) \
507 do { \
508 int k; \
509 for (k=0; attributes && k < attributes->num_values; k++) { \
510 schema_entry = talloc_asprintf_append(schema_entry, \
511 " %s", \
512 (const char *)attributes->values[k].data); \
513 IF_NULL_FAIL_RET(schema_entry); \
514 if (k != (attributes->num_values - 1)) { \
515 schema_entry = talloc_asprintf_append(schema_entry, \
516 " $"); \
517 IF_NULL_FAIL_RET(schema_entry); \
518 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
519 schema_entry = talloc_asprintf_append(schema_entry, \
520 "\n "); \
521 IF_NULL_FAIL_RET(schema_entry); \
525 } while (0)
527 if (must || sys_must) {
528 schema_entry = talloc_asprintf_append(schema_entry,
529 " MUST (");
530 IF_NULL_FAIL_RET(schema_entry);
532 APPEND_ATTRS(must);
533 if (must && sys_must) {
534 schema_entry = talloc_asprintf_append(schema_entry, \
535 " $"); \
537 APPEND_ATTRS(sys_must);
539 schema_entry = talloc_asprintf_append(schema_entry,
540 " )\n");
541 IF_NULL_FAIL_RET(schema_entry);
544 if (may || sys_may) {
545 schema_entry = talloc_asprintf_append(schema_entry,
546 " MAY (");
547 IF_NULL_FAIL_RET(schema_entry);
549 APPEND_ATTRS(may);
550 if (may && sys_may) {
551 schema_entry = talloc_asprintf_append(schema_entry, \
552 " $"); \
554 APPEND_ATTRS(sys_may);
556 schema_entry = talloc_asprintf_append(schema_entry,
557 " )\n");
558 IF_NULL_FAIL_RET(schema_entry);
561 schema_entry = talloc_asprintf_append(schema_entry,
562 " )");
564 switch (target) {
565 case TARGET_OPENLDAP:
566 fprintf(out, "%s\n\n", schema_entry);
567 break;
568 case TARGET_FEDORA_DS:
569 fprintf(out, "%s\n", schema_entry);
570 break;
572 ret.count++;
575 return ret;
578 int main(int argc, const char **argv)
580 TALLOC_CTX *ctx;
581 struct ldb_cmdline *options;
582 FILE *in = stdin;
583 FILE *out = stdout;
584 struct ldb_context *ldb;
585 struct schema_conv ret;
586 const char *target_str;
587 enum convert_target target;
589 ldb_global_init();
591 ctx = talloc_new(NULL);
592 ldb = ldb_init(ctx);
594 options = ldb_cmdline_process(ldb, argc, argv, usage);
596 if (options->input) {
597 in = fopen(options->input, "r");
598 if (!in) {
599 perror(options->input);
600 exit(1);
603 if (options->output) {
604 out = fopen(options->output, "w");
605 if (!out) {
606 perror(options->output);
607 exit(1);
611 target_str = lp_parm_string(-1, "convert", "target");
613 if (!target_str || strcasecmp(target_str, "openldap") == 0) {
614 target = TARGET_OPENLDAP;
615 } else if (strcasecmp(target_str, "fedora-ds") == 0) {
616 target = TARGET_FEDORA_DS;
617 } else {
618 printf("Unsupported target: %s\n", target_str);
619 exit(1);
622 ret = process_convert(ldb, target, in, out);
624 fclose(in);
625 fclose(out);
627 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);
629 return 0;