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);
121 char *filter = talloc_asprintf(mem_ctx, "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
124 ret = ldb_search(ldb, ldb, &res, schemadn, LDB_SCOPE_SUBTREE,
127 if (ret != LDB_SUCCESS) {
128 printf("Search failed: %s\n", ldb_errstring(ldb));
132 talloc_steal(mem_ctx, res);
134 res_list->msgs = talloc_realloc(res_list, res_list->msgs,
135 struct ldb_message *, res_list->count + 2);
136 if (!res_list->msgs) {
137 return LDB_ERR_OPERATIONS_ERROR;
139 res_list->msgs[res_list->count] = talloc_move(res_list,
140 &search_from->msgs[i]);
142 res_list->msgs[res_list->count] = NULL;
144 if (res->count > 0) {
145 ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list);
147 if (ret != LDB_SUCCESS) {
154 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
156 struct ldb_result **objectclasses_res)
158 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
159 struct ldb_result *top_res, *ret_res;
162 return LDB_ERR_OPERATIONS_ERROR;
166 ret = ldb_search(ldb, ldb, &top_res, schemadn, LDB_SCOPE_SUBTREE,
167 oc_attrs, "(&(objectClass=classSchema)(lDAPDisplayName=top))");
168 if (ret != LDB_SUCCESS) {
169 printf("Search failed: %s\n", ldb_errstring(ldb));
170 return LDB_ERR_OPERATIONS_ERROR;
173 talloc_steal(local_ctx, top_res);
175 if (top_res->count != 1) {
176 return LDB_ERR_OPERATIONS_ERROR;
179 ret_res = talloc_zero(local_ctx, struct ldb_result);
181 return LDB_ERR_OPERATIONS_ERROR;
184 ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res);
186 if (ret != LDB_SUCCESS) {
187 printf("Search failed: %s\n", ldb_errstring(ldb));
188 return LDB_ERR_OPERATIONS_ERROR;
191 *objectclasses_res = talloc_move(mem_ctx, &ret_res);
195 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
197 const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
198 struct ldb_dn *schemadn;
199 struct ldb_dn *basedn = ldb_dn_explode(mem_ctx, "");
200 struct ldb_result *rootdse_res;
206 /* Search for rootdse */
207 ldb_ret = ldb_search(ldb, ldb, &rootdse_res, basedn, LDB_SCOPE_BASE, rootdse_attrs, NULL);
208 if (ldb_ret != LDB_SUCCESS) {
209 printf("Search failed: %s\n", ldb_errstring(ldb));
213 talloc_steal(mem_ctx, rootdse_res);
215 if (rootdse_res->count != 1) {
216 printf("Failed to find rootDSE");
221 schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
226 talloc_free(rootdse_res);
230 #define IF_NULL_FAIL_RET(x) do { \
238 static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out)
240 /* Read list of attributes to skip, OIDs to map */
241 TALLOC_CTX *mem_ctx = talloc_new(ldb);
243 const char **attrs_skip = NULL;
250 struct ldb_result *attrs_res, *objectclasses_res;
251 struct ldb_dn *schemadn;
252 struct schema_conv ret;
260 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
262 if (line[0] == '\0') {
266 if (line[0] == '#') {
269 if (isdigit(line[0])) {
270 char *p = strchr(line, ':');
278 oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_maps + 2);
279 trim_string(line, " ", " ");
280 oid_map[num_maps].old_oid = talloc_move(oid_map, &line);
281 trim_string(p, " ", " ");
282 oid_map[num_maps].new_oid = p;
284 oid_map[num_maps].old_oid = NULL;
286 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
287 trim_string(line, " ", " ");
288 attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
290 attrs_skip[num_skip] = NULL;
294 schemadn = find_schema_dn(ldb, mem_ctx);
296 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
301 ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res);
302 if (ldb_ret != LDB_SUCCESS) {
303 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb));
309 case TARGET_OPENLDAP:
311 case TARGET_FEDORA_DS:
312 fprintf(out, "dn: cn=schema\n");
316 for (i=0; i < attrs_res->count; i++) {
317 struct ldb_message *msg = attrs_res->msgs[i];
319 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
320 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
321 const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL);
322 const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL);
323 BOOL single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", False);
324 const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax);
325 char *schema_entry = NULL;
328 /* We have been asked to skip some attributes/objectClasses */
329 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
334 /* We might have been asked to remap this oid, due to a conflict */
335 for (j=0; oid && oid_map[j].old_oid; j++) {
336 if (strcmp(oid, oid_map[j].old_oid) == 0) {
337 oid = oid_map[j].new_oid;
343 case TARGET_OPENLDAP:
344 schema_entry = talloc_asprintf(mem_ctx,
348 case TARGET_FEDORA_DS:
349 schema_entry = talloc_asprintf(mem_ctx,
350 "attributeTypes: (\n"
354 IF_NULL_FAIL_RET(schema_entry);
356 schema_entry = talloc_asprintf_append(schema_entry,
357 " NAME '%s'\n", name);
358 IF_NULL_FAIL_RET(schema_entry);
361 schema_entry = talloc_asprintf_append(schema_entry,
362 " DESC %s\n", description);
363 IF_NULL_FAIL_RET(schema_entry);
367 const char *syntax_oid;
369 schema_entry = talloc_asprintf_append(schema_entry,
370 " EQUALITY %s\n", map->equality);
371 IF_NULL_FAIL_RET(schema_entry);
373 if (map->substring) {
374 schema_entry = talloc_asprintf_append(schema_entry,
375 " SUBSTR %s\n", map->substring);
376 IF_NULL_FAIL_RET(schema_entry);
378 syntax_oid = map->Standard_OID;
379 /* We might have been asked to remap this oid,
380 * due to a conflict, or lack of
382 for (j=0; syntax_oid && oid_map[j].old_oid; j++) {
383 if (strcmp(syntax_oid, oid_map[j].old_oid) == 0) {
384 syntax_oid = oid_map[j].new_oid;
388 schema_entry = talloc_asprintf_append(schema_entry,
389 " SYNTAX %s\n", syntax_oid);
390 IF_NULL_FAIL_RET(schema_entry);
394 schema_entry = talloc_asprintf_append(schema_entry,
396 IF_NULL_FAIL_RET(schema_entry);
399 schema_entry = talloc_asprintf_append(schema_entry,
403 case TARGET_OPENLDAP:
404 fprintf(out, "%s\n\n", schema_entry);
406 case TARGET_FEDORA_DS:
407 fprintf(out, "%s\n", schema_entry);
413 ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res);
414 if (ldb_ret != LDB_SUCCESS) {
415 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
420 for (i=0; i < objectclasses_res->count; i++) {
421 struct ldb_message *msg = objectclasses_res->msgs[i];
422 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
423 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
424 const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL);
425 const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL);
426 int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0);
427 struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain");
428 struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain");
429 struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain");
430 struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain");
431 char *schema_entry = NULL;
434 /* We have been asked to skip some attributes/objectClasses */
435 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
440 /* We might have been asked to remap this oid, due to a conflict */
441 for (j=0; oid_map[j].old_oid; j++) {
442 if (strcmp(oid, oid_map[j].old_oid) == 0) {
443 oid = oid_map[j].new_oid;
449 case TARGET_OPENLDAP:
450 schema_entry = talloc_asprintf(mem_ctx,
454 case TARGET_FEDORA_DS:
455 schema_entry = talloc_asprintf(mem_ctx,
460 IF_NULL_FAIL_RET(schema_entry);
466 schema_entry = talloc_asprintf_append(schema_entry,
467 " NAME '%s'\n", name);
468 IF_NULL_FAIL_RET(schema_entry);
470 if (!schema_entry) return ret;
473 schema_entry = talloc_asprintf_append(schema_entry,
474 " DESC %s\n", description);
475 IF_NULL_FAIL_RET(schema_entry);
479 schema_entry = talloc_asprintf_append(schema_entry,
480 " SUP %s\n", subClassOf);
481 IF_NULL_FAIL_RET(schema_entry);
484 switch (objectClassCategory) {
486 schema_entry = talloc_asprintf_append(schema_entry,
488 IF_NULL_FAIL_RET(schema_entry);
491 schema_entry = talloc_asprintf_append(schema_entry,
493 IF_NULL_FAIL_RET(schema_entry);
496 schema_entry = talloc_asprintf_append(schema_entry,
498 IF_NULL_FAIL_RET(schema_entry);
502 #define APPEND_ATTRS(attributes) \
505 for (k=0; attributes && k < attributes->num_values; k++) { \
506 schema_entry = talloc_asprintf_append(schema_entry, \
508 (const char *)attributes->values[k].data); \
509 IF_NULL_FAIL_RET(schema_entry); \
510 if (k != (attributes->num_values - 1)) { \
511 schema_entry = talloc_asprintf_append(schema_entry, \
513 IF_NULL_FAIL_RET(schema_entry); \
514 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
515 schema_entry = talloc_asprintf_append(schema_entry, \
517 IF_NULL_FAIL_RET(schema_entry); \
523 if (must || sys_must) {
524 schema_entry = talloc_asprintf_append(schema_entry,
526 IF_NULL_FAIL_RET(schema_entry);
529 if (must && sys_must) {
530 schema_entry = talloc_asprintf_append(schema_entry, \
533 APPEND_ATTRS(sys_must);
535 schema_entry = talloc_asprintf_append(schema_entry,
537 IF_NULL_FAIL_RET(schema_entry);
540 if (may || sys_may) {
541 schema_entry = talloc_asprintf_append(schema_entry,
543 IF_NULL_FAIL_RET(schema_entry);
546 if (may && sys_may) {
547 schema_entry = talloc_asprintf_append(schema_entry, \
550 APPEND_ATTRS(sys_may);
552 schema_entry = talloc_asprintf_append(schema_entry,
554 IF_NULL_FAIL_RET(schema_entry);
557 schema_entry = talloc_asprintf_append(schema_entry,
561 case TARGET_OPENLDAP:
562 fprintf(out, "%s\n\n", schema_entry);
564 case TARGET_FEDORA_DS:
565 fprintf(out, "%s\n", schema_entry);
574 int main(int argc, const char **argv)
577 struct ldb_cmdline *options;
580 struct ldb_context *ldb;
581 struct schema_conv ret;
582 const char *target_str;
583 enum convert_target target;
587 ctx = talloc_new(NULL);
590 options = ldb_cmdline_process(ldb, argc, argv, usage);
592 if (options->input) {
593 in = fopen(options->input, "r");
595 perror(options->input);
599 if (options->output) {
600 out = fopen(options->output, "w");
602 perror(options->output);
607 target_str = lp_parm_string(-1, "convert", "target");
609 if (!target_str || strcasecmp(target_str, "openldap") == 0) {
610 target = TARGET_OPENLDAP;
611 } else if (strcasecmp(target_str, "fedora-ds") == 0) {
612 target = TARGET_FEDORA_DS;
614 printf("Unsupported target: %s\n", target_str);
618 ret = process_convert(ldb, target, in, out);
623 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);