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