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