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_includes.h"
37 #include "system/locale.h"
38 #include "tools/cmdline.h"
39 #include "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);
124 ret = ldb_search_exp_fmt(ldb, mem_ctx, &res,
125 schemadn, LDB_SCOPE_SUBTREE, oc_attrs,
126 "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
128 if (ret != LDB_SUCCESS) {
129 printf("Search failed: %s\n", ldb_errstring(ldb));
133 res_list->msgs = talloc_realloc(res_list, res_list->msgs,
134 struct ldb_message *, res_list->count + 2);
135 if (!res_list->msgs) {
136 return LDB_ERR_OPERATIONS_ERROR;
138 res_list->msgs[res_list->count] = talloc_move(res_list,
139 &search_from->msgs[i]);
141 res_list->msgs[res_list->count] = NULL;
143 if (res->count > 0) {
144 ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list);
146 if (ret != LDB_SUCCESS) {
153 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
155 struct ldb_result **objectclasses_res)
157 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
158 struct ldb_result *top_res, *ret_res;
161 return LDB_ERR_OPERATIONS_ERROR;
165 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
166 "(&(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 const char *no_attrs[] = { NULL };
199 struct ldb_dn *schemadn;
200 struct ldb_dn *basedn = ldb_dn_new(mem_ctx, ldb, NULL);
201 struct ldb_result *rootdse_res;
202 struct ldb_result *schema_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 ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_SUBTREE,
212 "(&(objectClass=dMD)(cn=Schema))",
213 no_attrs, &schema_res);
215 printf("cn=Schema Search failed: %s\n", ldb_errstring(ldb));
219 talloc_steal(mem_ctx, schema_res);
221 if (schema_res->count != 1) {
222 printf("Failed to find rootDSE");
226 schemadn = talloc_steal(mem_ctx, schema_res->msgs[0]->dn);
227 talloc_free(schema_res);
232 talloc_steal(mem_ctx, rootdse_res);
234 if (rootdse_res->count != 1) {
235 printf("Failed to find rootDSE");
240 schemadn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
245 talloc_free(rootdse_res);
249 #define IF_NULL_FAIL_RET(x) do { \
257 static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out)
259 /* Read list of attributes to skip, OIDs to map */
260 TALLOC_CTX *mem_ctx = talloc_new(ldb);
262 const char **attrs_skip = NULL;
268 int num_oid_maps = 0;
273 int num_attr_maps = 0;
274 struct ldb_result *attrs_res, *objectclasses_res;
275 struct ldb_dn *schemadn;
276 struct schema_conv ret;
284 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
286 if (line[0] == '\0') {
290 if (line[0] == '#') {
293 if (isdigit(line[0])) {
294 char *p = strchr(line, ':');
298 oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_oid_maps + 2);
299 trim_string(line, " ", " ");
300 oid_map[num_oid_maps].old_oid = talloc_move(oid_map, &line);
301 trim_string(p, " ", " ");
302 oid_map[num_oid_maps].new_oid = p;
304 oid_map[num_oid_maps].old_oid = NULL;
306 char *p = strchr(line, ':');
308 /* remap attribute/objectClass */
311 attr_map = talloc_realloc(mem_ctx, attr_map, struct attr_map, num_attr_maps + 2);
312 trim_string(line, " ", " ");
313 attr_map[num_attr_maps].old_attr = talloc_move(attr_map, &line);
314 trim_string(p, " ", " ");
315 attr_map[num_attr_maps].new_attr = p;
317 attr_map[num_attr_maps].old_attr = NULL;
319 /* skip attribute/objectClass */
320 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
321 trim_string(line, " ", " ");
322 attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
324 attrs_skip[num_skip] = NULL;
329 schemadn = find_schema_dn(ldb, mem_ctx);
331 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
336 ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res);
337 if (ldb_ret != LDB_SUCCESS) {
338 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb));
344 case TARGET_OPENLDAP:
346 case TARGET_FEDORA_DS:
347 fprintf(out, "dn: cn=schema\n");
351 for (i=0; i < attrs_res->count; i++) {
352 struct ldb_message *msg = attrs_res->msgs[i];
354 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
355 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
356 const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL);
357 const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL);
358 BOOL single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", False);
359 const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax);
360 char *schema_entry = NULL;
364 printf("Failed to find lDAPDisplayName for schema DN: %s\n", ldb_dn_get_linearized(msg->dn));
369 /* We have been asked to skip some attributes/objectClasses */
370 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
375 /* We might have been asked to remap this oid, due to a conflict */
376 for (j=0; oid && oid_map && oid_map[j].old_oid; j++) {
377 if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
378 oid = oid_map[j].new_oid;
384 case TARGET_OPENLDAP:
385 schema_entry = talloc_asprintf(mem_ctx,
389 case TARGET_FEDORA_DS:
390 schema_entry = talloc_asprintf(mem_ctx,
391 "attributeTypes: (\n"
395 IF_NULL_FAIL_RET(schema_entry);
397 /* We might have been asked to remap this name, due to a conflict */
398 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
399 if (strcasecmp(name, attr_map[j].old_attr) == 0) {
400 name = attr_map[j].new_attr;
405 schema_entry = talloc_asprintf_append(schema_entry,
406 " NAME '%s'\n", name);
407 IF_NULL_FAIL_RET(schema_entry);
410 schema_entry = talloc_asprintf_append(schema_entry,
411 " DESC %s\n", description);
412 IF_NULL_FAIL_RET(schema_entry);
416 const char *syntax_oid;
418 schema_entry = talloc_asprintf_append(schema_entry,
419 " EQUALITY %s\n", map->equality);
420 IF_NULL_FAIL_RET(schema_entry);
422 if (map->substring) {
423 schema_entry = talloc_asprintf_append(schema_entry,
424 " SUBSTR %s\n", map->substring);
425 IF_NULL_FAIL_RET(schema_entry);
427 syntax_oid = map->Standard_OID;
428 /* We might have been asked to remap this oid,
429 * due to a conflict, or lack of
431 for (j=0; syntax_oid && oid_map[j].old_oid; j++) {
432 if (strcasecmp(syntax_oid, oid_map[j].old_oid) == 0) {
433 syntax_oid = oid_map[j].new_oid;
437 schema_entry = talloc_asprintf_append(schema_entry,
438 " SYNTAX %s\n", syntax_oid);
439 IF_NULL_FAIL_RET(schema_entry);
443 schema_entry = talloc_asprintf_append(schema_entry,
445 IF_NULL_FAIL_RET(schema_entry);
448 schema_entry = talloc_asprintf_append(schema_entry,
452 case TARGET_OPENLDAP:
453 fprintf(out, "%s\n\n", schema_entry);
455 case TARGET_FEDORA_DS:
456 fprintf(out, "%s\n", schema_entry);
462 ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res);
463 if (ldb_ret != LDB_SUCCESS) {
464 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
469 for (i=0; i < objectclasses_res->count; i++) {
470 struct ldb_message *msg = objectclasses_res->msgs[i];
471 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
472 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
473 const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL);
474 const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL);
475 int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0);
476 struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain");
477 struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain");
478 struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain");
479 struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain");
480 char *schema_entry = NULL;
484 printf("Failed to find lDAPDisplayName for schema DN: %s\n", ldb_dn_get_linearized(msg->dn));
489 /* We have been asked to skip some attributes/objectClasses */
490 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
495 /* We might have been asked to remap this oid, due to a conflict */
496 for (j=0; oid_map[j].old_oid; j++) {
497 if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
498 oid = oid_map[j].new_oid;
504 case TARGET_OPENLDAP:
505 schema_entry = talloc_asprintf(mem_ctx,
509 case TARGET_FEDORA_DS:
510 schema_entry = talloc_asprintf(mem_ctx,
515 IF_NULL_FAIL_RET(schema_entry);
521 /* We might have been asked to remap this name, due to a conflict */
522 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
523 if (strcasecmp(name, attr_map[j].old_attr) == 0) {
524 name = attr_map[j].new_attr;
529 schema_entry = talloc_asprintf_append(schema_entry,
530 " NAME '%s'\n", name);
531 IF_NULL_FAIL_RET(schema_entry);
533 if (!schema_entry) return ret;
536 schema_entry = talloc_asprintf_append(schema_entry,
537 " DESC %s\n", description);
538 IF_NULL_FAIL_RET(schema_entry);
542 schema_entry = talloc_asprintf_append(schema_entry,
543 " SUP %s\n", subClassOf);
544 IF_NULL_FAIL_RET(schema_entry);
547 switch (objectClassCategory) {
549 schema_entry = talloc_asprintf_append(schema_entry,
551 IF_NULL_FAIL_RET(schema_entry);
554 schema_entry = talloc_asprintf_append(schema_entry,
556 IF_NULL_FAIL_RET(schema_entry);
559 schema_entry = talloc_asprintf_append(schema_entry,
561 IF_NULL_FAIL_RET(schema_entry);
565 #define APPEND_ATTRS(attributes) \
568 for (k=0; attributes && k < attributes->num_values; k++) { \
570 const char *attr_name = (const char *)attributes->values[k].data; \
571 /* We might have been asked to remap this name, due to a conflict */ \
572 for (attr_idx=0; attr_name && attr_map && attr_map[attr_idx].old_attr; attr_idx++) { \
573 if (strcasecmp(attr_name, attr_map[attr_idx].old_attr) == 0) { \
574 attr_name = attr_map[attr_idx].new_attr; \
579 schema_entry = talloc_asprintf_append(schema_entry, \
582 IF_NULL_FAIL_RET(schema_entry); \
583 if (k != (attributes->num_values - 1)) { \
584 schema_entry = talloc_asprintf_append(schema_entry, \
586 IF_NULL_FAIL_RET(schema_entry); \
587 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
588 schema_entry = talloc_asprintf_append(schema_entry, \
590 IF_NULL_FAIL_RET(schema_entry); \
596 if (must || sys_must) {
597 schema_entry = talloc_asprintf_append(schema_entry,
599 IF_NULL_FAIL_RET(schema_entry);
602 if (must && sys_must) {
603 schema_entry = talloc_asprintf_append(schema_entry, \
606 APPEND_ATTRS(sys_must);
608 schema_entry = talloc_asprintf_append(schema_entry,
610 IF_NULL_FAIL_RET(schema_entry);
613 if (may || sys_may) {
614 schema_entry = talloc_asprintf_append(schema_entry,
616 IF_NULL_FAIL_RET(schema_entry);
619 if (may && sys_may) {
620 schema_entry = talloc_asprintf_append(schema_entry, \
623 APPEND_ATTRS(sys_may);
625 schema_entry = talloc_asprintf_append(schema_entry,
627 IF_NULL_FAIL_RET(schema_entry);
630 schema_entry = talloc_asprintf_append(schema_entry,
634 case TARGET_OPENLDAP:
635 fprintf(out, "%s\n\n", schema_entry);
637 case TARGET_FEDORA_DS:
638 fprintf(out, "%s\n", schema_entry);
647 int main(int argc, const char **argv)
650 struct ldb_cmdline *options;
653 struct ldb_context *ldb;
654 struct schema_conv ret;
655 const char *target_str;
656 enum convert_target target;
660 ctx = talloc_new(NULL);
663 options = ldb_cmdline_process(ldb, argc, argv, usage);
665 if (options->input) {
666 in = fopen(options->input, "r");
668 perror(options->input);
672 if (options->output) {
673 out = fopen(options->output, "w");
675 perror(options->output);
680 target_str = lp_parm_string(-1, "convert", "target");
682 if (!target_str || strcasecmp(target_str, "openldap") == 0) {
683 target = TARGET_OPENLDAP;
684 } else if (strcasecmp(target_str, "fedora-ds") == 0) {
685 target = TARGET_FEDORA_DS;
687 printf("Unsupported target: %s\n", target_str);
691 ret = process_convert(ldb, target, in, out);
696 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);