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
);
124 ret
= ldb_search_exp_fmt(ldb
, mem_ctx
, &res
,
125 schemadn
, LDB_SCOPE_SUBTREE
, oc_attrs
,
126 "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
128 if (ret
!= LDB_SUCCESS
) {
129 printf("Search failed: %s\n", ldb_errstring(ldb
));
133 res_list
->msgs
= talloc_realloc(res_list
, res_list
->msgs
,
134 struct ldb_message
*, res_list
->count
+ 2);
135 if (!res_list
->msgs
) {
136 return LDB_ERR_OPERATIONS_ERROR
;
138 res_list
->msgs
[res_list
->count
] = talloc_move(res_list
,
139 &search_from
->msgs
[i
]);
141 res_list
->msgs
[res_list
->count
] = NULL
;
143 if (res
->count
> 0) {
144 ret
= fetch_oc_recursive(ldb
, schemadn
, mem_ctx
, res
, res_list
);
146 if (ret
!= LDB_SUCCESS
) {
153 static int fetch_objectclass_schema(struct ldb_context
*ldb
, struct ldb_dn
*schemadn
,
155 struct ldb_result
**objectclasses_res
)
157 TALLOC_CTX
*local_ctx
= talloc_new(mem_ctx
);
158 struct ldb_result
*top_res
, *ret_res
;
161 return LDB_ERR_OPERATIONS_ERROR
;
165 ret
= ldb_search(ldb
, schemadn
, LDB_SCOPE_SUBTREE
,
166 "(&(objectClass=classSchema)(lDAPDisplayName=top))",
168 if (ret
!= LDB_SUCCESS
) {
169 printf("Search failed: %s\n", ldb_errstring(ldb
));
170 return LDB_ERR_OPERATIONS_ERROR
;
173 talloc_steal(local_ctx
, top_res
);
175 if (top_res
->count
!= 1) {
176 return LDB_ERR_OPERATIONS_ERROR
;
179 ret_res
= talloc_zero(local_ctx
, struct ldb_result
);
181 return LDB_ERR_OPERATIONS_ERROR
;
184 ret
= fetch_oc_recursive(ldb
, schemadn
, local_ctx
, top_res
, ret_res
);
186 if (ret
!= LDB_SUCCESS
) {
187 printf("Search failed: %s\n", ldb_errstring(ldb
));
188 return LDB_ERR_OPERATIONS_ERROR
;
191 *objectclasses_res
= talloc_move(mem_ctx
, &ret_res
);
195 static struct ldb_dn
*find_schema_dn(struct ldb_context
*ldb
, TALLOC_CTX
*mem_ctx
)
197 const char *rootdse_attrs
[] = {"schemaNamingContext", NULL
};
198 struct ldb_dn
*schemadn
;
199 struct ldb_dn
*basedn
= ldb_dn_new(mem_ctx
, ldb
, NULL
);
200 struct ldb_result
*rootdse_res
;
206 /* Search for rootdse */
207 ldb_ret
= ldb_search(ldb
, basedn
, LDB_SCOPE_BASE
, NULL
, rootdse_attrs
, &rootdse_res
);
208 if (ldb_ret
!= LDB_SUCCESS
) {
209 printf("Search failed: %s\n", ldb_errstring(ldb
));
213 talloc_steal(mem_ctx
, rootdse_res
);
215 if (rootdse_res
->count
!= 1) {
216 printf("Failed to find rootDSE");
221 schemadn
= ldb_msg_find_attr_as_dn(ldb
, mem_ctx
, rootdse_res
->msgs
[0], "schemaNamingContext");
226 talloc_free(rootdse_res
);
230 #define IF_NULL_FAIL_RET(x) do { \
238 static struct schema_conv
process_convert(struct ldb_context
*ldb
, enum convert_target target
, FILE *in
, FILE *out
)
240 /* Read list of attributes to skip, OIDs to map */
241 TALLOC_CTX
*mem_ctx
= talloc_new(ldb
);
243 const char **attrs_skip
= NULL
;
249 int num_oid_maps
= 0;
254 int num_attr_maps
= 0;
255 struct ldb_result
*attrs_res
, *objectclasses_res
;
256 struct ldb_dn
*schemadn
;
257 struct schema_conv ret
;
265 while ((line
= afdgets(fileno(in
), mem_ctx
, 0))) {
267 if (line
[0] == '\0') {
271 if (line
[0] == '#') {
274 if (isdigit(line
[0])) {
275 char *p
= strchr(line
, ':');
279 oid_map
= talloc_realloc(mem_ctx
, oid_map
, struct oid_map
, num_oid_maps
+ 2);
280 trim_string(line
, " ", " ");
281 oid_map
[num_oid_maps
].old_oid
= talloc_move(oid_map
, &line
);
282 trim_string(p
, " ", " ");
283 oid_map
[num_oid_maps
].new_oid
= p
;
285 oid_map
[num_oid_maps
].old_oid
= NULL
;
287 char *p
= strchr(line
, ':');
289 /* remap attribute/objectClass */
292 attr_map
= talloc_realloc(mem_ctx
, attr_map
, struct attr_map
, num_attr_maps
+ 2);
293 trim_string(line
, " ", " ");
294 attr_map
[num_attr_maps
].old_attr
= talloc_move(attr_map
, &line
);
295 trim_string(p
, " ", " ");
296 attr_map
[num_attr_maps
].new_attr
= p
;
298 attr_map
[num_attr_maps
].old_attr
= NULL
;
300 /* skip attribute/objectClass */
301 attrs_skip
= talloc_realloc(mem_ctx
, attrs_skip
, const char *, num_skip
+ 2);
302 trim_string(line
, " ", " ");
303 attrs_skip
[num_skip
] = talloc_move(attrs_skip
, &line
);
305 attrs_skip
[num_skip
] = NULL
;
310 schemadn
= find_schema_dn(ldb
, mem_ctx
);
312 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb
));
317 ldb_ret
= fetch_attrs_schema(ldb
, schemadn
, mem_ctx
, &attrs_res
);
318 if (ldb_ret
!= LDB_SUCCESS
) {
319 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb
));
325 case TARGET_OPENLDAP
:
327 case TARGET_FEDORA_DS
:
328 fprintf(out
, "dn: cn=schema\n");
332 for (i
=0; i
< attrs_res
->count
; i
++) {
333 struct ldb_message
*msg
= attrs_res
->msgs
[i
];
335 const char *name
= ldb_msg_find_attr_as_string(msg
, "lDAPDisplayName", NULL
);
336 const char *description
= ldb_msg_find_attr_as_string(msg
, "description", NULL
);
337 const char *oid
= ldb_msg_find_attr_as_string(msg
, "attributeID", NULL
);
338 const char *syntax
= ldb_msg_find_attr_as_string(msg
, "attributeSyntax", NULL
);
339 BOOL single_value
= ldb_msg_find_attr_as_bool(msg
, "isSingleValued", False
);
340 const struct syntax_map
*map
= find_syntax_map_by_ad_oid(syntax
);
341 char *schema_entry
= NULL
;
345 printf("Failed to find lDAPDisplayName for schema DN: %s\n", ldb_dn_get_linearized(msg
->dn
));
350 /* We have been asked to skip some attributes/objectClasses */
351 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
356 /* We might have been asked to remap this oid, due to a conflict */
357 for (j
=0; oid
&& oid_map
&& oid_map
[j
].old_oid
; j
++) {
358 if (strcmp(oid
, oid_map
[j
].old_oid
) == 0) {
359 oid
= oid_map
[j
].new_oid
;
365 case TARGET_OPENLDAP
:
366 schema_entry
= talloc_asprintf(mem_ctx
,
370 case TARGET_FEDORA_DS
:
371 schema_entry
= talloc_asprintf(mem_ctx
,
372 "attributeTypes: (\n"
376 IF_NULL_FAIL_RET(schema_entry
);
378 /* We might have been asked to remap this name, due to a conflict */
379 for (j
=0; name
&& attr_map
&& attr_map
[j
].old_attr
; j
++) {
380 if (strcmp(name
, attr_map
[j
].old_attr
) == 0) {
381 name
= attr_map
[j
].new_attr
;
386 schema_entry
= talloc_asprintf_append(schema_entry
,
387 " NAME '%s'\n", name
);
388 IF_NULL_FAIL_RET(schema_entry
);
391 schema_entry
= talloc_asprintf_append(schema_entry
,
392 " DESC %s\n", description
);
393 IF_NULL_FAIL_RET(schema_entry
);
397 const char *syntax_oid
;
399 schema_entry
= talloc_asprintf_append(schema_entry
,
400 " EQUALITY %s\n", map
->equality
);
401 IF_NULL_FAIL_RET(schema_entry
);
403 if (map
->substring
) {
404 schema_entry
= talloc_asprintf_append(schema_entry
,
405 " SUBSTR %s\n", map
->substring
);
406 IF_NULL_FAIL_RET(schema_entry
);
408 syntax_oid
= map
->Standard_OID
;
409 /* We might have been asked to remap this oid,
410 * due to a conflict, or lack of
412 for (j
=0; syntax_oid
&& oid_map
[j
].old_oid
; j
++) {
413 if (strcmp(syntax_oid
, oid_map
[j
].old_oid
) == 0) {
414 syntax_oid
= oid_map
[j
].new_oid
;
418 schema_entry
= talloc_asprintf_append(schema_entry
,
419 " SYNTAX %s\n", syntax_oid
);
420 IF_NULL_FAIL_RET(schema_entry
);
424 schema_entry
= talloc_asprintf_append(schema_entry
,
426 IF_NULL_FAIL_RET(schema_entry
);
429 schema_entry
= talloc_asprintf_append(schema_entry
,
433 case TARGET_OPENLDAP
:
434 fprintf(out
, "%s\n\n", schema_entry
);
436 case TARGET_FEDORA_DS
:
437 fprintf(out
, "%s\n", schema_entry
);
443 ldb_ret
= fetch_objectclass_schema(ldb
, schemadn
, mem_ctx
, &objectclasses_res
);
444 if (ldb_ret
!= LDB_SUCCESS
) {
445 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb
));
450 for (i
=0; i
< objectclasses_res
->count
; i
++) {
451 struct ldb_message
*msg
= objectclasses_res
->msgs
[i
];
452 const char *name
= ldb_msg_find_attr_as_string(msg
, "lDAPDisplayName", NULL
);
453 const char *description
= ldb_msg_find_attr_as_string(msg
, "description", NULL
);
454 const char *oid
= ldb_msg_find_attr_as_string(msg
, "governsID", NULL
);
455 const char *subClassOf
= ldb_msg_find_attr_as_string(msg
, "subClassOf", NULL
);
456 int objectClassCategory
= ldb_msg_find_attr_as_int(msg
, "objectClassCategory", 0);
457 struct ldb_message_element
*must
= ldb_msg_find_element(msg
, "mustContain");
458 struct ldb_message_element
*sys_must
= ldb_msg_find_element(msg
, "systemMustContain");
459 struct ldb_message_element
*may
= ldb_msg_find_element(msg
, "mayContain");
460 struct ldb_message_element
*sys_may
= ldb_msg_find_element(msg
, "systemMayContain");
461 char *schema_entry
= NULL
;
465 printf("Failed to find lDAPDisplayName for schema DN: %s\n", ldb_dn_get_linearized(msg
->dn
));
470 /* We have been asked to skip some attributes/objectClasses */
471 if (attrs_skip
&& str_list_check_ci(attrs_skip
, name
)) {
476 /* We might have been asked to remap this oid, due to a conflict */
477 for (j
=0; oid_map
[j
].old_oid
; j
++) {
478 if (strcmp(oid
, oid_map
[j
].old_oid
) == 0) {
479 oid
= oid_map
[j
].new_oid
;
485 case TARGET_OPENLDAP
:
486 schema_entry
= talloc_asprintf(mem_ctx
,
490 case TARGET_FEDORA_DS
:
491 schema_entry
= talloc_asprintf(mem_ctx
,
496 IF_NULL_FAIL_RET(schema_entry
);
502 /* We might have been asked to remap this name, due to a conflict */
503 for (j
=0; name
&& attr_map
&& attr_map
[j
].old_attr
; j
++) {
504 if (strcmp(name
, attr_map
[j
].old_attr
) == 0) {
505 name
= attr_map
[j
].new_attr
;
510 schema_entry
= talloc_asprintf_append(schema_entry
,
511 " NAME '%s'\n", name
);
512 IF_NULL_FAIL_RET(schema_entry
);
514 if (!schema_entry
) return ret
;
517 schema_entry
= talloc_asprintf_append(schema_entry
,
518 " DESC %s\n", description
);
519 IF_NULL_FAIL_RET(schema_entry
);
523 schema_entry
= talloc_asprintf_append(schema_entry
,
524 " SUP %s\n", subClassOf
);
525 IF_NULL_FAIL_RET(schema_entry
);
528 switch (objectClassCategory
) {
530 schema_entry
= talloc_asprintf_append(schema_entry
,
532 IF_NULL_FAIL_RET(schema_entry
);
535 schema_entry
= talloc_asprintf_append(schema_entry
,
537 IF_NULL_FAIL_RET(schema_entry
);
540 schema_entry
= talloc_asprintf_append(schema_entry
,
542 IF_NULL_FAIL_RET(schema_entry
);
546 #define APPEND_ATTRS(attributes) \
549 for (k=0; attributes && k < attributes->num_values; k++) { \
551 const char *attr_name = (const char *)attributes->values[k].data; \
552 /* We might have been asked to remap this name, due to a conflict */ \
553 for (attr_idx=0; attr_name && attr_map && attr_map[attr_idx].old_attr; attr_idx++) { \
554 if (strcmp(attr_name, attr_map[attr_idx].old_attr) == 0) { \
555 attr_name = attr_map[attr_idx].new_attr; \
560 schema_entry = talloc_asprintf_append(schema_entry, \
563 IF_NULL_FAIL_RET(schema_entry); \
564 if (k != (attributes->num_values - 1)) { \
565 schema_entry = talloc_asprintf_append(schema_entry, \
567 IF_NULL_FAIL_RET(schema_entry); \
568 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
569 schema_entry = talloc_asprintf_append(schema_entry, \
571 IF_NULL_FAIL_RET(schema_entry); \
577 if (must
|| sys_must
) {
578 schema_entry
= talloc_asprintf_append(schema_entry
,
580 IF_NULL_FAIL_RET(schema_entry
);
583 if (must
&& sys_must
) {
584 schema_entry
= talloc_asprintf_append(schema_entry
, \
587 APPEND_ATTRS(sys_must
);
589 schema_entry
= talloc_asprintf_append(schema_entry
,
591 IF_NULL_FAIL_RET(schema_entry
);
594 if (may
|| sys_may
) {
595 schema_entry
= talloc_asprintf_append(schema_entry
,
597 IF_NULL_FAIL_RET(schema_entry
);
600 if (may
&& sys_may
) {
601 schema_entry
= talloc_asprintf_append(schema_entry
, \
604 APPEND_ATTRS(sys_may
);
606 schema_entry
= talloc_asprintf_append(schema_entry
,
608 IF_NULL_FAIL_RET(schema_entry
);
611 schema_entry
= talloc_asprintf_append(schema_entry
,
615 case TARGET_OPENLDAP
:
616 fprintf(out
, "%s\n\n", schema_entry
);
618 case TARGET_FEDORA_DS
:
619 fprintf(out
, "%s\n", schema_entry
);
628 int main(int argc
, const char **argv
)
631 struct ldb_cmdline
*options
;
634 struct ldb_context
*ldb
;
635 struct schema_conv ret
;
636 const char *target_str
;
637 enum convert_target target
;
641 ctx
= talloc_new(NULL
);
644 options
= ldb_cmdline_process(ldb
, argc
, argv
, usage
);
646 if (options
->input
) {
647 in
= fopen(options
->input
, "r");
649 perror(options
->input
);
653 if (options
->output
) {
654 out
= fopen(options
->output
, "w");
656 perror(options
->output
);
661 target_str
= lp_parm_string(-1, "convert", "target");
663 if (!target_str
|| strcasecmp(target_str
, "openldap") == 0) {
664 target
= TARGET_OPENLDAP
;
665 } else if (strcasecmp(target_str
, "fedora-ds") == 0) {
666 target
= TARGET_FEDORA_DS
;
668 printf("Unsupported target: %s\n", target_str
);
672 ret
= process_convert(ldb
, target
, in
, out
);
677 printf("Converted %d records (skipped %d) with %d failures\n", ret
.count
, ret
.skipped
, ret
.failures
);