s4:provision - Removed dependency on full Samba 3 schema from FDS
[ira/wip.git] / source4 / utils / oLschema2ldif.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce 2005
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: oLschema2ldif
28  *
29  *  Description: utility to convert an OpenLDAP schema into AD LDIF
30  *
31  *  Author: Simo Sorce
32  */
33
34 #include "includes.h"
35 #include "ldb.h"
36 #include "tools/cmdline.h"
37 #include "dsdb/samdb/samdb.h"
38 #include "../lib/crypto/sha256.h"
39 #include "../librpc/gen_ndr/ndr_misc.h"
40
41 #define SCHEMA_UNKNOWN 0
42 #define SCHEMA_NAME 1
43 #define SCHEMA_SUP 2
44 #define SCHEMA_STRUCTURAL 3
45 #define SCHEMA_ABSTRACT 4
46 #define SCHEMA_AUXILIARY 5
47 #define SCHEMA_MUST 6
48 #define SCHEMA_MAY 7
49 #define SCHEMA_SINGLE_VALUE 8
50 #define SCHEMA_EQUALITY 9
51 #define SCHEMA_ORDERING 10
52 #define SCHEMA_SUBSTR 11
53 #define SCHEMA_SYNTAX 12
54 #define SCHEMA_DESC 13
55
56 struct schema_conv {
57         int count;
58         int failures;
59 };
60
61 struct schema_token {
62         int type;
63         char *value;
64 };
65
66 struct ldb_context *ldb_ctx;
67 struct ldb_dn *basedn;
68
69 static int check_braces(const char *string)
70 {
71         int b;
72         char *c;
73
74         b = 0;
75         if ((c = strchr(string, '(')) == NULL) {
76                 return -1;
77         }
78         b++;
79         c++;
80         while (b) {
81                 c = strpbrk(c, "()");
82                 if (c == NULL) return 1;
83                 if (*c == '(') b++;
84                 if (*c == ')') b--;
85                 c++;
86         }
87         return 0;
88 }
89
90 static char *skip_spaces(char *string) {
91         return (string + strspn(string, " \t\n"));
92 }
93
94 static int add_multi_string(struct ldb_message *msg, const char *attr, char *values)
95 {
96         char *c;
97         char *s;
98         int n;
99
100         c = skip_spaces(values);
101         while (*c) {
102                 n = strcspn(c, " \t$");
103                 s = talloc_strndup(msg, c, n);
104                 if (ldb_msg_add_string(msg, attr, s) != 0) {
105                         return -1;
106                 }
107                 c += n;
108                 c += strspn(c, " \t$");
109         }
110
111         return 0;
112 }
113
114 #define MSG_ADD_STRING(a, v) do { if (ldb_msg_add_string(msg, a, v) != 0) goto failed; } while(0)
115 #define MSG_ADD_M_STRING(a, v) do { if (add_multi_string(msg, a, v) != 0) goto failed; } while(0)
116
117 static char *get_def_value(TALLOC_CTX *ctx, char **string)
118 {
119         char *c = *string;
120         char *value;
121         int n;
122
123         if (*c == '\'') {
124                 c++;
125                 n = strcspn(c, "\'");
126                 value = talloc_strndup(ctx, c, n);
127                 c += n;
128                 c++; /* skip closing \' */
129         } else {
130                 n = strcspn(c, " \t\n");
131                 value = talloc_strndup(ctx, c, n);
132                 c += n;
133         }
134         *string = c;
135
136         return value;
137 }
138
139 static struct schema_token *get_next_schema_token(TALLOC_CTX *ctx, char **string)
140 {
141         char *c = skip_spaces(*string);
142         char *type;
143         struct schema_token *token;
144         int n;
145
146         token = talloc(ctx, struct schema_token);
147
148         n = strcspn(c, " \t\n");
149         type = talloc_strndup(token, c, n);
150         c += n;
151         c = skip_spaces(c);
152
153         if (strcasecmp("NAME", type) == 0) {
154                 talloc_free(type);
155                 token->type = SCHEMA_NAME;
156                 /* we do not support aliases so we get only the first name given and skip others */
157                 if (*c == '(') {
158                         char *s = strchr(c, ')');
159                         if (s == NULL) return NULL;
160                         s = skip_spaces(s);
161                         *string = s;
162
163                         c++;
164                         c = skip_spaces(c);
165                 }
166
167                 token->value = get_def_value(ctx, &c);
168
169                 if (*string < c) { /* single name */
170                         c = skip_spaces(c);
171                         *string = c;
172                 }
173                 return token;
174         }
175         if (strcasecmp("SUP", type) == 0) {
176                 talloc_free(type);
177                 token->type = SCHEMA_SUP;
178
179                 if (*c == '(') {
180                         c++;
181                         n = strcspn(c, ")");
182                         token->value = talloc_strndup(ctx, c, n);
183                         c += n;
184                         c++;
185                 } else {
186                         token->value = get_def_value(ctx, &c);
187                 }
188
189                 c = skip_spaces(c);
190                 *string = c;
191                 return token;
192         }
193
194         if (strcasecmp("STRUCTURAL", type) == 0) {
195                 talloc_free(type);
196                 token->type = SCHEMA_STRUCTURAL;
197                 *string = c;
198                 return token;
199         }
200
201         if (strcasecmp("ABSTRACT", type) == 0) {
202                 talloc_free(type);
203                 token->type = SCHEMA_ABSTRACT;
204                 *string = c;
205                 return token;
206         }
207
208         if (strcasecmp("AUXILIARY", type) == 0) {
209                 talloc_free(type);
210                 token->type = SCHEMA_AUXILIARY;
211                 *string = c;
212                 return token;
213         }
214
215         if (strcasecmp("MUST", type) == 0) {
216                 talloc_free(type);
217                 token->type = SCHEMA_MUST;
218
219                 if (*c == '(') {
220                         c++;
221                         n = strcspn(c, ")");
222                         token->value = talloc_strndup(ctx, c, n);
223                         c += n;
224                         c++;
225                 } else {
226                         token->value = get_def_value(ctx, &c);
227                 }
228
229                 c = skip_spaces(c);
230                 *string = c;
231                 return token;
232         }
233
234         if (strcasecmp("MAY", type) == 0) {
235                 talloc_free(type);
236                 token->type = SCHEMA_MAY;
237
238                 if (*c == '(') {
239                         c++;
240                         n = strcspn(c, ")");
241                         token->value = talloc_strndup(ctx, c, n);
242                         c += n;
243                         c++;
244                 } else {
245                         token->value = get_def_value(ctx, &c);
246                 }
247
248                 c = skip_spaces(c);
249                 *string = c;
250                 return token;
251         }
252
253         if (strcasecmp("SINGLE-VALUE", type) == 0) {
254                 talloc_free(type);
255                 token->type = SCHEMA_SINGLE_VALUE;
256                 *string = c;
257                 return token;
258         }
259
260         if (strcasecmp("EQUALITY", type) == 0) {
261                 talloc_free(type);
262                 token->type = SCHEMA_EQUALITY;
263
264                 token->value = get_def_value(ctx, &c);
265
266                 c = skip_spaces(c);
267                 *string = c;
268                 return token;
269         }
270
271         if (strcasecmp("ORDERING", type) == 0) {
272                 talloc_free(type);
273                 token->type = SCHEMA_ORDERING;
274
275                 token->value = get_def_value(ctx, &c);
276
277                 c = skip_spaces(c);
278                 *string = c;
279                 return token;
280         }
281
282         if (strcasecmp("SUBSTR", type) == 0) {
283                 talloc_free(type);
284                 token->type = SCHEMA_SUBSTR;
285
286                 token->value = get_def_value(ctx, &c);
287
288                 c = skip_spaces(c);
289                 *string = c;
290                 return token;
291         }
292
293         if (strcasecmp("SYNTAX", type) == 0) {
294                 talloc_free(type);
295                 token->type = SCHEMA_SYNTAX;
296
297                 token->value = get_def_value(ctx, &c);
298
299                 c = skip_spaces(c);
300                 *string = c;
301                 return token;
302         }
303
304         if (strcasecmp("DESC", type) == 0) {
305                 talloc_free(type);
306                 token->type = SCHEMA_DESC;
307
308                 token->value = get_def_value(ctx, &c);
309
310                 c = skip_spaces(c);
311                 *string = c;
312                 return token;
313         }
314
315         token->type = SCHEMA_UNKNOWN;
316         token->value = type;
317         if (*c == ')') {
318                 *string = c;
319                 return token;
320         }
321         if (*c == '\'') {
322                 c = strchr(++c, '\'');
323                 c++;
324         } else {
325                 c += strcspn(c, " \t\n");
326         }
327         c = skip_spaces(c);
328         *string = c;
329
330         return token;
331 }
332
333 static struct ldb_message *process_entry(TALLOC_CTX *mem_ctx, const char *entry)
334 {
335         TALLOC_CTX *ctx;
336         struct ldb_message *msg;
337         struct schema_token *token;
338         char *c, *s;
339         int n;
340
341         SHA256_CTX sha256_context;
342         uint8_t digest[SHA256_DIGEST_LENGTH];
343
344         struct GUID guid;
345         struct ldb_val schemaIdGuid;
346         enum ndr_err_code ndr_err;
347
348         bool isAttribute = false;
349         bool single_valued = false;
350
351         ctx = talloc_new(mem_ctx);
352         msg = ldb_msg_new(ctx);
353
354         ldb_msg_add_string(msg, "objectClass", "top");
355
356         c = talloc_strdup(ctx, entry);
357         if (!c) return NULL;
358
359         c = skip_spaces(c);
360
361         switch (*c) {
362         case 'a':
363                 if (strncmp(c, "attributetype", 13) == 0) {
364                         c += 13;
365                         MSG_ADD_STRING("objectClass", "attributeSchema");
366                         isAttribute = true;
367                         break;
368                 }
369                 goto failed;
370         case 'o':
371                 if (strncmp(c, "objectclass", 11) == 0) {
372                         c += 11;
373                         MSG_ADD_STRING("objectClass", "classSchema");
374                         break;
375                 }
376                 goto failed;
377         default:
378                 goto failed;
379         }
380
381         c = strchr(c, '(');
382         if (c == NULL) goto failed;
383         c++;
384
385         c = skip_spaces(c);
386
387         /* get attributeID */
388         n = strcspn(c, " \t");
389         s = talloc_strndup(msg, c, n);
390         if (isAttribute) {
391                 MSG_ADD_STRING("attributeID", s);
392         } else {
393                 MSG_ADD_STRING("governsID", s);
394         }
395
396         SHA256_Init(&sha256_context);
397         SHA256_Update(&sha256_context, (uint8_t*)s, strlen(s));
398         SHA256_Final(digest, &sha256_context);
399
400         memcpy(&guid, digest, sizeof(struct GUID));
401
402         ndr_err = ndr_push_struct_blob(&schemaIdGuid, ctx, NULL, &guid,
403                         (ndr_push_flags_fn_t)ndr_push_GUID);
404
405         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
406                 goto failed;
407         }
408
409         if (ldb_msg_add_value(msg, "schemaIdGuid", &schemaIdGuid, NULL) != 0) {
410                 goto failed;
411         }
412
413         c += n;
414         c = skip_spaces(c);     
415
416         while (*c != ')') {
417                 token = get_next_schema_token(msg, &c);
418                 if (!token) goto failed;
419
420                 switch (token->type) {
421                 case SCHEMA_NAME:
422                         MSG_ADD_STRING("cn", token->value);
423                         MSG_ADD_STRING("name", token->value);
424                         MSG_ADD_STRING("lDAPDisplayName", token->value);
425                         msg->dn = ldb_dn_copy(msg, basedn);
426                         ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Schema,CN=Configuration", token->value);
427                         break;
428
429                 case SCHEMA_SUP:
430                         MSG_ADD_M_STRING("subClassOf", token->value);
431                         break;
432
433                 case SCHEMA_STRUCTURAL:
434                         MSG_ADD_STRING("objectClassCategory", "1");
435                         break;
436
437                 case SCHEMA_ABSTRACT:
438                         MSG_ADD_STRING("objectClassCategory", "2");
439                         break;
440
441                 case SCHEMA_AUXILIARY:
442                         MSG_ADD_STRING("objectClassCategory", "3");
443                         break;
444
445                 case SCHEMA_MUST:
446                         MSG_ADD_M_STRING("mustContain", token->value);
447                         break;
448
449                 case SCHEMA_MAY:
450                         MSG_ADD_M_STRING("mayContain", token->value);
451                         break;
452
453                 case SCHEMA_SINGLE_VALUE:
454                         single_valued = true;
455                         break;
456
457                 case SCHEMA_EQUALITY:
458                         /* TODO */
459                         break;
460
461                 case SCHEMA_ORDERING:
462                         /* TODO */
463                         break;
464
465                 case SCHEMA_SUBSTR:
466                         /* TODO */
467                         break;
468
469                 case SCHEMA_SYNTAX:
470                 {
471                         char *syntax_oid;
472                         const struct dsdb_syntax *map;
473                         char *oMSyntax;
474
475                         n = strcspn(token->value, "{");
476                         syntax_oid = talloc_strndup(ctx, token->value, n);
477
478                         map = find_syntax_map_by_standard_oid(syntax_oid);
479                         if (!map) {
480                                 break;
481                         }
482
483                         MSG_ADD_STRING("attributeSyntax", map->attributeSyntax_oid);
484
485                         oMSyntax = talloc_asprintf(msg, "%d", map->oMSyntax);
486                         MSG_ADD_STRING("oMSyntax", oMSyntax);
487
488                         break;
489                 }
490                 case SCHEMA_DESC:
491                         MSG_ADD_STRING("description", token->value);
492                         break;
493
494                 default:
495                         fprintf(stderr, "Unknown Definition: %s\n", token->value);
496                 }
497         }
498
499         if (isAttribute) {
500                 MSG_ADD_STRING("isSingleValued", single_valued ? "TRUE" : "FALSE");
501         } else {
502                 MSG_ADD_STRING("defaultObjectCategory", ldb_dn_get_linearized(msg->dn));
503         }
504
505         talloc_steal(mem_ctx, msg);
506         talloc_free(ctx);
507         return msg;
508
509 failed:
510         talloc_free(ctx);
511         return NULL;
512 }
513
514 static struct schema_conv process_file(FILE *in, FILE *out)
515 {
516         TALLOC_CTX *ctx;
517         struct schema_conv ret;
518         char *entry;
519         int c, t, line;
520         struct ldb_ldif ldif;
521
522         ldif.changetype = LDB_CHANGETYPE_NONE;
523
524         ctx = talloc_new(NULL);
525
526         ret.count = 0;
527         ret.failures = 0;
528         line = 0;
529
530         while ((c = fgetc(in)) != EOF) {
531                 line++;
532                 /* fprintf(stderr, "Parsing line %d\n", line); */
533                 if (c == '#') {
534                         do {
535                                 c = fgetc(in);
536                         } while (c != EOF && c != '\n');
537                         continue;
538                 }
539                 if (c == '\n') {
540                         continue;
541                 }
542
543                 t = 0;
544                 entry = talloc_array(ctx, char, 1024);
545                 if (entry == NULL) exit(-1);
546
547                 do { 
548                         if (c == '\n') {
549                                 entry[t] = '\0';        
550                                 if (check_braces(entry) == 0) {
551                                         ret.count++;
552                                         ldif.msg = process_entry(ctx, entry);
553                                         if (ldif.msg == NULL) {
554                                                 ret.failures++;
555                                                 fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
556                                                 break;
557                                         }
558                                         ldb_ldif_write_file(ldb_ctx, out, &ldif);
559                                         break;
560                                 }
561                                 line++;
562                         } else {
563                                 entry[t] = c;
564                                 t++;
565                         }
566                         if ((t % 1023) == 0) {
567                                 entry = talloc_realloc(ctx, entry, char, t + 1024);
568                                 if (entry == NULL) exit(-1);
569                         }
570                 } while ((c = fgetc(in)) != EOF); 
571
572                 if (c != '\n') {
573                         entry[t] = '\0';
574                         if (check_braces(entry) == 0) {
575                                 ret.count++;
576                                 ldif.msg = process_entry(ctx, entry);
577                                 if (ldif.msg == NULL) {
578                                         ret.failures++;
579                                         fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
580                                         break;
581                                 }
582                                 ldb_ldif_write_file(ldb_ctx, out, &ldif);
583                         } else {
584                                 fprintf(stderr, "malformed entry on line %d\n", line);
585                                 ret.failures++;
586                         }
587                 }
588         
589                 if (c == EOF) break;
590         }
591
592         return ret;
593 }
594
595 static void usage(void)
596 {
597         printf("Usage: oLschema2ldif -H NONE <options>\n");
598         printf("\nConvert OpenLDAP schema to AD-like LDIF format\n\n");
599         printf("Options:\n");
600         printf("  -I inputfile     inputfile of OpenLDAP style schema otherwise STDIN\n");
601         printf("  -O outputfile    outputfile otherwise STDOUT\n");
602         printf("  -o options       pass options like modules to activate\n");
603         printf("              e.g: -o modules:timestamps\n");
604         printf("\n");
605         printf("Converts records from an openLdap formatted schema to an ldif schema\n\n");
606         exit(1);
607 }
608
609  int main(int argc, const char **argv)
610 {
611         TALLOC_CTX *ctx;
612         struct schema_conv ret;
613         struct ldb_cmdline *options;
614         FILE *in = stdin;
615         FILE *out = stdout;
616         ctx = talloc_new(NULL);
617         ldb_ctx = ldb_init(ctx, NULL);
618
619         setenv("LDB_URL", "NONE", 1);
620         options = ldb_cmdline_process(ldb_ctx, argc, argv, usage);
621
622         if (options->basedn == NULL) {
623                 perror("Base DN not specified");
624                 exit(1);
625         } else {
626                 basedn = ldb_dn_new(ctx, ldb_ctx, options->basedn);
627                 if ( ! ldb_dn_validate(basedn)) {
628                         perror("Malformed Base DN");
629                         exit(1);
630                 }
631         }
632
633         if (options->input) {
634                 in = fopen(options->input, "r");
635                 if (!in) {
636                         perror(options->input);
637                         exit(1);
638                 }
639         }
640         if (options->output) {
641                 out = fopen(options->output, "w");
642                 if (!out) {
643                         perror(options->output);
644                         exit(1);
645                 }
646         }
647
648         ret = process_file(in, out);
649
650         fclose(in);
651         fclose(out);
652
653         printf("Converted %d records with %d failures\n", ret.count, ret.failures);
654
655         return 0;
656 }