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 2 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, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * Component: ad2oLschema
30 * Description: utility to convert an AD schema into the format required by OpenLDAP
32 * Author: Andrew Tridgell
36 #include "ldb/include/includes.h"
37 #include "system/locale.h"
38 #include "ldb/tools/cmdline.h"
39 #include "ldb/tools/convert.h"
53 static void usage(void)
55 printf("Usage: ad2oLschema <options>\n");
56 printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
58 printf(" -I inputfile inputfile of mapped OIDs and skipped attributes/ObjectClasses");
59 printf(" -H url LDB or LDAP server to read schmea from\n");
60 printf(" -O outputfile outputfile otherwise STDOUT\n");
61 printf(" -o options pass options like modules to activate\n");
62 printf(" e.g: -o modules:timestamps\n");
64 printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
68 static int fetch_attrs_schema(struct ldb_context
*ldb
, struct ldb_dn
*schemadn
,
70 struct ldb_result
**attrs_res
)
72 TALLOC_CTX
*local_ctx
= talloc_new(mem_ctx
);
74 const char *attrs
[] = {
84 return LDB_ERR_OPERATIONS_ERROR
;
88 ret
= ldb_search(ldb
, schemadn
, LDB_SCOPE_SUBTREE
,
89 "objectClass=attributeSchema",
91 if (ret
!= LDB_SUCCESS
) {
92 printf("Search failed: %s\n", ldb_errstring(ldb
));
93 return LDB_ERR_OPERATIONS_ERROR
;
99 static const char *oc_attrs
[] = {
105 "objectClassCategory",
112 static int fetch_oc_recursive(struct ldb_context
*ldb
, struct ldb_dn
*schemadn
,
114 struct ldb_result
*search_from
,
115 struct ldb_result
*res_list
)
119 for (i
=0; i
< search_from
->count
; i
++) {
120 struct ldb_result
*res
;
121 const char *name
= ldb_msg_find_attr_as_string(search_from
->msgs
[i
],
122 "lDAPDisplayname", NULL
);
123 char *filter
= talloc_asprintf(mem_ctx
, "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
126 ret
= ldb_search(ldb
, schemadn
, LDB_SCOPE_SUBTREE
,
130 if (ret
!= LDB_SUCCESS
) {
131 printf("Search failed: %s\n", ldb_errstring(ldb
));
135 talloc_steal(mem_ctx
, res
);
137 res_list
->msgs
= talloc_realloc(res_list
, res_list
->msgs
,
138 struct ldb_message
*, res_list
->count
+ 2);
139 if (!res_list
->msgs
) {
140 return LDB_ERR_OPERATIONS_ERROR
;
142 res_list
->msgs
[res_list
->count
] = talloc_move(res_list
,
143 &search_from
->msgs
[i
]);
145 res_list
->msgs
[res_list
->count
] = NULL
;
147 if (res
->count
> 0) {
148 ret
= fetch_oc_recursive(ldb
, schemadn
, mem_ctx
, res
, res_list
);
150 if (ret
!= LDB_SUCCESS
) {
157 static int fetch_objectclass_schema(struct ldb_context
*ldb
, struct ldb_dn
*schemadn
,
159 struct ldb_result
**objectclasses_res
)
161 TALLOC_CTX
*local_ctx
= talloc_new(mem_ctx
);
162 struct ldb_result
*top_res
, *ret_res
;
165 return LDB_ERR_OPERATIONS_ERROR
;
169 ret
= ldb_search(ldb
, schemadn
, LDB_SCOPE_SUBTREE
,
170 "(&(objectClass=classSchema)(lDAPDisplayName=top))",
172 if (ret
!= LDB_SUCCESS
) {
173 printf("Search failed: %s\n", ldb_errstring(ldb
));
174 return LDB_ERR_OPERATIONS_ERROR
;
177 talloc_steal(local_ctx
, top_res
);
179 if (top_res
->count
!= 1) {
180 return LDB_ERR_OPERATIONS_ERROR
;
183 ret_res
= talloc_zero(local_ctx
, struct ldb_result
);
185 return LDB_ERR_OPERATIONS_ERROR
;
188 ret
= fetch_oc_recursive(ldb
, schemadn
, local_ctx
, top_res
, ret_res
);
190 if (ret
!= LDB_SUCCESS
) {
191 printf("Search failed: %s\n", ldb_errstring(ldb
));
192 return LDB_ERR_OPERATIONS_ERROR
;
195 *objectclasses_res
= talloc_move(mem_ctx
, &ret_res
);
199 static struct ldb_dn
*find_schema_dn(struct ldb_context
*ldb
, TALLOC_CTX
*mem_ctx
)
201 const char *rootdse_attrs
[] = {"schemaNamingContext", NULL
};
202 struct ldb_dn
*schemadn
;
203 struct ldb_dn
*basedn
= ldb_dn_explode(mem_ctx
, "");
204 struct ldb_result
*rootdse_res
;
210 /* Search for rootdse */
211 ldb_ret
= ldb_search(ldb
, basedn
, LDB_SCOPE_BASE
, NULL
, rootdse_attrs
, &rootdse_res
);
212 if (ldb_ret
!= LDB_SUCCESS
) {
213 printf("Search failed: %s\n", ldb_errstring(ldb
));
217 talloc_steal(mem_ctx
, rootdse_res
);
219 if (rootdse_res
->count
!= 1) {
220 printf("Failed to find rootDSE");
225 schemadn
= ldb_msg_find_attr_as_dn(mem_ctx
, rootdse_res
->msgs
[0], "schemaNamingContext");
230 talloc_free(rootdse_res
);
234 #define IF_NULL_FAIL_RET(x) do { \
242 static struct schema_conv
process_convert(struct ldb_context
*ldb
, enum convert_target target
, FILE *in
, FILE *out
)
244 /* Read list of attributes to skip, OIDs to map */
245 TALLOC_CTX
*mem_ctx
= talloc_new(ldb
);
247 const char **attrs_skip
= NULL
;
254 struct ldb_result
*attrs_res
, *objectclasses_res
;
255 struct ldb_dn
*schemadn
;
256 struct schema_conv ret
;
264 while ((line
= afdgets(fileno(in
), mem_ctx
, 0))) {
266 if (line
[0] == '\0') {
270 if (line
[0] == '#') {
273 if (isdigit(line
[0])) {
274 char *p
= strchr(line
, ':');
282 oid_map
= talloc_realloc(mem_ctx
, oid_map
, struct oid_map
, num_maps
+ 2);
283 trim_string(line
, " ", " ");
284 oid_map
[num_maps
].old_oid
= talloc_move(oid_map
, &line
);
285 trim_string(p
, " ", " ");
286 oid_map
[num_maps
].new_oid
= p
;
288 oid_map
[num_maps
].old_oid
= NULL
;
290 attrs_skip
= talloc_realloc(mem_ctx
, attrs_skip
, const char *, num_skip
+ 2);
291 trim_string(line
, " ", " ");
292 attrs_skip
[num_skip
] = talloc_move(attrs_skip
, &line
);
294 attrs_skip
[num_skip
] = NULL
;
298 schemadn
= find_schema_dn(ldb
, mem_ctx
);
300 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb
));
305 ldb_ret
= fetch_attrs_schema(ldb
, schemadn
, mem_ctx
, &attrs_res
);
306 if (ldb_ret
!= LDB_SUCCESS
) {
307 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb
));
313 case TARGET_OPENLDAP
:
315 case TARGET_FEDORA_DS
:
316 fprintf(out
, "dn: cn=schema\n");
320 for (i
=0; i
< attrs_res
->count
; i
++) {
321 struct ldb_message
*msg
= attrs_res
->msgs
[i
];
323 const char *name
= ldb_msg_find_attr_as_string(msg
, "lDAPDisplayName", NULL
);
324 const char *description
= ldb_msg_find_attr_as_string(msg
, "description", NULL
);
325 const char *oid
= ldb_msg_find_attr_as_string(msg
, "attributeID", NULL
);
326 const char *syntax
= ldb_msg_find_attr_as_string(msg
, "attributeSyntax", NULL
);
327 BOOL single_value
= ldb_msg_find_attr_as_bool(msg
, "isSingleValued", False
);
328 const struct syntax_map
*map
= find_syntax_map_by_ad_oid(syntax
);
329 char *schema_entry
= NULL
;
332 /* We have been asked to skip some attributes/objectClasses */
333 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
338 /* We might have been asked to remap this oid, due to a conflict */
339 for (j
=0; oid
&& oid_map
[j
].old_oid
; j
++) {
340 if (strcmp(oid
, oid_map
[j
].old_oid
) == 0) {
341 oid
= oid_map
[j
].new_oid
;
347 case TARGET_OPENLDAP
:
348 schema_entry
= talloc_asprintf(mem_ctx
,
352 case TARGET_FEDORA_DS
:
353 schema_entry
= talloc_asprintf(mem_ctx
,
354 "attributeTypes: (\n"
358 IF_NULL_FAIL_RET(schema_entry
);
360 schema_entry
= talloc_asprintf_append(schema_entry
,
361 " NAME '%s'\n", name
);
362 IF_NULL_FAIL_RET(schema_entry
);
365 schema_entry
= talloc_asprintf_append(schema_entry
,
366 " DESC %s\n", description
);
367 IF_NULL_FAIL_RET(schema_entry
);
371 const char *syntax_oid
;
373 schema_entry
= talloc_asprintf_append(schema_entry
,
374 " EQUALITY %s\n", map
->equality
);
375 IF_NULL_FAIL_RET(schema_entry
);
377 if (map
->substring
) {
378 schema_entry
= talloc_asprintf_append(schema_entry
,
379 " SUBSTR %s\n", map
->substring
);
380 IF_NULL_FAIL_RET(schema_entry
);
382 syntax_oid
= map
->Standard_OID
;
383 /* We might have been asked to remap this oid,
384 * due to a conflict, or lack of
386 for (j
=0; syntax_oid
&& oid_map
[j
].old_oid
; j
++) {
387 if (strcmp(syntax_oid
, oid_map
[j
].old_oid
) == 0) {
388 syntax_oid
= oid_map
[j
].new_oid
;
392 schema_entry
= talloc_asprintf_append(schema_entry
,
393 " SYNTAX %s\n", syntax_oid
);
394 IF_NULL_FAIL_RET(schema_entry
);
398 schema_entry
= talloc_asprintf_append(schema_entry
,
400 IF_NULL_FAIL_RET(schema_entry
);
403 schema_entry
= talloc_asprintf_append(schema_entry
,
407 case TARGET_OPENLDAP
:
408 fprintf(out
, "%s\n\n", schema_entry
);
410 case TARGET_FEDORA_DS
:
411 fprintf(out
, "%s\n", schema_entry
);
417 ldb_ret
= fetch_objectclass_schema(ldb
, schemadn
, mem_ctx
, &objectclasses_res
);
418 if (ldb_ret
!= LDB_SUCCESS
) {
419 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb
));
424 for (i
=0; i
< objectclasses_res
->count
; i
++) {
425 struct ldb_message
*msg
= objectclasses_res
->msgs
[i
];
426 const char *name
= ldb_msg_find_attr_as_string(msg
, "lDAPDisplayName", NULL
);
427 const char *description
= ldb_msg_find_attr_as_string(msg
, "description", NULL
);
428 const char *oid
= ldb_msg_find_attr_as_string(msg
, "governsID", NULL
);
429 const char *subClassOf
= ldb_msg_find_attr_as_string(msg
, "subClassOf", NULL
);
430 int objectClassCategory
= ldb_msg_find_attr_as_int(msg
, "objectClassCategory", 0);
431 struct ldb_message_element
*must
= ldb_msg_find_element(msg
, "mustContain");
432 struct ldb_message_element
*sys_must
= ldb_msg_find_element(msg
, "systemMustContain");
433 struct ldb_message_element
*may
= ldb_msg_find_element(msg
, "mayContain");
434 struct ldb_message_element
*sys_may
= ldb_msg_find_element(msg
, "systemMayContain");
435 char *schema_entry
= NULL
;
438 /* We have been asked to skip some attributes/objectClasses */
439 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
444 /* We might have been asked to remap this oid, due to a conflict */
445 for (j
=0; oid_map
[j
].old_oid
; j
++) {
446 if (strcmp(oid
, oid_map
[j
].old_oid
) == 0) {
447 oid
= oid_map
[j
].new_oid
;
453 case TARGET_OPENLDAP
:
454 schema_entry
= talloc_asprintf(mem_ctx
,
458 case TARGET_FEDORA_DS
:
459 schema_entry
= talloc_asprintf(mem_ctx
,
464 IF_NULL_FAIL_RET(schema_entry
);
470 schema_entry
= talloc_asprintf_append(schema_entry
,
471 " NAME '%s'\n", name
);
472 IF_NULL_FAIL_RET(schema_entry
);
474 if (!schema_entry
) return ret
;
477 schema_entry
= talloc_asprintf_append(schema_entry
,
478 " DESC %s\n", description
);
479 IF_NULL_FAIL_RET(schema_entry
);
483 schema_entry
= talloc_asprintf_append(schema_entry
,
484 " SUP %s\n", subClassOf
);
485 IF_NULL_FAIL_RET(schema_entry
);
488 switch (objectClassCategory
) {
490 schema_entry
= talloc_asprintf_append(schema_entry
,
492 IF_NULL_FAIL_RET(schema_entry
);
495 schema_entry
= talloc_asprintf_append(schema_entry
,
497 IF_NULL_FAIL_RET(schema_entry
);
500 schema_entry
= talloc_asprintf_append(schema_entry
,
502 IF_NULL_FAIL_RET(schema_entry
);
506 #define APPEND_ATTRS(attributes) \
509 for (k=0; attributes && k < attributes->num_values; k++) { \
510 schema_entry = talloc_asprintf_append(schema_entry, \
512 (const char *)attributes->values[k].data); \
513 IF_NULL_FAIL_RET(schema_entry); \
514 if (k != (attributes->num_values - 1)) { \
515 schema_entry = talloc_asprintf_append(schema_entry, \
517 IF_NULL_FAIL_RET(schema_entry); \
518 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
519 schema_entry = talloc_asprintf_append(schema_entry, \
521 IF_NULL_FAIL_RET(schema_entry); \
527 if (must
|| sys_must
) {
528 schema_entry
= talloc_asprintf_append(schema_entry
,
530 IF_NULL_FAIL_RET(schema_entry
);
533 if (must
&& sys_must
) {
534 schema_entry
= talloc_asprintf_append(schema_entry
, \
537 APPEND_ATTRS(sys_must
);
539 schema_entry
= talloc_asprintf_append(schema_entry
,
541 IF_NULL_FAIL_RET(schema_entry
);
544 if (may
|| sys_may
) {
545 schema_entry
= talloc_asprintf_append(schema_entry
,
547 IF_NULL_FAIL_RET(schema_entry
);
550 if (may
&& sys_may
) {
551 schema_entry
= talloc_asprintf_append(schema_entry
, \
554 APPEND_ATTRS(sys_may
);
556 schema_entry
= talloc_asprintf_append(schema_entry
,
558 IF_NULL_FAIL_RET(schema_entry
);
561 schema_entry
= talloc_asprintf_append(schema_entry
,
565 case TARGET_OPENLDAP
:
566 fprintf(out
, "%s\n\n", schema_entry
);
568 case TARGET_FEDORA_DS
:
569 fprintf(out
, "%s\n", schema_entry
);
578 int main(int argc
, const char **argv
)
581 struct ldb_cmdline
*options
;
584 struct ldb_context
*ldb
;
585 struct schema_conv ret
;
586 const char *target_str
;
587 enum convert_target target
;
591 ctx
= talloc_new(NULL
);
594 options
= ldb_cmdline_process(ldb
, argc
, argv
, usage
);
596 if (options
->input
) {
597 in
= fopen(options
->input
, "r");
599 perror(options
->input
);
603 if (options
->output
) {
604 out
= fopen(options
->output
, "w");
606 perror(options
->output
);
611 target_str
= lp_parm_string(-1, "convert", "target");
613 if (!target_str
|| strcasecmp(target_str
, "openldap") == 0) {
614 target
= TARGET_OPENLDAP
;
615 } else if (strcasecmp(target_str
, "fedora-ds") == 0) {
616 target
= TARGET_FEDORA_DS
;
618 printf("Unsupported target: %s\n", target_str
);
622 ret
= process_convert(ldb
, target
, in
, out
);
627 printf("Converted %d records (skipped %d) with %d failures\n", ret
.count
, ret
.skipped
, ret
.failures
);