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, ldb, attrs_res, schemadn, LDB_SCOPE_SUBTREE,
88 attrs, "objectClass=attributeSchema");
89 if (ret != LDB_SUCCESS) {
90 printf("Search failed: %s\n", ldb_errstring(ldb));
91 return LDB_ERR_OPERATIONS_ERROR;
97 static const char *oc_attrs[] = {
103 "objectClassCategory",
110 static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn,
112 struct ldb_result *search_from,
113 struct ldb_result *res_list)
117 for (i=0; i < search_from->count; i++) {
118 struct ldb_result *res;
119 const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i],
120 "lDAPDisplayname", NULL);
122 ret = ldb_search(ldb, ldb, &res, schemadn, LDB_SCOPE_SUBTREE,
123 oc_attrs, "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
125 if (ret != LDB_SUCCESS) {
126 printf("Search failed: %s\n", ldb_errstring(ldb));
130 talloc_steal(mem_ctx, res);
132 res_list->msgs = talloc_realloc(res_list, res_list->msgs,
133 struct ldb_message *, res_list->count + 2);
134 if (!res_list->msgs) {
135 return LDB_ERR_OPERATIONS_ERROR;
137 res_list->msgs[res_list->count] = talloc_move(res_list,
138 &search_from->msgs[i]);
140 res_list->msgs[res_list->count] = NULL;
142 if (res->count > 0) {
143 ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list);
145 if (ret != LDB_SUCCESS) {
152 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
154 struct ldb_result **objectclasses_res)
156 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
157 struct ldb_result *top_res, *ret_res;
160 return LDB_ERR_OPERATIONS_ERROR;
164 ret = ldb_search(ldb, ldb, &top_res, schemadn, LDB_SCOPE_SUBTREE,
165 oc_attrs, "(&(objectClass=classSchema)(lDAPDisplayName=top))");
166 if (ret != LDB_SUCCESS) {
167 printf("Search failed: %s\n", ldb_errstring(ldb));
168 return LDB_ERR_OPERATIONS_ERROR;
171 talloc_steal(local_ctx, top_res);
173 if (top_res->count != 1) {
174 return LDB_ERR_OPERATIONS_ERROR;
177 ret_res = talloc_zero(local_ctx, struct ldb_result);
179 return LDB_ERR_OPERATIONS_ERROR;
182 ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res);
184 if (ret != LDB_SUCCESS) {
185 printf("Search failed: %s\n", ldb_errstring(ldb));
186 return LDB_ERR_OPERATIONS_ERROR;
189 *objectclasses_res = talloc_move(mem_ctx, &ret_res);
193 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
195 const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
196 struct ldb_dn *schemadn;
197 struct ldb_dn *basedn = ldb_dn_explode(mem_ctx, "");
198 struct ldb_result *rootdse_res;
204 /* Search for rootdse */
205 ldb_ret = ldb_search(ldb, ldb, &rootdse_res, basedn, LDB_SCOPE_BASE, rootdse_attrs, NULL);
206 if (ldb_ret != LDB_SUCCESS) {
207 printf("Search failed: %s\n", ldb_errstring(ldb));
211 talloc_steal(mem_ctx, rootdse_res);
213 if (rootdse_res->count != 1) {
214 printf("Failed to find rootDSE");
219 schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
224 talloc_free(rootdse_res);
228 #define IF_NULL_FAIL_RET(x) do { \
236 static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out)
238 /* Read list of attributes to skip, OIDs to map */
239 TALLOC_CTX *mem_ctx = talloc_new(ldb);
241 const char **attrs_skip = NULL;
248 struct ldb_result *attrs_res, *objectclasses_res;
249 struct ldb_dn *schemadn;
250 struct schema_conv ret;
258 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
260 if (line[0] == '\0') {
264 if (line[0] == '#') {
267 if (isdigit(line[0])) {
268 char *p = strchr(line, ':');
276 oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_maps + 2);
277 trim_string(line, " ", " ");
278 oid_map[num_maps].old_oid = talloc_move(oid_map, &line);
279 trim_string(p, " ", " ");
280 oid_map[num_maps].new_oid = p;
282 oid_map[num_maps].old_oid = NULL;
284 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
285 trim_string(line, " ", " ");
286 attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
288 attrs_skip[num_skip] = NULL;
292 schemadn = find_schema_dn(ldb, mem_ctx);
294 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
299 ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res);
300 if (ldb_ret != LDB_SUCCESS) {
301 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb));
307 case TARGET_OPENLDAP:
309 case TARGET_FEDORA_DS:
310 fprintf(out, "dn: cn=schema\n");
314 for (i=0; i < attrs_res->count; i++) {
315 struct ldb_message *msg = attrs_res->msgs[i];
317 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
318 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
319 const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL);
320 const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL);
321 BOOL single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", False);
322 const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax);
323 char *schema_entry = NULL;
326 /* We have been asked to skip some attributes/objectClasses */
327 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
332 /* We might have been asked to remap this oid, due to a conflict */
333 for (j=0; oid && oid_map[j].old_oid; j++) {
334 if (strcmp(oid, oid_map[j].old_oid) == 0) {
335 oid = oid_map[j].new_oid;
341 case TARGET_OPENLDAP:
342 schema_entry = talloc_asprintf(mem_ctx,
346 case TARGET_FEDORA_DS:
347 schema_entry = talloc_asprintf(mem_ctx,
348 "attributeTypes: (\n"
352 IF_NULL_FAIL_RET(schema_entry);
354 schema_entry = talloc_asprintf_append(schema_entry,
355 " NAME '%s'\n", name);
356 IF_NULL_FAIL_RET(schema_entry);
359 schema_entry = talloc_asprintf_append(schema_entry,
360 " DESC %s\n", description);
361 IF_NULL_FAIL_RET(schema_entry);
365 const char *syntax_oid;
367 schema_entry = talloc_asprintf_append(schema_entry,
368 " EQUALITY %s\n", map->equality);
369 IF_NULL_FAIL_RET(schema_entry);
371 if (map->substring) {
372 schema_entry = talloc_asprintf_append(schema_entry,
373 " SUBSTR %s\n", map->substring);
374 IF_NULL_FAIL_RET(schema_entry);
376 syntax_oid = map->Standard_OID;
377 /* We might have been asked to remap this oid,
378 * due to a conflict, or lack of
380 for (j=0; syntax_oid && oid_map[j].old_oid; j++) {
381 if (strcmp(syntax_oid, oid_map[j].old_oid) == 0) {
382 syntax_oid = oid_map[j].new_oid;
386 schema_entry = talloc_asprintf_append(schema_entry,
387 " SYNTAX %s\n", syntax_oid);
388 IF_NULL_FAIL_RET(schema_entry);
392 schema_entry = talloc_asprintf_append(schema_entry,
394 IF_NULL_FAIL_RET(schema_entry);
397 schema_entry = talloc_asprintf_append(schema_entry,
401 case TARGET_OPENLDAP:
402 fprintf(out, "%s\n\n", schema_entry);
404 case TARGET_FEDORA_DS:
405 fprintf(out, "%s\n", schema_entry);
411 ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res);
412 if (ldb_ret != LDB_SUCCESS) {
413 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
418 for (i=0; i < objectclasses_res->count; i++) {
419 struct ldb_message *msg = objectclasses_res->msgs[i];
420 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
421 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
422 const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL);
423 const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL);
424 int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0);
425 struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain");
426 struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain");
427 struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain");
428 struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain");
429 char *schema_entry = NULL;
432 /* We have been asked to skip some attributes/objectClasses */
433 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
438 /* We might have been asked to remap this oid, due to a conflict */
439 for (j=0; oid_map[j].old_oid; j++) {
440 if (strcmp(oid, oid_map[j].old_oid) == 0) {
441 oid = oid_map[j].new_oid;
447 case TARGET_OPENLDAP:
448 schema_entry = talloc_asprintf(mem_ctx,
452 case TARGET_FEDORA_DS:
453 schema_entry = talloc_asprintf(mem_ctx,
458 IF_NULL_FAIL_RET(schema_entry);
464 schema_entry = talloc_asprintf_append(schema_entry,
465 " NAME '%s'\n", name);
466 IF_NULL_FAIL_RET(schema_entry);
468 if (!schema_entry) return ret;
471 schema_entry = talloc_asprintf_append(schema_entry,
472 " DESC %s\n", description);
473 IF_NULL_FAIL_RET(schema_entry);
477 schema_entry = talloc_asprintf_append(schema_entry,
478 " SUP %s\n", subClassOf);
479 IF_NULL_FAIL_RET(schema_entry);
482 switch (objectClassCategory) {
484 schema_entry = talloc_asprintf_append(schema_entry,
486 IF_NULL_FAIL_RET(schema_entry);
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);
500 #define APPEND_ATTRS(attributes) \
503 for (k=0; attributes && k < attributes->num_values; k++) { \
504 schema_entry = talloc_asprintf_append(schema_entry, \
506 (const char *)attributes->values[k].data); \
507 IF_NULL_FAIL_RET(schema_entry); \
508 if (k != (attributes->num_values - 1)) { \
509 schema_entry = talloc_asprintf_append(schema_entry, \
511 IF_NULL_FAIL_RET(schema_entry); \
512 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
513 schema_entry = talloc_asprintf_append(schema_entry, \
515 IF_NULL_FAIL_RET(schema_entry); \
521 if (must || sys_must) {
522 schema_entry = talloc_asprintf_append(schema_entry,
524 IF_NULL_FAIL_RET(schema_entry);
527 if (must && sys_must) {
528 schema_entry = talloc_asprintf_append(schema_entry, \
531 APPEND_ATTRS(sys_must);
533 schema_entry = talloc_asprintf_append(schema_entry,
535 IF_NULL_FAIL_RET(schema_entry);
538 if (may || sys_may) {
539 schema_entry = talloc_asprintf_append(schema_entry,
541 IF_NULL_FAIL_RET(schema_entry);
544 if (may && sys_may) {
545 schema_entry = talloc_asprintf_append(schema_entry, \
548 APPEND_ATTRS(sys_may);
550 schema_entry = talloc_asprintf_append(schema_entry,
552 IF_NULL_FAIL_RET(schema_entry);
555 schema_entry = talloc_asprintf_append(schema_entry,
559 case TARGET_OPENLDAP:
560 fprintf(out, "%s\n\n", schema_entry);
562 case TARGET_FEDORA_DS:
563 fprintf(out, "%s\n", schema_entry);
572 int main(int argc, const char **argv)
575 struct ldb_cmdline *options;
578 struct ldb_context *ldb;
579 struct schema_conv ret;
580 const char *target_str;
581 enum convert_target target;
585 ctx = talloc_new(NULL);
588 options = ldb_cmdline_process(ldb, argc, argv, usage);
590 if (options->input) {
591 in = fopen(options->input, "r");
593 perror(options->input);
597 if (options->output) {
598 out = fopen(options->output, "w");
600 perror(options->output);
605 target_str = lp_parm_string(-1, "convert", "target");
607 if (!target_str || strcasecmp(target_str, "openldap") == 0) {
608 target = TARGET_OPENLDAP;
609 } else if (strcasecmp(target_str, "fedora-ds") == 0) {
610 target = TARGET_FEDORA_DS;
612 printf("Unsupported target: %s\n", target_str);
616 ret = process_convert(ldb, target, in, out);
621 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);