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 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
28 * Component: ad2oLschema
30 * Description: utility to convert an AD schema into the format required by OpenLDAP
32 * Author: Andrew Tridgell
36 #include "ldb/include/includes.h"
37 #include "system/locale.h"
38 #include "ldb/tools/cmdline.h"
39 #include "ldb/tools/convert.h"
53 static void usage(void)
55 printf("Usage: ad2oLschema <options>\n");
56 printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\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");
64 printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
68 static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
70 struct ldb_result **attrs_res)
72 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
74 const char *attrs[] = {
84 return LDB_ERR_OPERATIONS_ERROR;
88 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
89 "objectClass=attributeSchema",
91 if (ret != LDB_SUCCESS) {
92 printf("Search failed: %s\n", ldb_errstring(ldb));
93 return LDB_ERR_OPERATIONS_ERROR;
99 static const char *oc_attrs[] = {
105 "objectClassCategory",
112 static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn,
114 struct ldb_result *search_from,
115 struct ldb_result *res_list)
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)))",
126 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
130 if (ret != LDB_SUCCESS) {
131 printf("Search failed: %s\n", ldb_errstring(ldb));
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]);
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) {
157 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
159 struct ldb_result **objectclasses_res)
161 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
162 struct ldb_result *top_res, *ret_res;
165 return LDB_ERR_OPERATIONS_ERROR;
169 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
170 "(&(objectClass=classSchema)(lDAPDisplayName=top))",
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);
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);
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;
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));
217 talloc_steal(mem_ctx, rootdse_res);
219 if (rootdse_res->count != 1) {
220 printf("Failed to find rootDSE");
225 schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
230 talloc_free(rootdse_res);
234 #define IF_NULL_FAIL_RET(x) do { \
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);
247 const char **attrs_skip = NULL;
254 struct ldb_result *attrs_res, *objectclasses_res;
255 struct ldb_dn *schemadn;
256 struct schema_conv ret;
264 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
266 if (line[0] == '\0') {
270 if (line[0] == '#') {
273 if (isdigit(line[0])) {
274 char *p = strchr(line, ':');
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;
288 oid_map[num_maps].old_oid = NULL;
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);
294 attrs_skip[num_skip] = NULL;
298 schemadn = find_schema_dn(ldb, mem_ctx);
300 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
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));
313 case TARGET_OPENLDAP:
315 case TARGET_FEDORA_DS:
316 fprintf(out, "dn: cn=schema\n");
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;
332 /* We have been asked to skip some attributes/objectClasses */
333 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
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;
347 case TARGET_OPENLDAP:
348 schema_entry = talloc_asprintf(mem_ctx,
352 case TARGET_FEDORA_DS:
353 schema_entry = talloc_asprintf(mem_ctx,
354 "attributeTypes: (\n"
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);
365 schema_entry = talloc_asprintf_append(schema_entry,
366 " DESC %s\n", description);
367 IF_NULL_FAIL_RET(schema_entry);
371 const char *syntax_oid;
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
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;
392 schema_entry = talloc_asprintf_append(schema_entry,
393 " SYNTAX %s\n", syntax_oid);
394 IF_NULL_FAIL_RET(schema_entry);
398 schema_entry = talloc_asprintf_append(schema_entry,
400 IF_NULL_FAIL_RET(schema_entry);
403 schema_entry = talloc_asprintf_append(schema_entry,
407 case TARGET_OPENLDAP:
408 fprintf(out, "%s\n\n", schema_entry);
410 case TARGET_FEDORA_DS:
411 fprintf(out, "%s\n", schema_entry);
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));
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;
438 /* We have been asked to skip some attributes/objectClasses */
439 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
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;
453 case TARGET_OPENLDAP:
454 schema_entry = talloc_asprintf(mem_ctx,
458 case TARGET_FEDORA_DS:
459 schema_entry = talloc_asprintf(mem_ctx,
464 IF_NULL_FAIL_RET(schema_entry);
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;
477 schema_entry = talloc_asprintf_append(schema_entry,
478 " DESC %s\n", description);
479 IF_NULL_FAIL_RET(schema_entry);
483 schema_entry = talloc_asprintf_append(schema_entry,
484 " SUP %s\n", subClassOf);
485 IF_NULL_FAIL_RET(schema_entry);
488 switch (objectClassCategory) {
490 schema_entry = talloc_asprintf_append(schema_entry,
492 IF_NULL_FAIL_RET(schema_entry);
495 schema_entry = talloc_asprintf_append(schema_entry,
497 IF_NULL_FAIL_RET(schema_entry);
500 schema_entry = talloc_asprintf_append(schema_entry,
502 IF_NULL_FAIL_RET(schema_entry);
506 #define APPEND_ATTRS(attributes) \
509 for (k=0; attributes && k < attributes->num_values; k++) { \
510 schema_entry = talloc_asprintf_append(schema_entry, \
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, \
517 IF_NULL_FAIL_RET(schema_entry); \
518 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
519 schema_entry = talloc_asprintf_append(schema_entry, \
521 IF_NULL_FAIL_RET(schema_entry); \
527 if (must || sys_must) {
528 schema_entry = talloc_asprintf_append(schema_entry,
530 IF_NULL_FAIL_RET(schema_entry);
533 if (must && sys_must) {
534 schema_entry = talloc_asprintf_append(schema_entry, \
537 APPEND_ATTRS(sys_must);
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,
547 IF_NULL_FAIL_RET(schema_entry);
550 if (may && sys_may) {
551 schema_entry = talloc_asprintf_append(schema_entry, \
554 APPEND_ATTRS(sys_may);
556 schema_entry = talloc_asprintf_append(schema_entry,
558 IF_NULL_FAIL_RET(schema_entry);
561 schema_entry = talloc_asprintf_append(schema_entry,
565 case TARGET_OPENLDAP:
566 fprintf(out, "%s\n\n", schema_entry);
568 case TARGET_FEDORA_DS:
569 fprintf(out, "%s\n", schema_entry);
578 int main(int argc, const char **argv)
581 struct ldb_cmdline *options;
584 struct ldb_context *ldb;
585 struct schema_conv ret;
586 const char *target_str;
587 enum convert_target target;
591 ctx = talloc_new(NULL);
594 options = ldb_cmdline_process(ldb, argc, argv, usage);
596 if (options->input) {
597 in = fopen(options->input, "r");
599 perror(options->input);
603 if (options->output) {
604 out = fopen(options->output, "w");
606 perror(options->output);
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;
618 printf("Unsupported target: %s\n", target_str);
622 ret = process_convert(ldb, target, in, out);
627 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);