s3-build: fix the build of vfs_notify_fam.
[kai/samba-autobuild/.git] / source3 / lib / ldb / tools / ad2oLschema.c
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)
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,
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, 
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, 
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) 
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) 
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 }