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 *description
= attribute
->adminDescription
;
225 const char *oid
= attribute
->attributeID_oid
;
226 const char *syntax
= attribute
->attributeSyntax_oid
;
227 const char *equality
= NULL
, *substring
= NULL
;
228 bool single_value
= attribute
->isSingleValued
;
230 const struct dsdb_syntax
*map
= find_syntax_map_by_ad_syntax(attribute
->oMSyntax
);
231 char *schema_entry
= NULL
;
234 /* We have been asked to skip some attributes/objectClasses */
235 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
240 /* We might have been asked to remap this oid, due to a conflict */
241 for (j
=0; oid
&& oid_map
&& oid_map
[j
].old_oid
; j
++) {
242 if (strcasecmp(oid
, oid_map
[j
].old_oid
) == 0) {
243 oid
= oid_map
[j
].new_oid
;
249 /* We might have been asked to remap this oid,
250 * due to a conflict, or lack of
252 syntax
= map
->ldap_oid
;
253 /* We might have been asked to remap this oid, due to a conflict */
254 for (j
=0; syntax
&& oid_map
&& oid_map
[j
].old_oid
; j
++) {
255 if (strcasecmp(syntax
, oid_map
[j
].old_oid
) == 0) {
256 syntax
= oid_map
[j
].new_oid
;
261 equality
= map
->equality
;
262 substring
= map
->substring
;
265 /* We might have been asked to remap this name, due to a conflict */
266 for (j
=0; name
&& attr_map
&& attr_map
[j
].old_attr
; j
++) {
267 if (strcasecmp(name
, attr_map
[j
].old_attr
) == 0) {
268 name
= attr_map
[j
].new_attr
;
273 schema_entry
= schema_attribute_description(mem_ctx
, target
, seperator
, oid
, name
, description
, equality
, substring
, syntax
, single_value
, false);
275 if (schema_entry
== NULL
) {
281 case TARGET_OPENLDAP
:
282 fprintf(out
, "attributetype %s\n\n", schema_entry
);
284 case TARGET_FEDORA_DS
:
285 fprintf(out
, "attributeTypes: %s\n", schema_entry
);
291 /* This is already sorted to have 'top' and similar classes first */
292 for (objectclass
=schema
->classes
; objectclass
; objectclass
= objectclass
->next
) {
293 const char *name
= objectclass
->lDAPDisplayName
;
294 const char *description
= objectclass
->adminDescription
;
295 const char *oid
= objectclass
->governsID_oid
;
296 const char *subClassOf
= objectclass
->subClassOf
;
297 int objectClassCategory
= objectclass
->objectClassCategory
;
300 char *schema_entry
= NULL
;
301 const char *objectclass_name_as_list
[] = {
302 objectclass
->lDAPDisplayName
,
308 /* We have been asked to skip some attributes/objectClasses */
309 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
314 /* We might have been asked to remap this oid, due to a conflict */
315 for (j
=0; oid_map
&& oid_map
[j
].old_oid
; j
++) {
316 if (strcasecmp(oid
, oid_map
[j
].old_oid
) == 0) {
317 oid
= oid_map
[j
].new_oid
;
322 /* We might have been asked to remap this name, due to a conflict */
323 for (j
=0; name
&& attr_map
&& attr_map
[j
].old_attr
; j
++) {
324 if (strcasecmp(name
, attr_map
[j
].old_attr
) == 0) {
325 name
= attr_map
[j
].new_attr
;
330 may
= dsdb_full_attribute_list(mem_ctx
, schema
, objectclass_name_as_list
, DSDB_SCHEMA_ALL_MAY
);
332 for (j
=0; may
&& may
[j
]; j
++) {
333 /* We might have been asked to remap this name, due to a conflict */
334 for (attr_idx
=0; attr_map
&& attr_map
[attr_idx
].old_attr
; attr_idx
++) {
335 if (strcasecmp(may
[j
], attr_map
[attr_idx
].old_attr
) == 0) {
336 may
[j
] = attr_map
[attr_idx
].new_attr
;
342 must
= dsdb_full_attribute_list(mem_ctx
, schema
, objectclass_name_as_list
, DSDB_SCHEMA_ALL_MUST
);
344 for (j
=0; must
&& must
[j
]; j
++) {
345 /* We might have been asked to remap this name, due to a conflict */
346 for (attr_idx
=0; attr_map
&& attr_map
[attr_idx
].old_attr
; attr_idx
++) {
347 if (strcasecmp(must
[j
], attr_map
[attr_idx
].old_attr
) == 0) {
348 must
[j
] = attr_map
[attr_idx
].new_attr
;
354 schema_entry
= schema_class_description(mem_ctx
, target
,
364 if (schema_entry
== NULL
) {
370 case TARGET_OPENLDAP
:
371 fprintf(out
, "objectclass %s\n\n", schema_entry
);
373 case TARGET_FEDORA_DS
:
374 fprintf(out
, "objectClasses: %s\n", schema_entry
);
383 int main(int argc
, const char **argv
)
386 struct ldb_cmdline
*options
;
389 struct ldb_context
*ldb
;
390 struct schema_conv ret
;
391 const char *target_str
;
392 enum dsdb_schema_convert_target target
;
394 ctx
= talloc_new(NULL
);
395 ldb
= ldb_init(ctx
, NULL
);
397 options
= ldb_cmdline_process(ldb
, argc
, argv
, usage
);
399 if (options
->input
) {
400 in
= fopen(options
->input
, "r");
402 perror(options
->input
);
406 if (options
->output
) {
407 out
= fopen(options
->output
, "w");
409 perror(options
->output
);
414 target_str
= lp_parm_string(cmdline_lp_ctx
, NULL
, "convert", "target");
416 if (!target_str
|| strcasecmp(target_str
, "openldap") == 0) {
417 target
= TARGET_OPENLDAP
;
418 } else if (strcasecmp(target_str
, "fedora-ds") == 0) {
419 target
= TARGET_FEDORA_DS
;
421 printf("Unsupported target: %s\n", target_str
);
425 ret
= process_convert(ldb
, target
, in
, out
);
430 printf("Converted %d records (skipped %d) with %d failures\n", ret
.count
, ret
.skipped
, ret
.failures
);