r18866: Jeremy and Volker have given the go-ahead on the group mapping ldb
[kai/samba-autobuild/.git] / 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 (file)
index 0000000..8b1203b
--- /dev/null
@@ -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;
+}