4 Copyright (C) Andrew Bartlett 2006-2008
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 3 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, see <http://www.gnu.org/licenses/>.
27 * Component: ad2oLschema
29 * Description: utility to convert an AD schema into the format required by OpenLDAP
31 * Author: Andrew Bartlett
35 #include "ldb_includes.h"
36 #include "system/locale.h"
37 #include "lib/ldb/tools/cmdline.h"
38 #include "param/param.h"
39 #include "lib/cmdline/popt_common.h"
40 #include "dsdb/samdb/samdb.h"
49 static void usage(void)
51 printf("Usage: ad2oLschema <options>\n");
52 printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
54 printf(" -I inputfile inputfile of mapped OIDs and skipped attributes/ObjectClasses");
55 printf(" -H url LDB or LDAP server to read schmea from\n");
56 printf(" -O outputfile outputfile otherwise STDOUT\n");
57 printf(" -o options pass options like modules to activate\n");
58 printf(" e.g: -o modules:timestamps\n");
60 printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
64 static struct ldb_dn
*find_schema_dn(struct ldb_context
*ldb
, TALLOC_CTX
*mem_ctx
)
66 const char *rootdse_attrs
[] = {"schemaNamingContext", NULL
};
67 struct ldb_dn
*schemadn
;
68 struct ldb_dn
*basedn
= ldb_dn_new(mem_ctx
, ldb
, NULL
);
69 struct ldb_result
*rootdse_res
;
70 struct ldb_result
*schema_res
;
77 /* Search for rootdse */
78 ldb_ret
= ldb_search(ldb
, mem_ctx
, &rootdse_res
,
79 basedn
, LDB_SCOPE_BASE
, rootdse_attrs
, NULL
);
80 if (ldb_ret
!= LDB_SUCCESS
) {
81 ldb_ret
= ldb_search(ldb
, mem_ctx
, &schema_res
, basedn
, LDB_SCOPE_SUBTREE
,
82 NULL
, "(&(objectClass=dMD)(cn=Schema))");
84 printf("cn=Schema Search failed: %s\n", ldb_errstring(ldb
));
88 if (schema_res
->count
!= 1) {
89 talloc_free(schema_res
);
90 printf("Failed to find rootDSE");
94 schemadn
= talloc_steal(mem_ctx
, schema_res
->msgs
[0]->dn
);
95 talloc_free(schema_res
);
99 if (rootdse_res
->count
!= 1) {
100 printf("Failed to find rootDSE");
101 talloc_free(rootdse_res
);
106 schemadn
= ldb_msg_find_attr_as_dn(ldb
, mem_ctx
, rootdse_res
->msgs
[0], "schemaNamingContext");
107 talloc_free(rootdse_res
);
117 static struct schema_conv
process_convert(struct ldb_context
*ldb
, enum dsdb_schema_convert_target target
, FILE *in
, FILE *out
)
119 /* Read list of attributes to skip, OIDs to map */
120 TALLOC_CTX
*mem_ctx
= talloc_new(ldb
);
122 const char **attrs_skip
= NULL
;
128 int num_oid_maps
= 0;
133 int num_attr_maps
= 0;
134 struct dsdb_class
*objectclass
;
135 struct dsdb_attribute
*attribute
;
136 struct ldb_dn
*schemadn
;
137 struct schema_conv ret
;
138 struct dsdb_schema
*schema
;
139 const char *seperator
;
148 while ((line
= afdgets(fileno(in
), mem_ctx
, 0))) {
150 if (line
[0] == '\0') {
154 if (line
[0] == '#') {
157 if (isdigit(line
[0])) {
158 char *p
= strchr(line
, ':');
165 oid_map
= talloc_realloc(mem_ctx
, oid_map
, struct oid_map
, num_oid_maps
+ 2);
166 trim_string(line
, " ", " ");
167 oid_map
[num_oid_maps
].old_oid
= talloc_move(oid_map
, &line
);
168 trim_string(p
, " ", " ");
169 oid_map
[num_oid_maps
].new_oid
= p
;
171 oid_map
[num_oid_maps
].old_oid
= NULL
;
173 char *p
= strchr(line
, ':');
175 /* remap attribute/objectClass */
178 attr_map
= talloc_realloc(mem_ctx
, attr_map
, struct attr_map
, num_attr_maps
+ 2);
179 trim_string(line
, " ", " ");
180 attr_map
[num_attr_maps
].old_attr
= talloc_move(attr_map
, &line
);
181 trim_string(p
, " ", " ");
182 attr_map
[num_attr_maps
].new_attr
= p
;
184 attr_map
[num_attr_maps
].old_attr
= NULL
;
186 /* skip attribute/objectClass */
187 attrs_skip
= talloc_realloc(mem_ctx
, attrs_skip
, const char *, num_skip
+ 2);
188 trim_string(line
, " ", " ");
189 attrs_skip
[num_skip
] = talloc_move(attrs_skip
, &line
);
191 attrs_skip
[num_skip
] = NULL
;
196 schemadn
= find_schema_dn(ldb
, mem_ctx
);
198 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb
));
203 ldb_ret
= dsdb_schema_from_schema_dn(mem_ctx
, ldb
,
204 lp_iconv_convenience(cmdline_lp_ctx
),
205 schemadn
, &schema
, &error_string
);
206 if (ldb_ret
!= LDB_SUCCESS
) {
207 printf("Failed to load schema: %s\n", error_string
);
213 case TARGET_OPENLDAP
:
216 case TARGET_FEDORA_DS
:
218 fprintf(out
, "dn: cn=schema\n");
222 for (attribute
=schema
->attributes
; attribute
; attribute
= attribute
->next
) {
223 const char *name
= attribute
->lDAPDisplayName
;
224 const char *oid
= attribute
->attributeID_oid
;
225 const char *syntax
= attribute
->attributeSyntax_oid
;
226 const char *equality
= NULL
, *substring
= NULL
;
227 bool single_value
= attribute
->isSingleValued
;
229 char *schema_entry
= NULL
;
232 /* We have been asked to skip some attributes/objectClasses */
233 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
238 /* We might have been asked to remap this oid, due to a conflict */
239 for (j
=0; oid
&& oid_map
&& oid_map
[j
].old_oid
; j
++) {
240 if (strcasecmp(oid
, oid_map
[j
].old_oid
) == 0) {
241 oid
= oid_map
[j
].new_oid
;
246 if (attribute
->syntax
) {
247 /* We might have been asked to remap this oid,
248 * due to a conflict, or lack of
250 syntax
= attribute
->syntax
->ldap_oid
;
251 /* We might have been asked to remap this oid, due to a conflict */
252 for (j
=0; syntax
&& oid_map
&& oid_map
[j
].old_oid
; j
++) {
253 if (strcasecmp(syntax
, oid_map
[j
].old_oid
) == 0) {
254 syntax
= oid_map
[j
].new_oid
;
259 equality
= attribute
->syntax
->equality
;
260 substring
= attribute
->syntax
->substring
;
263 /* We might have been asked to remap this name, due to a conflict */
264 for (j
=0; name
&& attr_map
&& attr_map
[j
].old_attr
; j
++) {
265 if (strcasecmp(name
, attr_map
[j
].old_attr
) == 0) {
266 name
= attr_map
[j
].new_attr
;
271 schema_entry
= schema_attribute_description(mem_ctx
,
285 if (schema_entry
== NULL
) {
291 case TARGET_OPENLDAP
:
292 fprintf(out
, "attributetype %s\n\n", schema_entry
);
294 case TARGET_FEDORA_DS
:
295 fprintf(out
, "attributeTypes: %s\n", schema_entry
);
301 /* This is already sorted to have 'top' and similar classes first */
302 for (objectclass
=schema
->classes
; objectclass
; objectclass
= objectclass
->next
) {
303 const char *name
= objectclass
->lDAPDisplayName
;
304 const char *oid
= objectclass
->governsID_oid
;
305 const char *subClassOf
= objectclass
->subClassOf
;
306 int objectClassCategory
= objectclass
->objectClassCategory
;
309 char *schema_entry
= NULL
;
310 const char *objectclass_name_as_list
[] = {
311 objectclass
->lDAPDisplayName
,
317 /* We have been asked to skip some attributes/objectClasses */
318 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
323 /* We might have been asked to remap this oid, due to a conflict */
324 for (j
=0; oid_map
&& oid_map
[j
].old_oid
; j
++) {
325 if (strcasecmp(oid
, oid_map
[j
].old_oid
) == 0) {
326 oid
= oid_map
[j
].new_oid
;
331 /* We might have been asked to remap this name, due to a conflict */
332 for (j
=0; name
&& attr_map
&& attr_map
[j
].old_attr
; j
++) {
333 if (strcasecmp(name
, attr_map
[j
].old_attr
) == 0) {
334 name
= attr_map
[j
].new_attr
;
339 may
= dsdb_full_attribute_list(mem_ctx
, schema
, objectclass_name_as_list
, DSDB_SCHEMA_ALL_MAY
);
341 for (j
=0; may
&& may
[j
]; j
++) {
342 /* We might have been asked to remap this name, due to a conflict */
343 for (attr_idx
=0; attr_map
&& attr_map
[attr_idx
].old_attr
; attr_idx
++) {
344 if (strcasecmp(may
[j
], attr_map
[attr_idx
].old_attr
) == 0) {
345 may
[j
] = attr_map
[attr_idx
].new_attr
;
351 must
= dsdb_full_attribute_list(mem_ctx
, schema
, objectclass_name_as_list
, DSDB_SCHEMA_ALL_MUST
);
353 for (j
=0; must
&& must
[j
]; j
++) {
354 /* We might have been asked to remap this name, due to a conflict */
355 for (attr_idx
=0; attr_map
&& attr_map
[attr_idx
].old_attr
; attr_idx
++) {
356 if (strcasecmp(must
[j
], attr_map
[attr_idx
].old_attr
) == 0) {
357 must
[j
] = attr_map
[attr_idx
].new_attr
;
363 schema_entry
= schema_class_description(mem_ctx
, target
,
373 if (schema_entry
== NULL
) {
379 case TARGET_OPENLDAP
:
380 fprintf(out
, "objectclass %s\n\n", schema_entry
);
382 case TARGET_FEDORA_DS
:
383 fprintf(out
, "objectClasses: %s\n", schema_entry
);
392 int main(int argc
, const char **argv
)
395 struct ldb_cmdline
*options
;
398 struct ldb_context
*ldb
;
399 struct schema_conv ret
;
400 const char *target_str
;
401 enum dsdb_schema_convert_target target
;
403 ctx
= talloc_new(NULL
);
404 ldb
= ldb_init(ctx
, NULL
);
406 options
= ldb_cmdline_process(ldb
, argc
, argv
, usage
);
408 if (options
->input
) {
409 in
= fopen(options
->input
, "r");
411 perror(options
->input
);
415 if (options
->output
) {
416 out
= fopen(options
->output
, "w");
418 perror(options
->output
);
423 target_str
= lp_parm_string(cmdline_lp_ctx
, NULL
, "convert", "target");
425 if (!target_str
|| strcasecmp(target_str
, "openldap") == 0) {
426 target
= TARGET_OPENLDAP
;
427 } else if (strcasecmp(target_str
, "fedora-ds") == 0) {
428 target
= TARGET_FEDORA_DS
;
430 printf("Unsupported target: %s\n", target_str
);
434 ret
= process_convert(ldb
, target
, in
, out
);
439 printf("Converted %d records (skipped %d) with %d failures\n", ret
.count
, ret
.skipped
, ret
.failures
);