From 3e2c696e45b24b0192ab7b1ddaf1dd4d79571609 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 24 Sep 2006 02:49:04 +0000 Subject: r18866: Jeremy and Volker have given the go-ahead on the group mapping ldb code. Yay! This first commit copies lib/ldb/ from Samba4. A huge congratulations should go to Simo on this - he has put an enormous amount of work into ldb, and it's great to see it go into the Samba3 tree. (This used to be commit bbedf2e34315f5c420a3a05dfe22b1d5cf79f042) --- source3/lib/ldb/tools/ad2oLschema.c | 630 ++++++++++++++++++++++++++++++++++++ 1 file changed, 630 insertions(+) create mode 100644 source3/lib/ldb/tools/ad2oLschema.c (limited to 'source3/lib/ldb/tools/ad2oLschema.c') diff --git a/source3/lib/ldb/tools/ad2oLschema.c b/source3/lib/ldb/tools/ad2oLschema.c new file mode 100644 index 0000000000..8b1203bff0 --- /dev/null +++ b/source3/lib/ldb/tools/ad2oLschema.c @@ -0,0 +1,630 @@ +/* + ldb database library + + Copyright (C) Andrew Bartlett 2006 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Name: ldb + * + * Component: ad2oLschema + * + * Description: utility to convert an AD schema into the format required by OpenLDAP + * + * Author: Andrew Tridgell + */ + +#include "includes.h" +#include "ldb/include/includes.h" +#include "system/locale.h" +#include "ldb/tools/cmdline.h" +#include "ldb/tools/convert.h" + +struct schema_conv { + int count; + int skipped; + int failures; +}; + +enum convert_target { + TARGET_OPENLDAP, + TARGET_FEDORA_DS +}; + + +static void usage(void) +{ + printf("Usage: ad2oLschema \n"); + printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n"); + printf("Options:\n"); + printf(" -I inputfile inputfile of mapped OIDs and skipped attributes/ObjectClasses"); + printf(" -H url LDB or LDAP server to read schmea from\n"); + printf(" -O outputfile outputfile otherwise STDOUT\n"); + printf(" -o options pass options like modules to activate\n"); + printf(" e.g: -o modules:timestamps\n"); + printf("\n"); + printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n"); + exit(1); +} + +static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn, + TALLOC_CTX *mem_ctx, + struct ldb_result **attrs_res) +{ + TALLOC_CTX *local_ctx = talloc_new(mem_ctx); + int ret; + const char *attrs[] = { + "lDAPDisplayName", + "isSingleValued", + "attributeID", + "attributeSyntax", + "description", + NULL + }; + + if (!local_ctx) { + return LDB_ERR_OPERATIONS_ERROR; + } + + /* Downlaod schema */ + ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, + "objectClass=attributeSchema", + attrs, attrs_res); + if (ret != LDB_SUCCESS) { + printf("Search failed: %s\n", ldb_errstring(ldb)); + return LDB_ERR_OPERATIONS_ERROR; + } + + return ret; +} + +static const char *oc_attrs[] = { + "lDAPDisplayName", + "mayContain", + "mustContain", + "systemMayContain", + "systemMustContain", + "objectClassCategory", + "governsID", + "description", + "subClassOf", + NULL +}; + +static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn, + TALLOC_CTX *mem_ctx, + struct ldb_result *search_from, + struct ldb_result *res_list) +{ + int i; + int ret = 0; + for (i=0; i < search_from->count; i++) { + struct ldb_result *res; + const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i], + "lDAPDisplayname", NULL); + char *filter = talloc_asprintf(mem_ctx, "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))", + name, name); + + ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, + filter, + oc_attrs, &res); + talloc_free(filter); + if (ret != LDB_SUCCESS) { + printf("Search failed: %s\n", ldb_errstring(ldb)); + return ret; + } + + talloc_steal(mem_ctx, res); + + res_list->msgs = talloc_realloc(res_list, res_list->msgs, + struct ldb_message *, res_list->count + 2); + if (!res_list->msgs) { + return LDB_ERR_OPERATIONS_ERROR; + } + res_list->msgs[res_list->count] = talloc_move(res_list, + &search_from->msgs[i]); + res_list->count++; + res_list->msgs[res_list->count] = NULL; + + if (res->count > 0) { + ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list); + } + if (ret != LDB_SUCCESS) { + return ret; + } + } + return ret; +} + +static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn, + TALLOC_CTX *mem_ctx, + struct ldb_result **objectclasses_res) +{ + TALLOC_CTX *local_ctx = talloc_new(mem_ctx); + struct ldb_result *top_res, *ret_res; + int ret; + if (!local_ctx) { + return LDB_ERR_OPERATIONS_ERROR; + } + + /* Downlaod 'top' */ + ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, + "(&(objectClass=classSchema)(lDAPDisplayName=top))", + oc_attrs, &top_res); + if (ret != LDB_SUCCESS) { + printf("Search failed: %s\n", ldb_errstring(ldb)); + return LDB_ERR_OPERATIONS_ERROR; + } + + talloc_steal(local_ctx, top_res); + + if (top_res->count != 1) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret_res = talloc_zero(local_ctx, struct ldb_result); + if (!ret_res) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res); + + if (ret != LDB_SUCCESS) { + printf("Search failed: %s\n", ldb_errstring(ldb)); + return LDB_ERR_OPERATIONS_ERROR; + } + + *objectclasses_res = talloc_move(mem_ctx, &ret_res); + return ret; +} + +static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx) +{ + const char *rootdse_attrs[] = {"schemaNamingContext", NULL}; + struct ldb_dn *schemadn; + struct ldb_dn *basedn = ldb_dn_explode(mem_ctx, ""); + struct ldb_result *rootdse_res; + int ldb_ret; + if (!basedn) { + return NULL; + } + + /* Search for rootdse */ + ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, rootdse_attrs, &rootdse_res); + if (ldb_ret != LDB_SUCCESS) { + printf("Search failed: %s\n", ldb_errstring(ldb)); + return NULL; + } + + talloc_steal(mem_ctx, rootdse_res); + + if (rootdse_res->count != 1) { + printf("Failed to find rootDSE"); + return NULL; + } + + /* Locate schema */ + schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext"); + if (!schemadn) { + return NULL; + } + + talloc_free(rootdse_res); + return schemadn; +} + +#define IF_NULL_FAIL_RET(x) do { \ + if (!x) { \ + ret.failures++; \ + return ret; \ + } \ + } while (0) + + +static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out) +{ + /* Read list of attributes to skip, OIDs to map */ + TALLOC_CTX *mem_ctx = talloc_new(ldb); + char *line; + const char **attrs_skip = NULL; + int num_skip = 0; + struct oid_map { + char *old_oid; + char *new_oid; + } *oid_map = NULL; + int num_maps = 0; + struct ldb_result *attrs_res, *objectclasses_res; + struct ldb_dn *schemadn; + struct schema_conv ret; + + int ldb_ret, i; + + ret.count = 0; + ret.skipped = 0; + ret.failures = 0; + + while ((line = afdgets(fileno(in), mem_ctx, 0))) { + /* Blank Line */ + if (line[0] == '\0') { + continue; + } + /* Comment */ + if (line[0] == '#') { + continue; + } + if (isdigit(line[0])) { + char *p = strchr(line, ':'); + IF_NULL_FAIL_RET(p); + if (!p) { + ret.failures = 1; + return ret; + } + p[0] = '\0'; + p++; + oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_maps + 2); + trim_string(line, " ", " "); + oid_map[num_maps].old_oid = talloc_move(oid_map, &line); + trim_string(p, " ", " "); + oid_map[num_maps].new_oid = p; + num_maps++; + oid_map[num_maps].old_oid = NULL; + } else { + attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2); + trim_string(line, " ", " "); + attrs_skip[num_skip] = talloc_move(attrs_skip, &line); + num_skip++; + attrs_skip[num_skip] = NULL; + } + } + + schemadn = find_schema_dn(ldb, mem_ctx); + if (!schemadn) { + printf("Failed to find schema DN: %s\n", ldb_errstring(ldb)); + ret.failures = 1; + return ret; + } + + ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res); + if (ldb_ret != LDB_SUCCESS) { + printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb)); + ret.failures = 1; + return ret; + } + + switch (target) { + case TARGET_OPENLDAP: + break; + case TARGET_FEDORA_DS: + fprintf(out, "dn: cn=schema\n"); + break; + } + + for (i=0; i < attrs_res->count; i++) { + struct ldb_message *msg = attrs_res->msgs[i]; + + const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL); + const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL); + const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL); + const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL); + BOOL single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", False); + const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax); + char *schema_entry = NULL; + int j; + + /* We have been asked to skip some attributes/objectClasses */ + if (str_list_check_ci(attrs_skip, name)) { + ret.skipped++; + continue; + } + + /* We might have been asked to remap this oid, due to a conflict */ + for (j=0; oid && oid_map[j].old_oid; j++) { + if (strcmp(oid, oid_map[j].old_oid) == 0) { + oid = oid_map[j].new_oid; + break; + } + } + + switch (target) { + case TARGET_OPENLDAP: + schema_entry = talloc_asprintf(mem_ctx, + "attributetype (\n" + " %s\n", oid); + break; + case TARGET_FEDORA_DS: + schema_entry = talloc_asprintf(mem_ctx, + "attributeTypes: (\n" + " %s\n", oid); + break; + } + IF_NULL_FAIL_RET(schema_entry); + + schema_entry = talloc_asprintf_append(schema_entry, + " NAME '%s'\n", name); + IF_NULL_FAIL_RET(schema_entry); + + if (description) { + schema_entry = talloc_asprintf_append(schema_entry, + " DESC %s\n", description); + IF_NULL_FAIL_RET(schema_entry); + } + + if (map) { + const char *syntax_oid; + if (map->equality) { + schema_entry = talloc_asprintf_append(schema_entry, + " EQUALITY %s\n", map->equality); + IF_NULL_FAIL_RET(schema_entry); + } + if (map->substring) { + schema_entry = talloc_asprintf_append(schema_entry, + " SUBSTR %s\n", map->substring); + IF_NULL_FAIL_RET(schema_entry); + } + syntax_oid = map->Standard_OID; + /* We might have been asked to remap this oid, + * due to a conflict, or lack of + * implementation */ + for (j=0; syntax_oid && oid_map[j].old_oid; j++) { + if (strcmp(syntax_oid, oid_map[j].old_oid) == 0) { + syntax_oid = oid_map[j].new_oid; + break; + } + } + schema_entry = talloc_asprintf_append(schema_entry, + " SYNTAX %s\n", syntax_oid); + IF_NULL_FAIL_RET(schema_entry); + } + + if (single_value) { + schema_entry = talloc_asprintf_append(schema_entry, + " SINGLE-VALUE\n"); + IF_NULL_FAIL_RET(schema_entry); + } + + schema_entry = talloc_asprintf_append(schema_entry, + " )"); + + switch (target) { + case TARGET_OPENLDAP: + fprintf(out, "%s\n\n", schema_entry); + break; + case TARGET_FEDORA_DS: + fprintf(out, "%s\n", schema_entry); + break; + } + ret.count++; + } + + ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res); + if (ldb_ret != LDB_SUCCESS) { + printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb)); + ret.failures = 1; + return ret; + } + + for (i=0; i < objectclasses_res->count; i++) { + struct ldb_message *msg = objectclasses_res->msgs[i]; + const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL); + const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL); + const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL); + const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL); + int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0); + struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain"); + struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain"); + struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain"); + struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain"); + char *schema_entry = NULL; + int j; + + /* We have been asked to skip some attributes/objectClasses */ + if (str_list_check_ci(attrs_skip, name)) { + ret.skipped++; + continue; + } + + /* We might have been asked to remap this oid, due to a conflict */ + for (j=0; oid_map[j].old_oid; j++) { + if (strcmp(oid, oid_map[j].old_oid) == 0) { + oid = oid_map[j].new_oid; + break; + } + } + + switch (target) { + case TARGET_OPENLDAP: + schema_entry = talloc_asprintf(mem_ctx, + "objectclass (\n" + " %s\n", oid); + break; + case TARGET_FEDORA_DS: + schema_entry = talloc_asprintf(mem_ctx, + "objectClasses: (\n" + " %s\n", oid); + break; + } + IF_NULL_FAIL_RET(schema_entry); + if (!schema_entry) { + ret.failures++; + break; + } + + schema_entry = talloc_asprintf_append(schema_entry, + " NAME '%s'\n", name); + IF_NULL_FAIL_RET(schema_entry); + + if (!schema_entry) return ret; + + if (description) { + schema_entry = talloc_asprintf_append(schema_entry, + " DESC %s\n", description); + IF_NULL_FAIL_RET(schema_entry); + } + + if (subClassOf) { + schema_entry = talloc_asprintf_append(schema_entry, + " SUP %s\n", subClassOf); + IF_NULL_FAIL_RET(schema_entry); + } + + switch (objectClassCategory) { + case 1: + schema_entry = talloc_asprintf_append(schema_entry, + " STRUCTURAL\n"); + IF_NULL_FAIL_RET(schema_entry); + break; + case 2: + schema_entry = talloc_asprintf_append(schema_entry, + " ABSTRACT\n"); + IF_NULL_FAIL_RET(schema_entry); + break; + case 3: + schema_entry = talloc_asprintf_append(schema_entry, + " AUXILIARY\n"); + IF_NULL_FAIL_RET(schema_entry); + break; + } + +#define APPEND_ATTRS(attributes) \ + do { \ + int k; \ + for (k=0; attributes && k < attributes->num_values; k++) { \ + schema_entry = talloc_asprintf_append(schema_entry, \ + " %s", \ + (const char *)attributes->values[k].data); \ + IF_NULL_FAIL_RET(schema_entry); \ + if (k != (attributes->num_values - 1)) { \ + schema_entry = talloc_asprintf_append(schema_entry, \ + " $"); \ + IF_NULL_FAIL_RET(schema_entry); \ + if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \ + schema_entry = talloc_asprintf_append(schema_entry, \ + "\n "); \ + IF_NULL_FAIL_RET(schema_entry); \ + } \ + } \ + } \ + } while (0) + + if (must || sys_must) { + schema_entry = talloc_asprintf_append(schema_entry, + " MUST ("); + IF_NULL_FAIL_RET(schema_entry); + + APPEND_ATTRS(must); + if (must && sys_must) { + schema_entry = talloc_asprintf_append(schema_entry, \ + " $"); \ + } + APPEND_ATTRS(sys_must); + + schema_entry = talloc_asprintf_append(schema_entry, + " )\n"); + IF_NULL_FAIL_RET(schema_entry); + } + + if (may || sys_may) { + schema_entry = talloc_asprintf_append(schema_entry, + " MAY ("); + IF_NULL_FAIL_RET(schema_entry); + + APPEND_ATTRS(may); + if (may && sys_may) { + schema_entry = talloc_asprintf_append(schema_entry, \ + " $"); \ + } + APPEND_ATTRS(sys_may); + + schema_entry = talloc_asprintf_append(schema_entry, + " )\n"); + IF_NULL_FAIL_RET(schema_entry); + } + + schema_entry = talloc_asprintf_append(schema_entry, + " )"); + + switch (target) { + case TARGET_OPENLDAP: + fprintf(out, "%s\n\n", schema_entry); + break; + case TARGET_FEDORA_DS: + fprintf(out, "%s\n", schema_entry); + break; + } + ret.count++; + } + + return ret; +} + + int main(int argc, const char **argv) +{ + TALLOC_CTX *ctx; + struct ldb_cmdline *options; + FILE *in = stdin; + FILE *out = stdout; + struct ldb_context *ldb; + struct schema_conv ret; + const char *target_str; + enum convert_target target; + + ldb_global_init(); + + ctx = talloc_new(NULL); + ldb = ldb_init(ctx); + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + if (options->input) { + in = fopen(options->input, "r"); + if (!in) { + perror(options->input); + exit(1); + } + } + if (options->output) { + out = fopen(options->output, "w"); + if (!out) { + perror(options->output); + exit(1); + } + } + + target_str = lp_parm_string(-1, "convert", "target"); + + if (!target_str || strcasecmp(target_str, "openldap") == 0) { + target = TARGET_OPENLDAP; + } else if (strcasecmp(target_str, "fedora-ds") == 0) { + target = TARGET_FEDORA_DS; + } else { + printf("Unsupported target: %s\n", target_str); + exit(1); + } + + ret = process_convert(ldb, target, in, out); + + fclose(in); + fclose(out); + + printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures); + + return 0; +} -- cgit