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 "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_steal(res_list, search_from->msgs[i]);
143 res_list->msgs[res_list->count] = NULL;
145 if (res->count > 0) {
146 ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list);
148 if (ret != LDB_SUCCESS) {
155 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
157 struct ldb_result **objectclasses_res)
159 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
160 struct ldb_result *top_res, *ret_res;
163 return LDB_ERR_OPERATIONS_ERROR;
167 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
168 "(&(objectClass=classSchema)(lDAPDisplayName=top))",
170 if (ret != LDB_SUCCESS) {
171 printf("Search failed: %s\n", ldb_errstring(ldb));
172 return LDB_ERR_OPERATIONS_ERROR;
175 talloc_steal(local_ctx, top_res);
177 if (top_res->count != 1) {
178 return LDB_ERR_OPERATIONS_ERROR;
181 ret_res = talloc_zero(local_ctx, struct ldb_result);
183 return LDB_ERR_OPERATIONS_ERROR;
186 ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res);
188 if (ret != LDB_SUCCESS) {
189 printf("Search failed: %s\n", ldb_errstring(ldb));
190 return LDB_ERR_OPERATIONS_ERROR;
193 *objectclasses_res = talloc_steal(mem_ctx, ret_res);
197 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
199 const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
200 struct ldb_dn *schemadn;
201 struct ldb_dn *basedn = ldb_dn_explode(mem_ctx, "");
202 struct ldb_result *rootdse_res;
208 /* Search for rootdse */
209 ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, rootdse_attrs, &rootdse_res);
210 if (ldb_ret != LDB_SUCCESS) {
211 printf("Search failed: %s\n", ldb_errstring(ldb));
215 talloc_steal(mem_ctx, rootdse_res);
217 if (rootdse_res->count != 1) {
218 printf("Failed to find rootDSE");
223 schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
228 talloc_free(rootdse_res);
232 #define IF_NULL_FAIL_RET(x) do { \
240 static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out)
242 /* Read list of attributes to skip, OIDs to map */
243 TALLOC_CTX *mem_ctx = talloc_new(ldb);
245 const char **attrs_skip = NULL;
252 struct ldb_result *attrs_res, *objectclasses_res;
253 struct ldb_dn *schemadn;
254 struct schema_conv ret;
262 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
264 if (line[0] == '\0') {
268 if (line[0] == '#') {
271 if (isdigit(line[0])) {
272 char *p = strchr(line, ':');
280 oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_maps + 2);
281 trim_string(line, " ", " ");
282 oid_map[num_maps].old_oid = talloc_steal(oid_map, line);
283 trim_string(p, " ", " ");
284 oid_map[num_maps].new_oid = p;
286 oid_map[num_maps].old_oid = NULL;
288 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
289 trim_string(line, " ", " ");
290 attrs_skip[num_skip] = talloc_steal(attrs_skip, line);
292 attrs_skip[num_skip] = NULL;
296 schemadn = find_schema_dn(ldb, mem_ctx);
298 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
303 ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res);
304 if (ldb_ret != LDB_SUCCESS) {
305 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb));
311 case TARGET_OPENLDAP:
313 case TARGET_FEDORA_DS:
314 fprintf(out, "dn: cn=schema\n");
318 for (i=0; i < attrs_res->count; i++) {
319 struct ldb_message *msg = attrs_res->msgs[i];
321 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
322 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
323 const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL);
324 const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL);
325 BOOL single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", False);
326 const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax);
327 char *schema_entry = NULL;
330 /* We have been asked to skip some attributes/objectClasses */
331 if (str_list_check_ci(attrs_skip, name)) {
336 /* We might have been asked to remap this oid, due to a conflict */
337 for (j=0; oid && oid_map[j].old_oid; j++) {
338 if (strcmp(oid, oid_map[j].old_oid) == 0) {
339 oid = oid_map[j].new_oid;
345 case TARGET_OPENLDAP:
346 schema_entry = talloc_asprintf(mem_ctx,
350 case TARGET_FEDORA_DS:
351 schema_entry = talloc_asprintf(mem_ctx,
352 "attributeTypes: (\n"
356 IF_NULL_FAIL_RET(schema_entry);
358 schema_entry = talloc_asprintf_append(schema_entry,
359 " NAME '%s'\n", name);
360 IF_NULL_FAIL_RET(schema_entry);
363 schema_entry = talloc_asprintf_append(schema_entry,
364 " DESC %s\n", description);
365 IF_NULL_FAIL_RET(schema_entry);
369 const char *syntax_oid;
371 schema_entry = talloc_asprintf_append(schema_entry,
372 " EQUALITY %s\n", map->equality);
373 IF_NULL_FAIL_RET(schema_entry);
375 if (map->substring) {
376 schema_entry = talloc_asprintf_append(schema_entry,
377 " SUBSTR %s\n", map->substring);
378 IF_NULL_FAIL_RET(schema_entry);
380 syntax_oid = map->Standard_OID;
381 /* We might have been asked to remap this oid,
382 * due to a conflict, or lack of
384 for (j=0; syntax_oid && oid_map[j].old_oid; j++) {
385 if (strcmp(syntax_oid, oid_map[j].old_oid) == 0) {
386 syntax_oid = oid_map[j].new_oid;
390 schema_entry = talloc_asprintf_append(schema_entry,
391 " SYNTAX %s\n", syntax_oid);
392 IF_NULL_FAIL_RET(schema_entry);
396 schema_entry = talloc_asprintf_append(schema_entry,
398 IF_NULL_FAIL_RET(schema_entry);
401 schema_entry = talloc_asprintf_append(schema_entry,
405 case TARGET_OPENLDAP:
406 fprintf(out, "%s\n\n", schema_entry);
408 case TARGET_FEDORA_DS:
409 fprintf(out, "%s\n", schema_entry);
414 ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res);
415 if (ldb_ret != LDB_SUCCESS) {
416 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
421 for (i=0; i < objectclasses_res->count; i++) {
422 struct ldb_message *msg = objectclasses_res->msgs[i];
423 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
424 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
425 const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL);
426 const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL);
427 int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0);
428 struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain");
429 struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain");
430 struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain");
431 struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain");
432 char *schema_entry = NULL;
435 /* We have been asked to skip some attributes/objectClasses */
436 if (str_list_check_ci(attrs_skip, name)) {
441 /* We might have been asked to remap this oid, due to a conflict */
442 for (j=0; oid_map[j].old_oid; j++) {
443 if (strcmp(oid, oid_map[j].old_oid) == 0) {
444 oid = oid_map[j].new_oid;
450 case TARGET_OPENLDAP:
451 schema_entry = talloc_asprintf(mem_ctx,
455 case TARGET_FEDORA_DS:
456 schema_entry = talloc_asprintf(mem_ctx,
461 IF_NULL_FAIL_RET(schema_entry);
467 schema_entry = talloc_asprintf_append(schema_entry,
468 " NAME '%s'\n", name);
469 IF_NULL_FAIL_RET(schema_entry);
471 if (!schema_entry) return ret;
474 schema_entry = talloc_asprintf_append(schema_entry,
475 " DESC %s\n", description);
476 IF_NULL_FAIL_RET(schema_entry);
480 schema_entry = talloc_asprintf_append(schema_entry,
481 " SUP %s\n", subClassOf);
482 IF_NULL_FAIL_RET(schema_entry);
485 switch (objectClassCategory) {
487 schema_entry = talloc_asprintf_append(schema_entry,
489 IF_NULL_FAIL_RET(schema_entry);
492 schema_entry = talloc_asprintf_append(schema_entry,
494 IF_NULL_FAIL_RET(schema_entry);
497 schema_entry = talloc_asprintf_append(schema_entry,
499 IF_NULL_FAIL_RET(schema_entry);
503 #define APPEND_ATTRS(attributes) \
506 for (k=0; attributes && k < attributes->num_values; k++) { \
507 schema_entry = talloc_asprintf_append(schema_entry, \
509 (const char *)attributes->values[k].data); \
510 IF_NULL_FAIL_RET(schema_entry); \
511 if (k != (attributes->num_values - 1)) { \
512 schema_entry = talloc_asprintf_append(schema_entry, \
514 IF_NULL_FAIL_RET(schema_entry); \
515 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
516 schema_entry = talloc_asprintf_append(schema_entry, \
518 IF_NULL_FAIL_RET(schema_entry); \
524 if (must || sys_must) {
525 schema_entry = talloc_asprintf_append(schema_entry,
527 IF_NULL_FAIL_RET(schema_entry);
530 if (must && sys_must) {
531 schema_entry = talloc_asprintf_append(schema_entry, \
534 APPEND_ATTRS(sys_must);
536 schema_entry = talloc_asprintf_append(schema_entry,
538 IF_NULL_FAIL_RET(schema_entry);
541 if (may || sys_may) {
542 schema_entry = talloc_asprintf_append(schema_entry,
544 IF_NULL_FAIL_RET(schema_entry);
547 if (may && sys_may) {
548 schema_entry = talloc_asprintf_append(schema_entry, \
551 APPEND_ATTRS(sys_may);
553 schema_entry = talloc_asprintf_append(schema_entry,
555 IF_NULL_FAIL_RET(schema_entry);
558 schema_entry = talloc_asprintf_append(schema_entry,
562 case TARGET_OPENLDAP:
563 fprintf(out, "%s\n\n", schema_entry);
565 case TARGET_FEDORA_DS:
566 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 with %d failures\n", ret.count, ret.failures);