• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source3/lib/ldb/tools/
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
40struct schema_conv {
41	int count;
42	int skipped;
43	int failures;
44};
45
46enum convert_target {
47	TARGET_OPENLDAP,
48	TARGET_FEDORA_DS
49};
50
51
52static void usage(void)
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
67static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
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
97static 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
110static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn,
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
152static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
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
193static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
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
236static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out)
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)
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}
625