4 Copyright (C) Andrew Bartlett 2006
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 Tridgell
35 #include "ldb/include/includes.h"
36 #include "system/locale.h"
37 #include "ldb/tools/cmdline.h"
38 #include "ldb/tools/convert.h"
52 static void usage(void)
54 printf("Usage: ad2oLschema <options>\n");
55 printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
57 printf(" -I inputfile inputfile of mapped OIDs and skipped attributes/ObjectClasses");
58 printf(" -H url LDB or LDAP server to read schmea from\n");
59 printf(" -O outputfile outputfile otherwise STDOUT\n");
60 printf(" -o options pass options like modules to activate\n");
61 printf(" e.g: -o modules:timestamps\n");
63 printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
67 static int fetch_attrs_schema(struct ldb_context
*ldb
, struct ldb_dn
*schemadn
,
69 struct ldb_result
**attrs_res
)
71 TALLOC_CTX
*local_ctx
= talloc_new(mem_ctx
);
73 const char *attrs
[] = {
83 return LDB_ERR_OPERATIONS_ERROR
;
87 ret
= ldb_search(ldb
, schemadn
, LDB_SCOPE_SUBTREE
,
88 "objectClass=attributeSchema",
90 if (ret
!= LDB_SUCCESS
) {
91 printf("Search failed: %s\n", ldb_errstring(ldb
));
92 return LDB_ERR_OPERATIONS_ERROR
;
98 static const char *oc_attrs
[] = {
104 "objectClassCategory",
111 static int fetch_oc_recursive(struct ldb_context
*ldb
, struct ldb_dn
*schemadn
,
113 struct ldb_result
*search_from
,
114 struct ldb_result
*res_list
)
118 for (i
=0; i
< search_from
->count
; i
++) {
119 struct ldb_result
*res
;
120 const char *name
= ldb_msg_find_attr_as_string(search_from
->msgs
[i
],
121 "lDAPDisplayname", NULL
);
122 char *filter
= talloc_asprintf(mem_ctx
, "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
125 ret
= ldb_search(ldb
, schemadn
, LDB_SCOPE_SUBTREE
,
129 if (ret
!= LDB_SUCCESS
) {
130 printf("Search failed: %s\n", ldb_errstring(ldb
));
134 talloc_steal(mem_ctx
, res
);
136 res_list
->msgs
= talloc_realloc(res_list
, res_list
->msgs
,
137 struct ldb_message
*, res_list
->count
+ 2);
138 if (!res_list
->msgs
) {
139 return LDB_ERR_OPERATIONS_ERROR
;
141 res_list
->msgs
[res_list
->count
] = talloc_move(res_list
,
142 &search_from
->msgs
[i
]);
144 res_list
->msgs
[res_list
->count
] = NULL
;
146 if (res
->count
> 0) {
147 ret
= fetch_oc_recursive(ldb
, schemadn
, mem_ctx
, res
, res_list
);
149 if (ret
!= LDB_SUCCESS
) {
156 static int fetch_objectclass_schema(struct ldb_context
*ldb
, struct ldb_dn
*schemadn
,
158 struct ldb_result
**objectclasses_res
)
160 TALLOC_CTX
*local_ctx
= talloc_new(mem_ctx
);
161 struct ldb_result
*top_res
, *ret_res
;
164 return LDB_ERR_OPERATIONS_ERROR
;
168 ret
= ldb_search(ldb
, schemadn
, LDB_SCOPE_SUBTREE
,
169 "(&(objectClass=classSchema)(lDAPDisplayName=top))",
171 if (ret
!= LDB_SUCCESS
) {
172 printf("Search failed: %s\n", ldb_errstring(ldb
));
173 return LDB_ERR_OPERATIONS_ERROR
;
176 talloc_steal(local_ctx
, top_res
);
178 if (top_res
->count
!= 1) {
179 return LDB_ERR_OPERATIONS_ERROR
;
182 ret_res
= talloc_zero(local_ctx
, struct ldb_result
);
184 return LDB_ERR_OPERATIONS_ERROR
;
187 ret
= fetch_oc_recursive(ldb
, schemadn
, local_ctx
, top_res
, ret_res
);
189 if (ret
!= LDB_SUCCESS
) {
190 printf("Search failed: %s\n", ldb_errstring(ldb
));
191 return LDB_ERR_OPERATIONS_ERROR
;
194 *objectclasses_res
= talloc_move(mem_ctx
, &ret_res
);
198 static struct ldb_dn
*find_schema_dn(struct ldb_context
*ldb
, TALLOC_CTX
*mem_ctx
)
200 const char *rootdse_attrs
[] = {"schemaNamingContext", NULL
};
201 struct ldb_dn
*schemadn
;
202 struct ldb_dn
*basedn
= ldb_dn_explode(mem_ctx
, "");
203 struct ldb_result
*rootdse_res
;
209 /* Search for rootdse */
210 ldb_ret
= ldb_search(ldb
, basedn
, LDB_SCOPE_BASE
, NULL
, rootdse_attrs
, &rootdse_res
);
211 if (ldb_ret
!= LDB_SUCCESS
) {
212 printf("Search failed: %s\n", ldb_errstring(ldb
));
216 talloc_steal(mem_ctx
, rootdse_res
);
218 if (rootdse_res
->count
!= 1) {
219 printf("Failed to find rootDSE");
224 schemadn
= ldb_msg_find_attr_as_dn(mem_ctx
, rootdse_res
->msgs
[0], "schemaNamingContext");
229 talloc_free(rootdse_res
);
233 #define IF_NULL_FAIL_RET(x) do { \
241 static struct schema_conv
process_convert(struct ldb_context
*ldb
, enum convert_target target
, FILE *in
, FILE *out
)
243 /* Read list of attributes to skip, OIDs to map */
244 TALLOC_CTX
*mem_ctx
= talloc_new(ldb
);
246 const char **attrs_skip
= NULL
;
253 struct ldb_result
*attrs_res
, *objectclasses_res
;
254 struct ldb_dn
*schemadn
;
255 struct schema_conv ret
;
263 while ((line
= afdgets(fileno(in
), mem_ctx
, 0))) {
265 if (line
[0] == '\0') {
269 if (line
[0] == '#') {
272 if (isdigit(line
[0])) {
273 char *p
= strchr(line
, ':');
281 oid_map
= talloc_realloc(mem_ctx
, oid_map
, struct oid_map
, num_maps
+ 2);
282 trim_string(line
, " ", " ");
283 oid_map
[num_maps
].old_oid
= talloc_move(oid_map
, &line
);
284 trim_string(p
, " ", " ");
285 oid_map
[num_maps
].new_oid
= p
;
287 oid_map
[num_maps
].old_oid
= NULL
;
289 attrs_skip
= talloc_realloc(mem_ctx
, attrs_skip
, const char *, num_skip
+ 2);
290 trim_string(line
, " ", " ");
291 attrs_skip
[num_skip
] = talloc_move(attrs_skip
, &line
);
293 attrs_skip
[num_skip
] = NULL
;
297 schemadn
= find_schema_dn(ldb
, mem_ctx
);
299 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb
));
304 ldb_ret
= fetch_attrs_schema(ldb
, schemadn
, mem_ctx
, &attrs_res
);
305 if (ldb_ret
!= LDB_SUCCESS
) {
306 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb
));
312 case TARGET_OPENLDAP
:
314 case TARGET_FEDORA_DS
:
315 fprintf(out
, "dn: cn=schema\n");
319 for (i
=0; i
< attrs_res
->count
; i
++) {
320 struct ldb_message
*msg
= attrs_res
->msgs
[i
];
322 const char *name
= ldb_msg_find_attr_as_string(msg
, "lDAPDisplayName", NULL
);
323 const char *description
= ldb_msg_find_attr_as_string(msg
, "description", NULL
);
324 const char *oid
= ldb_msg_find_attr_as_string(msg
, "attributeID", NULL
);
325 const char *syntax
= ldb_msg_find_attr_as_string(msg
, "attributeSyntax", NULL
);
326 BOOL single_value
= ldb_msg_find_attr_as_bool(msg
, "isSingleValued", False
);
327 const struct syntax_map
*map
= find_syntax_map_by_ad_oid(syntax
);
328 char *schema_entry
= NULL
;
331 /* We have been asked to skip some attributes/objectClasses */
332 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
337 /* We might have been asked to remap this oid, due to a conflict */
338 for (j
=0; oid
&& oid_map
[j
].old_oid
; j
++) {
339 if (strcmp(oid
, oid_map
[j
].old_oid
) == 0) {
340 oid
= oid_map
[j
].new_oid
;
346 case TARGET_OPENLDAP
:
347 schema_entry
= talloc_asprintf(mem_ctx
,
351 case TARGET_FEDORA_DS
:
352 schema_entry
= talloc_asprintf(mem_ctx
,
353 "attributeTypes: (\n"
357 IF_NULL_FAIL_RET(schema_entry
);
359 schema_entry
= talloc_asprintf_append(schema_entry
,
360 " NAME '%s'\n", name
);
361 IF_NULL_FAIL_RET(schema_entry
);
364 schema_entry
= talloc_asprintf_append(schema_entry
,
365 " DESC %s\n", description
);
366 IF_NULL_FAIL_RET(schema_entry
);
370 const char *syntax_oid
;
372 schema_entry
= talloc_asprintf_append(schema_entry
,
373 " EQUALITY %s\n", map
->equality
);
374 IF_NULL_FAIL_RET(schema_entry
);
376 if (map
->substring
) {
377 schema_entry
= talloc_asprintf_append(schema_entry
,
378 " SUBSTR %s\n", map
->substring
);
379 IF_NULL_FAIL_RET(schema_entry
);
381 syntax_oid
= map
->Standard_OID
;
382 /* We might have been asked to remap this oid,
383 * due to a conflict, or lack of
385 for (j
=0; syntax_oid
&& oid_map
[j
].old_oid
; j
++) {
386 if (strcmp(syntax_oid
, oid_map
[j
].old_oid
) == 0) {
387 syntax_oid
= oid_map
[j
].new_oid
;
391 schema_entry
= talloc_asprintf_append(schema_entry
,
392 " SYNTAX %s\n", syntax_oid
);
393 IF_NULL_FAIL_RET(schema_entry
);
397 schema_entry
= talloc_asprintf_append(schema_entry
,
399 IF_NULL_FAIL_RET(schema_entry
);
402 schema_entry
= talloc_asprintf_append(schema_entry
,
406 case TARGET_OPENLDAP
:
407 fprintf(out
, "%s\n\n", schema_entry
);
409 case TARGET_FEDORA_DS
:
410 fprintf(out
, "%s\n", schema_entry
);
416 ldb_ret
= fetch_objectclass_schema(ldb
, schemadn
, mem_ctx
, &objectclasses_res
);
417 if (ldb_ret
!= LDB_SUCCESS
) {
418 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb
));
423 for (i
=0; i
< objectclasses_res
->count
; i
++) {
424 struct ldb_message
*msg
= objectclasses_res
->msgs
[i
];
425 const char *name
= ldb_msg_find_attr_as_string(msg
, "lDAPDisplayName", NULL
);
426 const char *description
= ldb_msg_find_attr_as_string(msg
, "description", NULL
);
427 const char *oid
= ldb_msg_find_attr_as_string(msg
, "governsID", NULL
);
428 const char *subClassOf
= ldb_msg_find_attr_as_string(msg
, "subClassOf", NULL
);
429 int objectClassCategory
= ldb_msg_find_attr_as_int(msg
, "objectClassCategory", 0);
430 struct ldb_message_element
*must
= ldb_msg_find_element(msg
, "mustContain");
431 struct ldb_message_element
*sys_must
= ldb_msg_find_element(msg
, "systemMustContain");
432 struct ldb_message_element
*may
= ldb_msg_find_element(msg
, "mayContain");
433 struct ldb_message_element
*sys_may
= ldb_msg_find_element(msg
, "systemMayContain");
434 char *schema_entry
= NULL
;
437 /* We have been asked to skip some attributes/objectClasses */
438 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
443 /* We might have been asked to remap this oid, due to a conflict */
444 for (j
=0; oid_map
[j
].old_oid
; j
++) {
445 if (strcmp(oid
, oid_map
[j
].old_oid
) == 0) {
446 oid
= oid_map
[j
].new_oid
;
452 case TARGET_OPENLDAP
:
453 schema_entry
= talloc_asprintf(mem_ctx
,
457 case TARGET_FEDORA_DS
:
458 schema_entry
= talloc_asprintf(mem_ctx
,
463 IF_NULL_FAIL_RET(schema_entry
);
469 schema_entry
= talloc_asprintf_append(schema_entry
,
470 " NAME '%s'\n", name
);
471 IF_NULL_FAIL_RET(schema_entry
);
473 if (!schema_entry
) return ret
;
476 schema_entry
= talloc_asprintf_append(schema_entry
,
477 " DESC %s\n", description
);
478 IF_NULL_FAIL_RET(schema_entry
);
482 schema_entry
= talloc_asprintf_append(schema_entry
,
483 " SUP %s\n", subClassOf
);
484 IF_NULL_FAIL_RET(schema_entry
);
487 switch (objectClassCategory
) {
489 schema_entry
= talloc_asprintf_append(schema_entry
,
491 IF_NULL_FAIL_RET(schema_entry
);
494 schema_entry
= talloc_asprintf_append(schema_entry
,
496 IF_NULL_FAIL_RET(schema_entry
);
499 schema_entry
= talloc_asprintf_append(schema_entry
,
501 IF_NULL_FAIL_RET(schema_entry
);
505 #define APPEND_ATTRS(attributes) \
508 for (k=0; attributes && k < attributes->num_values; k++) { \
509 schema_entry = talloc_asprintf_append(schema_entry, \
511 (const char *)attributes->values[k].data); \
512 IF_NULL_FAIL_RET(schema_entry); \
513 if (k != (attributes->num_values - 1)) { \
514 schema_entry = talloc_asprintf_append(schema_entry, \
516 IF_NULL_FAIL_RET(schema_entry); \
517 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
518 schema_entry = talloc_asprintf_append(schema_entry, \
520 IF_NULL_FAIL_RET(schema_entry); \
526 if (must
|| sys_must
) {
527 schema_entry
= talloc_asprintf_append(schema_entry
,
529 IF_NULL_FAIL_RET(schema_entry
);
532 if (must
&& sys_must
) {
533 schema_entry
= talloc_asprintf_append(schema_entry
, \
536 APPEND_ATTRS(sys_must
);
538 schema_entry
= talloc_asprintf_append(schema_entry
,
540 IF_NULL_FAIL_RET(schema_entry
);
543 if (may
|| sys_may
) {
544 schema_entry
= talloc_asprintf_append(schema_entry
,
546 IF_NULL_FAIL_RET(schema_entry
);
549 if (may
&& sys_may
) {
550 schema_entry
= talloc_asprintf_append(schema_entry
, \
553 APPEND_ATTRS(sys_may
);
555 schema_entry
= talloc_asprintf_append(schema_entry
,
557 IF_NULL_FAIL_RET(schema_entry
);
560 schema_entry
= talloc_asprintf_append(schema_entry
,
564 case TARGET_OPENLDAP
:
565 fprintf(out
, "%s\n\n", schema_entry
);
567 case TARGET_FEDORA_DS
:
568 fprintf(out
, "%s\n", schema_entry
);
577 int main(int argc
, const char **argv
)
580 struct ldb_cmdline
*options
;
583 struct ldb_context
*ldb
;
584 struct schema_conv ret
;
585 const char *target_str
;
586 enum convert_target target
;
590 ctx
= talloc_new(NULL
);
593 options
= ldb_cmdline_process(ldb
, argc
, argv
, usage
);
595 if (options
->input
) {
596 in
= fopen(options
->input
, "r");
598 perror(options
->input
);
602 if (options
->output
) {
603 out
= fopen(options
->output
, "w");
605 perror(options
->output
);
610 target_str
= lp_parm_string(-1, "convert", "target");
612 if (!target_str
|| strcasecmp(target_str
, "openldap") == 0) {
613 target
= TARGET_OPENLDAP
;
614 } else if (strcasecmp(target_str
, "fedora-ds") == 0) {
615 target
= TARGET_FEDORA_DS
;
617 printf("Unsupported target: %s\n", target_str
);
621 ret
= process_convert(ldb
, target
, in
, out
);
626 printf("Converted %d records (skipped %d) with %d failures\n", ret
.count
, ret
.skipped
, ret
.failures
);