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