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
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/>.
27 * Component: ad2oLschema
29 * Description: utility to convert an AD schema into the format required by OpenLDAP
31 * Author: Andrew Tridgell
35 #include "ldb/include/includes.h"
36 #include "system/locale.h"
37 #include "ldb/tools/cmdline.h"
38 #include "ldb/tools/convert.h"
52 static void usage(void)
54 printf("Usage: ad2oLschema <options>\n");
55 printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\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");
63 printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
67 static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
69 struct ldb_result **attrs_res)
71 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
73 const char *attrs[] = {
83 return LDB_ERR_OPERATIONS_ERROR;
87 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
88 "objectClass=attributeSchema",
90 if (ret != LDB_SUCCESS) {
91 printf("Search failed: %s\n", ldb_errstring(ldb));
92 return LDB_ERR_OPERATIONS_ERROR;
98 static const char *oc_attrs[] = {
104 "objectClassCategory",
111 static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn,
113 struct ldb_result *search_from,
114 struct ldb_result *res_list)
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)))",
125 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
129 if (ret != LDB_SUCCESS) {
130 printf("Search failed: %s\n", ldb_errstring(ldb));
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]);
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) {
156 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
158 struct ldb_result **objectclasses_res)
160 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
161 struct ldb_result *top_res, *ret_res;
164 return LDB_ERR_OPERATIONS_ERROR;
168 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
169 "(&(objectClass=classSchema)(lDAPDisplayName=top))",
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);
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);
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;
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));
216 talloc_steal(mem_ctx, rootdse_res);
218 if (rootdse_res->count != 1) {
219 printf("Failed to find rootDSE");
224 schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
229 talloc_free(rootdse_res);
233 #define IF_NULL_FAIL_RET(x) do { \
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);
246 const char **attrs_skip = NULL;
253 struct ldb_result *attrs_res, *objectclasses_res;
254 struct ldb_dn *schemadn;
255 struct schema_conv ret;
263 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
265 if (line[0] == '\0') {
269 if (line[0] == '#') {
272 if (isdigit(line[0])) {
273 char *p = strchr(line, ':');
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;
287 oid_map[num_maps].old_oid = NULL;
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);
293 attrs_skip[num_skip] = NULL;
297 schemadn = find_schema_dn(ldb, mem_ctx);
299 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
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));
312 case TARGET_OPENLDAP:
314 case TARGET_FEDORA_DS:
315 fprintf(out, "dn: cn=schema\n");
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;
331 /* We have been asked to skip some attributes/objectClasses */
332 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
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;
346 case TARGET_OPENLDAP:
347 schema_entry = talloc_asprintf(mem_ctx,
351 case TARGET_FEDORA_DS:
352 schema_entry = talloc_asprintf(mem_ctx,
353 "attributeTypes: (\n"
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);
364 schema_entry = talloc_asprintf_append(schema_entry,
365 " DESC %s\n", description);
366 IF_NULL_FAIL_RET(schema_entry);
370 const char *syntax_oid;
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
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;
391 schema_entry = talloc_asprintf_append(schema_entry,
392 " SYNTAX %s\n", syntax_oid);
393 IF_NULL_FAIL_RET(schema_entry);
397 schema_entry = talloc_asprintf_append(schema_entry,
399 IF_NULL_FAIL_RET(schema_entry);
402 schema_entry = talloc_asprintf_append(schema_entry,
406 case TARGET_OPENLDAP:
407 fprintf(out, "%s\n\n", schema_entry);
409 case TARGET_FEDORA_DS:
410 fprintf(out, "%s\n", schema_entry);
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));
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;
437 /* We have been asked to skip some attributes/objectClasses */
438 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
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;
452 case TARGET_OPENLDAP:
453 schema_entry = talloc_asprintf(mem_ctx,
457 case TARGET_FEDORA_DS:
458 schema_entry = talloc_asprintf(mem_ctx,
463 IF_NULL_FAIL_RET(schema_entry);
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;
476 schema_entry = talloc_asprintf_append(schema_entry,
477 " DESC %s\n", description);
478 IF_NULL_FAIL_RET(schema_entry);
482 schema_entry = talloc_asprintf_append(schema_entry,
483 " SUP %s\n", subClassOf);
484 IF_NULL_FAIL_RET(schema_entry);
487 switch (objectClassCategory) {
489 schema_entry = talloc_asprintf_append(schema_entry,
491 IF_NULL_FAIL_RET(schema_entry);
494 schema_entry = talloc_asprintf_append(schema_entry,
496 IF_NULL_FAIL_RET(schema_entry);
499 schema_entry = talloc_asprintf_append(schema_entry,
501 IF_NULL_FAIL_RET(schema_entry);
505 #define APPEND_ATTRS(attributes) \
508 for (k=0; attributes && k < attributes->num_values; k++) { \
509 schema_entry = talloc_asprintf_append(schema_entry, \
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, \
516 IF_NULL_FAIL_RET(schema_entry); \
517 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
518 schema_entry = talloc_asprintf_append(schema_entry, \
520 IF_NULL_FAIL_RET(schema_entry); \
526 if (must || sys_must) {
527 schema_entry = talloc_asprintf_append(schema_entry,
529 IF_NULL_FAIL_RET(schema_entry);
532 if (must && sys_must) {
533 schema_entry = talloc_asprintf_append(schema_entry, \
536 APPEND_ATTRS(sys_must);
538 schema_entry = talloc_asprintf_append(schema_entry,
540 IF_NULL_FAIL_RET(schema_entry);
543 if (may || sys_may) {
544 schema_entry = talloc_asprintf_append(schema_entry,
546 IF_NULL_FAIL_RET(schema_entry);
549 if (may && sys_may) {
550 schema_entry = talloc_asprintf_append(schema_entry, \
553 APPEND_ATTRS(sys_may);
555 schema_entry = talloc_asprintf_append(schema_entry,
557 IF_NULL_FAIL_RET(schema_entry);
560 schema_entry = talloc_asprintf_append(schema_entry,
564 case TARGET_OPENLDAP:
565 fprintf(out, "%s\n\n", schema_entry);
567 case TARGET_FEDORA_DS:
568 fprintf(out, "%s\n", schema_entry);
577 int main(int argc, const char **argv)
580 struct ldb_cmdline *options;
583 struct ldb_context *ldb;
584 struct schema_conv ret;
585 const char *target_str;
586 enum convert_target target;
590 ctx = talloc_new(NULL);
593 options = ldb_cmdline_process(ldb, argc, argv, usage);
595 if (options->input) {
596 in = fopen(options->input, "r");
598 perror(options->input);
602 if (options->output) {
603 out = fopen(options->output, "w");
605 perror(options->output);
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;
617 printf("Unsupported target: %s\n", target_str);
621 ret = process_convert(ldb, target, in, out);
626 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);