/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- usage
- fetch_attrs_schema
- fetch_oc_recursive
- fetch_objectclass_schema
- find_schema_dn
- process_convert
- main
1 /*
2 ldb database library
3
4 Copyright (C) Andrew Bartlett 2006
5
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
8 ** under the LGPL
9
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.
14
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.
19
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/>.
22 */
23
24 /*
25 * Name: ldb
26 *
27 * Component: ad2oLschema
28 *
29 * Description: utility to convert an AD schema into the format required by OpenLDAP
30 *
31 * Author: Andrew Tridgell
32 */
33
34 #include "includes.h"
35 #include "ldb/include/includes.h"
36 #include "system/locale.h"
37 #include "ldb/tools/cmdline.h"
38 #include "ldb/tools/convert.h"
39
40 struct schema_conv {
41 int count;
42 int skipped;
43 int failures;
44 };
45
46 enum convert_target {
47 TARGET_OPENLDAP,
48 TARGET_FEDORA_DS
49 };
50
51
52 static void usage(void)
/* [<][>][^][v][top][bottom][index][help] */
53 {
54 printf("Usage: ad2oLschema <options>\n");
55 printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
56 printf("Options:\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");
62 printf("\n");
63 printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
64 exit(1);
65 }
66
67 static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
/* [<][>][^][v][top][bottom][index][help] */
68 TALLOC_CTX *mem_ctx,
69 struct ldb_result **attrs_res)
70 {
71 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
72 int ret;
73 const char *attrs[] = {
74 "lDAPDisplayName",
75 "isSingleValued",
76 "attributeID",
77 "attributeSyntax",
78 "description",
79 NULL
80 };
81
82 if (!local_ctx) {
83 return LDB_ERR_OPERATIONS_ERROR;
84 }
85
86 /* Downlaod schema */
87 ret = ldb_search(ldb, ldb, attrs_res, schemadn, LDB_SCOPE_SUBTREE,
88 attrs, "objectClass=attributeSchema");
89 if (ret != LDB_SUCCESS) {
90 printf("Search failed: %s\n", ldb_errstring(ldb));
91 return LDB_ERR_OPERATIONS_ERROR;
92 }
93
94 return ret;
95 }
96
97 static const char *oc_attrs[] = {
98 "lDAPDisplayName",
99 "mayContain",
100 "mustContain",
101 "systemMayContain",
102 "systemMustContain",
103 "objectClassCategory",
104 "governsID",
105 "description",
106 "subClassOf",
107 NULL
108 };
109
110 static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn,
/* [<][>][^][v][top][bottom][index][help] */
111 TALLOC_CTX *mem_ctx,
112 struct ldb_result *search_from,
113 struct ldb_result *res_list)
114 {
115 int i;
116 int ret = 0;
117 for (i=0; i < search_from->count; i++) {
118 struct ldb_result *res;
119 const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i],
120 "lDAPDisplayname", NULL);
121
122 ret = ldb_search(ldb, ldb, &res, schemadn, LDB_SCOPE_SUBTREE,
123 oc_attrs, "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
124 name, name);
125 if (ret != LDB_SUCCESS) {
126 printf("Search failed: %s\n", ldb_errstring(ldb));
127 return ret;
128 }
129
130 talloc_steal(mem_ctx, res);
131
132 res_list->msgs = talloc_realloc(res_list, res_list->msgs,
133 struct ldb_message *, res_list->count + 2);
134 if (!res_list->msgs) {
135 return LDB_ERR_OPERATIONS_ERROR;
136 }
137 res_list->msgs[res_list->count] = talloc_move(res_list,
138 &search_from->msgs[i]);
139 res_list->count++;
140 res_list->msgs[res_list->count] = NULL;
141
142 if (res->count > 0) {
143 ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list);
144 }
145 if (ret != LDB_SUCCESS) {
146 return ret;
147 }
148 }
149 return ret;
150 }
151
152 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
/* [<][>][^][v][top][bottom][index][help] */
153 TALLOC_CTX *mem_ctx,
154 struct ldb_result **objectclasses_res)
155 {
156 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
157 struct ldb_result *top_res, *ret_res;
158 int ret;
159 if (!local_ctx) {
160 return LDB_ERR_OPERATIONS_ERROR;
161 }
162
163 /* Downlaod 'top' */
164 ret = ldb_search(ldb, ldb, &top_res, schemadn, LDB_SCOPE_SUBTREE,
165 oc_attrs, "(&(objectClass=classSchema)(lDAPDisplayName=top))");
166 if (ret != LDB_SUCCESS) {
167 printf("Search failed: %s\n", ldb_errstring(ldb));
168 return LDB_ERR_OPERATIONS_ERROR;
169 }
170
171 talloc_steal(local_ctx, top_res);
172
173 if (top_res->count != 1) {
174 return LDB_ERR_OPERATIONS_ERROR;
175 }
176
177 ret_res = talloc_zero(local_ctx, struct ldb_result);
178 if (!ret_res) {
179 return LDB_ERR_OPERATIONS_ERROR;
180 }
181
182 ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res);
183
184 if (ret != LDB_SUCCESS) {
185 printf("Search failed: %s\n", ldb_errstring(ldb));
186 return LDB_ERR_OPERATIONS_ERROR;
187 }
188
189 *objectclasses_res = talloc_move(mem_ctx, &ret_res);
190 return ret;
191 }
192
193 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
/* [<][>][^][v][top][bottom][index][help] */
194 {
195 const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
196 struct ldb_dn *schemadn;
197 struct ldb_dn *basedn = ldb_dn_explode(mem_ctx, "");
198 struct ldb_result *rootdse_res;
199 int ldb_ret;
200 if (!basedn) {
201 return NULL;
202 }
203
204 /* Search for rootdse */
205 ldb_ret = ldb_search(ldb, ldb, &rootdse_res, basedn, LDB_SCOPE_BASE, rootdse_attrs, NULL);
206 if (ldb_ret != LDB_SUCCESS) {
207 printf("Search failed: %s\n", ldb_errstring(ldb));
208 return NULL;
209 }
210
211 talloc_steal(mem_ctx, rootdse_res);
212
213 if (rootdse_res->count != 1) {
214 printf("Failed to find rootDSE");
215 return NULL;
216 }
217
218 /* Locate schema */
219 schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
220 if (!schemadn) {
221 return NULL;
222 }
223
224 talloc_free(rootdse_res);
225 return schemadn;
226 }
227
228 #define IF_NULL_FAIL_RET(x) do { \
229 if (!x) { \
230 ret.failures++; \
231 return ret; \
232 } \
233 } while (0)
234
235
236 static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out)
/* [<][>][^][v][top][bottom][index][help] */
237 {
238 /* Read list of attributes to skip, OIDs to map */
239 TALLOC_CTX *mem_ctx = talloc_new(ldb);
240 char *line;
241 const char **attrs_skip = NULL;
242 int num_skip = 0;
243 struct oid_map {
244 char *old_oid;
245 char *new_oid;
246 } *oid_map = NULL;
247 int num_maps = 0;
248 struct ldb_result *attrs_res, *objectclasses_res;
249 struct ldb_dn *schemadn;
250 struct schema_conv ret;
251
252 int ldb_ret, i;
253
254 ret.count = 0;
255 ret.skipped = 0;
256 ret.failures = 0;
257
258 while ((line = afdgets(fileno(in), mem_ctx, 0))) {
259 /* Blank Line */
260 if (line[0] == '\0') {
261 continue;
262 }
263 /* Comment */
264 if (line[0] == '#') {
265 continue;
266 }
267 if (isdigit(line[0])) {
268 char *p = strchr(line, ':');
269 IF_NULL_FAIL_RET(p);
270 if (!p) {
271 ret.failures = 1;
272 return ret;
273 }
274 p[0] = '\0';
275 p++;
276 oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_maps + 2);
277 trim_string(line, " ", " ");
278 oid_map[num_maps].old_oid = talloc_move(oid_map, &line);
279 trim_string(p, " ", " ");
280 oid_map[num_maps].new_oid = p;
281 num_maps++;
282 oid_map[num_maps].old_oid = NULL;
283 } else {
284 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
285 trim_string(line, " ", " ");
286 attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
287 num_skip++;
288 attrs_skip[num_skip] = NULL;
289 }
290 }
291
292 schemadn = find_schema_dn(ldb, mem_ctx);
293 if (!schemadn) {
294 printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
295 ret.failures = 1;
296 return ret;
297 }
298
299 ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res);
300 if (ldb_ret != LDB_SUCCESS) {
301 printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb));
302 ret.failures = 1;
303 return ret;
304 }
305
306 switch (target) {
307 case TARGET_OPENLDAP:
308 break;
309 case TARGET_FEDORA_DS:
310 fprintf(out, "dn: cn=schema\n");
311 break;
312 }
313
314 for (i=0; i < attrs_res->count; i++) {
315 struct ldb_message *msg = attrs_res->msgs[i];
316
317 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
318 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
319 const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL);
320 const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL);
321 BOOL single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", False);
322 const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax);
323 char *schema_entry = NULL;
324 int j;
325
326 /* We have been asked to skip some attributes/objectClasses */
327 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
328 ret.skipped++;
329 continue;
330 }
331
332 /* We might have been asked to remap this oid, due to a conflict */
333 for (j=0; oid && oid_map[j].old_oid; j++) {
334 if (strcmp(oid, oid_map[j].old_oid) == 0) {
335 oid = oid_map[j].new_oid;
336 break;
337 }
338 }
339
340 switch (target) {
341 case TARGET_OPENLDAP:
342 schema_entry = talloc_asprintf(mem_ctx,
343 "attributetype (\n"
344 " %s\n", oid);
345 break;
346 case TARGET_FEDORA_DS:
347 schema_entry = talloc_asprintf(mem_ctx,
348 "attributeTypes: (\n"
349 " %s\n", oid);
350 break;
351 }
352 IF_NULL_FAIL_RET(schema_entry);
353
354 schema_entry = talloc_asprintf_append(schema_entry,
355 " NAME '%s'\n", name);
356 IF_NULL_FAIL_RET(schema_entry);
357
358 if (description) {
359 schema_entry = talloc_asprintf_append(schema_entry,
360 " DESC %s\n", description);
361 IF_NULL_FAIL_RET(schema_entry);
362 }
363
364 if (map) {
365 const char *syntax_oid;
366 if (map->equality) {
367 schema_entry = talloc_asprintf_append(schema_entry,
368 " EQUALITY %s\n", map->equality);
369 IF_NULL_FAIL_RET(schema_entry);
370 }
371 if (map->substring) {
372 schema_entry = talloc_asprintf_append(schema_entry,
373 " SUBSTR %s\n", map->substring);
374 IF_NULL_FAIL_RET(schema_entry);
375 }
376 syntax_oid = map->Standard_OID;
377 /* We might have been asked to remap this oid,
378 * due to a conflict, or lack of
379 * implementation */
380 for (j=0; syntax_oid && oid_map[j].old_oid; j++) {
381 if (strcmp(syntax_oid, oid_map[j].old_oid) == 0) {
382 syntax_oid = oid_map[j].new_oid;
383 break;
384 }
385 }
386 schema_entry = talloc_asprintf_append(schema_entry,
387 " SYNTAX %s\n", syntax_oid);
388 IF_NULL_FAIL_RET(schema_entry);
389 }
390
391 if (single_value) {
392 schema_entry = talloc_asprintf_append(schema_entry,
393 " SINGLE-VALUE\n");
394 IF_NULL_FAIL_RET(schema_entry);
395 }
396
397 schema_entry = talloc_asprintf_append(schema_entry,
398 " )");
399
400 switch (target) {
401 case TARGET_OPENLDAP:
402 fprintf(out, "%s\n\n", schema_entry);
403 break;
404 case TARGET_FEDORA_DS:
405 fprintf(out, "%s\n", schema_entry);
406 break;
407 }
408 ret.count++;
409 }
410
411 ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res);
412 if (ldb_ret != LDB_SUCCESS) {
413 printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
414 ret.failures = 1;
415 return ret;
416 }
417
418 for (i=0; i < objectclasses_res->count; i++) {
419 struct ldb_message *msg = objectclasses_res->msgs[i];
420 const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
421 const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
422 const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL);
423 const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL);
424 int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0);
425 struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain");
426 struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain");
427 struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain");
428 struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain");
429 char *schema_entry = NULL;
430 int j;
431
432 /* We have been asked to skip some attributes/objectClasses */
433 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
434 ret.skipped++;
435 continue;
436 }
437
438 /* We might have been asked to remap this oid, due to a conflict */
439 for (j=0; oid_map[j].old_oid; j++) {
440 if (strcmp(oid, oid_map[j].old_oid) == 0) {
441 oid = oid_map[j].new_oid;
442 break;
443 }
444 }
445
446 switch (target) {
447 case TARGET_OPENLDAP:
448 schema_entry = talloc_asprintf(mem_ctx,
449 "objectclass (\n"
450 " %s\n", oid);
451 break;
452 case TARGET_FEDORA_DS:
453 schema_entry = talloc_asprintf(mem_ctx,
454 "objectClasses: (\n"
455 " %s\n", oid);
456 break;
457 }
458 IF_NULL_FAIL_RET(schema_entry);
459 if (!schema_entry) {
460 ret.failures++;
461 break;
462 }
463
464 schema_entry = talloc_asprintf_append(schema_entry,
465 " NAME '%s'\n", name);
466 IF_NULL_FAIL_RET(schema_entry);
467
468 if (!schema_entry) return ret;
469
470 if (description) {
471 schema_entry = talloc_asprintf_append(schema_entry,
472 " DESC %s\n", description);
473 IF_NULL_FAIL_RET(schema_entry);
474 }
475
476 if (subClassOf) {
477 schema_entry = talloc_asprintf_append(schema_entry,
478 " SUP %s\n", subClassOf);
479 IF_NULL_FAIL_RET(schema_entry);
480 }
481
482 switch (objectClassCategory) {
483 case 1:
484 schema_entry = talloc_asprintf_append(schema_entry,
485 " STRUCTURAL\n");
486 IF_NULL_FAIL_RET(schema_entry);
487 break;
488 case 2:
489 schema_entry = talloc_asprintf_append(schema_entry,
490 " ABSTRACT\n");
491 IF_NULL_FAIL_RET(schema_entry);
492 break;
493 case 3:
494 schema_entry = talloc_asprintf_append(schema_entry,
495 " AUXILIARY\n");
496 IF_NULL_FAIL_RET(schema_entry);
497 break;
498 }
499
500 #define APPEND_ATTRS(attributes) \
501 do { \
502 int k; \
503 for (k=0; attributes && k < attributes->num_values; k++) { \
504 schema_entry = talloc_asprintf_append(schema_entry, \
505 " %s", \
506 (const char *)attributes->values[k].data); \
507 IF_NULL_FAIL_RET(schema_entry); \
508 if (k != (attributes->num_values - 1)) { \
509 schema_entry = talloc_asprintf_append(schema_entry, \
510 " $"); \
511 IF_NULL_FAIL_RET(schema_entry); \
512 if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
513 schema_entry = talloc_asprintf_append(schema_entry, \
514 "\n "); \
515 IF_NULL_FAIL_RET(schema_entry); \
516 } \
517 } \
518 } \
519 } while (0)
520
521 if (must || sys_must) {
522 schema_entry = talloc_asprintf_append(schema_entry,
523 " MUST (");
524 IF_NULL_FAIL_RET(schema_entry);
525
526 APPEND_ATTRS(must);
527 if (must && sys_must) {
528 schema_entry = talloc_asprintf_append(schema_entry, \
529 " $"); \
530 }
531 APPEND_ATTRS(sys_must);
532
533 schema_entry = talloc_asprintf_append(schema_entry,
534 " )\n");
535 IF_NULL_FAIL_RET(schema_entry);
536 }
537
538 if (may || sys_may) {
539 schema_entry = talloc_asprintf_append(schema_entry,
540 " MAY (");
541 IF_NULL_FAIL_RET(schema_entry);
542
543 APPEND_ATTRS(may);
544 if (may && sys_may) {
545 schema_entry = talloc_asprintf_append(schema_entry, \
546 " $"); \
547 }
548 APPEND_ATTRS(sys_may);
549
550 schema_entry = talloc_asprintf_append(schema_entry,
551 " )\n");
552 IF_NULL_FAIL_RET(schema_entry);
553 }
554
555 schema_entry = talloc_asprintf_append(schema_entry,
556 " )");
557
558 switch (target) {
559 case TARGET_OPENLDAP:
560 fprintf(out, "%s\n\n", schema_entry);
561 break;
562 case TARGET_FEDORA_DS:
563 fprintf(out, "%s\n", schema_entry);
564 break;
565 }
566 ret.count++;
567 }
568
569 return ret;
570 }
571
572 int main(int argc, const char **argv)
/* [<][>][^][v][top][bottom][index][help] */
573 {
574 TALLOC_CTX *ctx;
575 struct ldb_cmdline *options;
576 FILE *in = stdin;
577 FILE *out = stdout;
578 struct ldb_context *ldb;
579 struct schema_conv ret;
580 const char *target_str;
581 enum convert_target target;
582
583 ldb_global_init();
584
585 ctx = talloc_new(NULL);
586 ldb = ldb_init(ctx);
587
588 options = ldb_cmdline_process(ldb, argc, argv, usage);
589
590 if (options->input) {
591 in = fopen(options->input, "r");
592 if (!in) {
593 perror(options->input);
594 exit(1);
595 }
596 }
597 if (options->output) {
598 out = fopen(options->output, "w");
599 if (!out) {
600 perror(options->output);
601 exit(1);
602 }
603 }
604
605 target_str = lp_parm_string(-1, "convert", "target");
606
607 if (!target_str || strcasecmp(target_str, "openldap") == 0) {
608 target = TARGET_OPENLDAP;
609 } else if (strcasecmp(target_str, "fedora-ds") == 0) {
610 target = TARGET_FEDORA_DS;
611 } else {
612 printf("Unsupported target: %s\n", target_str);
613 exit(1);
614 }
615
616 ret = process_convert(ldb, target, in, out);
617
618 fclose(in);
619 fclose(out);
620
621 printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);
622
623 return 0;
624 }