summaryrefslogtreecommitdiff
path: root/source3/lib/ldb/tools/ad2oLschema.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/lib/ldb/tools/ad2oLschema.c')
-rw-r--r--source3/lib/ldb/tools/ad2oLschema.c630
1 files changed, 630 insertions, 0 deletions
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 <options>\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;
+}