1 /* 2 * Copyright (c) 1999 - 2001, PADL Software Pty Ltd. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * 3. Neither the name of PADL Software nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include "hdb_locl.h" 34 35 RCSID("$Id: hdb-ldap.c,v 1.8 2001/03/26 00:59:37 assar Exp $"); 36 37 #ifdef OPENLDAP 38 39 #include <lber.h> 40 #include <ldap.h> 41 #include <ctype.h> 42 #include <sys/un.h> 43 44 static krb5_error_code LDAP__connect(krb5_context context, HDB * db); 45 46 static krb5_error_code 47 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, 48 hdb_entry * ent); 49 50 static char *krb5kdcentry_attrs[] = 51 { "krb5PrincipalName", "cn", "krb5PrincipalRealm", 52 "krb5KeyVersionNumber", "krb5Key", 53 "krb5ValidStart", "krb5ValidEnd", "krb5PasswordEnd", 54 "krb5MaxLife", "krb5MaxRenew", "krb5KDCFlags", "krb5EncryptionType", 55 "modifiersName", "modifyTimestamp", "creatorsName", "createTimestamp", 56 NULL 57 }; 58 59 static char *krb5principal_attrs[] = 60 { "krb5PrincipalName", "cn", "krb5PrincipalRealm", 61 "modifiersName", "modifyTimestamp", "creatorsName", "createTimestamp", 62 NULL 63 }; 64 65 /* based on samba: source/passdb/ldap.c */ 66 static krb5_error_code 67 LDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute, 68 unsigned char *value, size_t len) 69 { 70 LDAPMod **mods = *modlist; 71 int i, j; 72 73 if (mods == NULL) { 74 mods = (LDAPMod **) calloc(1, sizeof(LDAPMod *)); 75 if (mods == NULL) { 76 return ENOMEM; 77 } 78 mods[0] = NULL; 79 } 80 81 for (i = 0; mods[i] != NULL; ++i) { 82 if ((mods[i]->mod_op & (~LDAP_MOD_BVALUES)) == modop 83 && (!strcasecmp(mods[i]->mod_type, attribute))) { 84 break; 85 } 86 } 87 88 if (mods[i] == NULL) { 89 mods = (LDAPMod **) realloc(mods, (i + 2) * sizeof(LDAPMod *)); 90 if (mods == NULL) { 91 return ENOMEM; 92 } 93 mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod)); 94 if (mods[i] == NULL) { 95 return ENOMEM; 96 } 97 mods[i]->mod_op = modop | LDAP_MOD_BVALUES; 98 mods[i]->mod_bvalues = NULL; 99 mods[i]->mod_type = strdup(attribute); 100 if (mods[i]->mod_type == NULL) { 101 return ENOMEM; 102 } 103 mods[i + 1] = NULL; 104 } 105 106 if (value != NULL) { 107 j = 0; 108 if (mods[i]->mod_bvalues != NULL) { 109 for (; mods[i]->mod_bvalues[j] != NULL; j++); 110 } 111 mods[i]->mod_bvalues = 112 (struct berval **) realloc(mods[i]->mod_bvalues, 113 (j + 2) * sizeof(struct berval *)); 114 if (mods[i]->mod_bvalues == NULL) { 115 return ENOMEM; 116 } 117 /* Caller allocates memory on our behalf, unlike LDAP_addmod. */ 118 mods[i]->mod_bvalues[j] = 119 (struct berval *) malloc(sizeof(struct berval)); 120 if (mods[i]->mod_bvalues[j] == NULL) { 121 return ENOMEM; 122 } 123 mods[i]->mod_bvalues[j]->bv_val = value; 124 mods[i]->mod_bvalues[j]->bv_len = len; 125 mods[i]->mod_bvalues[j + 1] = NULL; 126 } 127 *modlist = mods; 128 return 0; 129 } 130 131 static krb5_error_code 132 LDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute, 133 const char *value) 134 { 135 LDAPMod **mods = *modlist; 136 int i, j; 137 138 if (mods == NULL) { 139 mods = (LDAPMod **) calloc(1, sizeof(LDAPMod *)); 140 if (mods == NULL) { 141 return ENOMEM; 142 } 143 mods[0] = NULL; 144 } 145 146 for (i = 0; mods[i] != NULL; ++i) { 147 if (mods[i]->mod_op == modop 148 && (!strcasecmp(mods[i]->mod_type, attribute))) { 149 break; 150 } 151 } 152 153 if (mods[i] == NULL) { 154 mods = (LDAPMod **) realloc(mods, (i + 2) * sizeof(LDAPMod *)); 155 if (mods == NULL) { 156 return ENOMEM; 157 } 158 mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod)); 159 if (mods[i] == NULL) { 160 return ENOMEM; 161 } 162 mods[i]->mod_op = modop; 163 mods[i]->mod_values = NULL; 164 mods[i]->mod_type = strdup(attribute); 165 if (mods[i]->mod_type == NULL) { 166 return ENOMEM; 167 } 168 mods[i + 1] = NULL; 169 } 170 171 if (value != NULL) { 172 j = 0; 173 if (mods[i]->mod_values != NULL) { 174 for (; mods[i]->mod_values[j] != NULL; j++); 175 } 176 mods[i]->mod_values = (char **) realloc(mods[i]->mod_values, 177 (j + 2) * sizeof(char *)); 178 if (mods[i]->mod_values == NULL) { 179 return ENOMEM; 180 } 181 mods[i]->mod_values[j] = strdup(value); 182 if (mods[i]->mod_values[j] == NULL) { 183 return ENOMEM; 184 } 185 mods[i]->mod_values[j + 1] = NULL; 186 } 187 *modlist = mods; 188 return 0; 189 } 190 191 static krb5_error_code 192 LDAP_addmod_generalized_time(LDAPMod *** mods, int modop, 193 const char *attribute, KerberosTime * time) 194 { 195 char buf[22]; 196 struct tm *tm; 197 198 /* XXX not threadsafe */ 199 tm = gmtime(time); 200 strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm); 201 202 return LDAP_addmod(mods, modop, attribute, buf); 203 } 204 205 static krb5_error_code 206 LDAP_get_string_value(HDB * db, LDAPMessage * entry, 207 const char *attribute, char **ptr) 208 { 209 char **vals; 210 int ret; 211 212 vals = ldap_get_values((LDAP *) db->db, entry, (char *) attribute); 213 if (vals == NULL) { 214 return HDB_ERR_NOENTRY; 215 } 216 *ptr = strdup(vals[0]); 217 if (*ptr == NULL) { 218 ret = ENOMEM; 219 } else { 220 ret = 0; 221 } 222 223 ldap_value_free(vals); 224 225 return ret; 226 } 227 228 static krb5_error_code 229 LDAP_get_integer_value(HDB * db, LDAPMessage * entry, 230 const char *attribute, int *ptr) 231 { 232 char **vals; 233 234 vals = ldap_get_values((LDAP *) db->db, entry, (char *) attribute); 235 if (vals == NULL) { 236 return HDB_ERR_NOENTRY; 237 } 238 *ptr = atoi(vals[0]); 239 ldap_value_free(vals); 240 return 0; 241 } 242 243 static krb5_error_code 244 LDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry, 245 const char *attribute, KerberosTime * kt) 246 { 247 char *tmp, *gentime; 248 struct tm tm; 249 int ret; 250 251 *kt = 0; 252 253 ret = LDAP_get_string_value(db, entry, attribute, &gentime); 254 if (ret != 0) { 255 return ret; 256 } 257 258 tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm); 259 if (tmp == NULL) { 260 free(gentime); 261 return HDB_ERR_NOENTRY; 262 } 263 264 free(gentime); 265 266 *kt = timegm(&tm); 267 268 return 0; 269 } 270 271 static krb5_error_code 272 LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry * ent, 273 LDAPMessage * msg, LDAPMod *** pmods) 274 { 275 krb5_error_code ret; 276 krb5_boolean is_new_entry; 277 int rc, i; 278 char *tmp = NULL; 279 LDAPMod **mods = NULL; 280 hdb_entry orig; 281 unsigned long oflags, nflags; 282 283 if (msg != NULL) { 284 ret = LDAP_message2entry(context, db, msg, &orig); 285 if (ret != 0) { 286 goto out; 287 } 288 is_new_entry = FALSE; 289 } else { 290 /* to make it perfectly obvious we're depending on 291 * orig being intiialized to zero */ 292 memset(&orig, 0, sizeof(orig)); 293 is_new_entry = TRUE; 294 } 295 296 if (is_new_entry) { 297 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top"); 298 if (ret != 0) { 299 goto out; 300 } 301 /* person is the structural object class */ 302 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "person"); 303 if (ret != 0) { 304 goto out; 305 } 306 ret = 307 LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", 308 "krb5Principal"); 309 if (ret != 0) { 310 goto out; 311 } 312 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", 313 "krb5KDCEntry"); 314 if (ret != 0) { 315 goto out; 316 } 317 } 318 319 if (is_new_entry || 320 krb5_principal_compare(context, ent->principal, orig.principal) == 321 FALSE) { 322 ret = krb5_unparse_name(context, ent->principal, &tmp); 323 if (ret != 0) { 324 goto out; 325 } 326 ret = 327 LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5PrincipalName", tmp); 328 if (ret != 0) { 329 free(tmp); 330 goto out; 331 } 332 free(tmp); 333 } 334 335 if (ent->kvno != orig.kvno) { 336 rc = asprintf(&tmp, "%d", ent->kvno); 337 if (rc < 0) { 338 ret = ENOMEM; 339 goto out; 340 } 341 ret = 342 LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5KeyVersionNumber", 343 tmp); 344 free(tmp); 345 if (ret != 0) { 346 goto out; 347 } 348 } 349 350 if (ent->valid_start) { 351 if (orig.valid_end == NULL 352 || (*(ent->valid_start) != *(orig.valid_start))) { 353 ret = 354 LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 355 "krb5ValidStart", 356 ent->valid_start); 357 if (ret != 0) { 358 goto out; 359 } 360 } 361 } 362 363 if (ent->valid_end) { 364 if (orig.valid_end == NULL 365 || (*(ent->valid_end) != *(orig.valid_end))) { 366 ret = 367 LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 368 "krb5ValidEnd", 369 ent->valid_end); 370 if (ret != 0) { 371 goto out; 372 } 373 } 374 } 375 376 if (ent->pw_end) { 377 if (orig.pw_end == NULL || (*(ent->pw_end) != *(orig.pw_end))) { 378 ret = 379 LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 380 "krb5PasswordEnd", 381 ent->pw_end); 382 if (ret != 0) { 383 goto out; 384 } 385 } 386 } 387 388 if (ent->max_life) { 389 if (orig.max_life == NULL 390 || (*(ent->max_life) != *(orig.max_life))) { 391 rc = asprintf(&tmp, "%d", *(ent->max_life)); 392 if (rc < 0) { 393 ret = ENOMEM; 394 goto out; 395 } 396 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5MaxLife", tmp); 397 free(tmp); 398 if (ret != 0) { 399 goto out; 400 } 401 } 402 } 403 404 if (ent->max_renew) { 405 if (orig.max_renew == NULL 406 || (*(ent->max_renew) != *(orig.max_renew))) { 407 rc = asprintf(&tmp, "%d", *(ent->max_renew)); 408 if (rc < 0) { 409 ret = ENOMEM; 410 goto out; 411 } 412 ret = 413 LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5MaxRenew", tmp); 414 free(tmp); 415 if (ret != 0) { 416 goto out; 417 } 418 } 419 } 420 421 memset(&oflags, 0, sizeof(oflags)); 422 memcpy(&oflags, &orig.flags, sizeof(HDBFlags)); 423 memset(&nflags, 0, sizeof(nflags)); 424 memcpy(&nflags, &ent->flags, sizeof(HDBFlags)); 425 426 if (memcmp(&oflags, &nflags, sizeof(HDBFlags))) { 427 rc = asprintf(&tmp, "%lu", nflags); 428 if (rc < 0) { 429 ret = ENOMEM; 430 goto out; 431 } 432 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5KDCFlags", tmp); 433 free(tmp); 434 if (ret != 0) { 435 goto out; 436 } 437 } 438 439 if (is_new_entry == FALSE && orig.keys.len > 0) { 440 /* for the moment, clobber and replace keys. */ 441 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL); 442 if (ret != 0) { 443 goto out; 444 } 445 } 446 447 for (i = 0; i < ent->keys.len; i++) { 448 unsigned char *buf; 449 size_t len; 450 Key new; 451 452 ret = copy_Key(&ent->keys.val[i], &new); 453 if (ret != 0) { 454 goto out; 455 } 456 457 len = length_Key(&new); 458 buf = malloc(len); 459 if (buf == NULL) { 460 ret = ENOMEM; 461 free_Key(&new); 462 goto out; 463 } 464 465 ret = encode_Key(buf + len - 1, len, &new, &len); 466 if (ret != 0) { 467 free(buf); 468 free_Key(&new); 469 goto out; 470 } 471 free_Key(&new); 472 473 /* addmod_len _owns_ the key, doesn't need to copy it */ 474 ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len); 475 if (ret != 0) { 476 goto out; 477 } 478 } 479 480 if (ent->etypes) { 481 /* clobber and replace encryption types. */ 482 if (is_new_entry == FALSE) { 483 ret = 484 LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType", 485 NULL); 486 } 487 for (i = 0; i < ent->etypes->len; i++) { 488 rc = asprintf(&tmp, "%d", ent->etypes->val[i]); 489 if (rc < 0) { 490 ret = ENOMEM; 491 goto out; 492 } 493 free(tmp); 494 ret = 495 LDAP_addmod(&mods, LDAP_MOD_ADD, "krb5EncryptionType", 496 tmp); 497 if (ret != 0) { 498 goto out; 499 } 500 } 501 } 502 503 /* for clarity */ 504 ret = 0; 505 506 out: 507 508 if (ret == 0) { 509 *pmods = mods; 510 } else if (mods != NULL) { 511 ldap_mods_free(mods, 1); 512 *pmods = NULL; 513 } 514 515 if (msg != NULL) { 516 hdb_free_entry(context, &orig); 517 } 518 519 return ret; 520 } 521 522 static krb5_error_code 523 LDAP_dn2principal(krb5_context context, HDB * db, const char *dn, 524 krb5_principal * principal) 525 { 526 krb5_error_code ret; 527 int rc; 528 char **values; 529 LDAPMessage *res = NULL, *e; 530 531 rc = 1; 532 (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, &rc); 533 rc = ldap_search_s((LDAP *) db->db, db->name, LDAP_SCOPE_BASE, 534 "(objectclass=krb5Principal)", krb5principal_attrs, 535 0, &res); 536 537 if (rc != LDAP_SUCCESS) { 538 ret = HDB_ERR_NOENTRY; 539 goto out; 540 } 541 542 e = ldap_first_entry((LDAP *) db->db, res); 543 if (e == NULL) { 544 ret = HDB_ERR_NOENTRY; 545 goto out; 546 } 547 548 values = ldap_get_values((LDAP *) db->db, e, "krb5PrincipalName"); 549 if (values == NULL) { 550 ret = HDB_ERR_NOENTRY; 551 goto out; 552 } 553 554 ret = krb5_parse_name(context, values[0], principal); 555 ldap_value_free(values); 556 557 out: 558 if (res != NULL) { 559 ldap_msgfree(res); 560 } 561 return ret; 562 } 563 564 static krb5_error_code 565 LDAP__lookup_princ(krb5_context context, HDB * db, const char *princname, 566 LDAPMessage ** msg) 567 { 568 krb5_error_code ret; 569 int rc; 570 char *filter = NULL; 571 572 (void) LDAP__connect(context, db); 573 574 rc = 575 asprintf(&filter, 576 "(&(objectclass=krb5KDCEntry)(krb5PrincipalName=%s))", 577 princname); 578 if (rc < 0) { 579 ret = ENOMEM; 580 goto out; 581 } 582 583 rc = 1; 584 (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (void *) &rc); 585 586 rc = ldap_search_s((LDAP *) db->db, db->name, LDAP_SCOPE_ONELEVEL, filter, 587 krb5kdcentry_attrs, 0, msg); 588 if (rc != LDAP_SUCCESS) { 589 ret = HDB_ERR_NOENTRY; 590 goto out; 591 } 592 593 ret = 0; 594 595 out: 596 if (filter != NULL) { 597 free(filter); 598 } 599 return ret; 600 } 601 602 static krb5_error_code 603 LDAP_principal2message(krb5_context context, HDB * db, 604 krb5_principal princ, LDAPMessage ** msg) 605 { 606 char *princname = NULL; 607 krb5_error_code ret; 608 609 ret = krb5_unparse_name(context, princ, &princname); 610 if (ret != 0) { 611 return ret; 612 } 613 614 ret = LDAP__lookup_princ(context, db, princname, msg); 615 free(princname); 616 617 return ret; 618 } 619 620 /* 621 * Construct an hdb_entry from a directory entry. 622 */ 623 static krb5_error_code 624 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, 625 hdb_entry * ent) 626 { 627 char *unparsed_name = NULL, *dn = NULL; 628 int ret; 629 unsigned long tmp; 630 struct berval **keys; 631 char **values; 632 633 memset(ent, 0, sizeof(*ent)); 634 memset(&ent->flags, 0, sizeof(HDBFlags)); 635 636 ret = 637 LDAP_get_string_value(db, msg, "krb5PrincipalName", 638 &unparsed_name); 639 if (ret != 0) { 640 return ret; 641 } 642 643 ret = krb5_parse_name(context, unparsed_name, &ent->principal); 644 if (ret != 0) { 645 goto out; 646 } 647 648 ret = 649 LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber", 650 &ent->kvno); 651 if (ret != 0) { 652 ent->kvno = 0; 653 } 654 655 keys = ldap_get_values_len((LDAP *) db->db, msg, "krb5Key"); 656 if (keys != NULL) { 657 int i; 658 size_t l; 659 660 ent->keys.len = ldap_count_values_len(keys); 661 ent->keys.val = (Key *) calloc(ent->keys.len, sizeof(Key)); 662 for (i = 0; i < ent->keys.len; i++) { 663 decode_Key((unsigned char *) keys[i]->bv_val, 664 (size_t) keys[i]->bv_len, &ent->keys.val[i], &l); 665 } 666 ber_bvecfree(keys); 667 } else { 668 #if 1 669 /* 670 * This violates the ASN1 but it allows a principal to 671 * be related to a general directory entry without creating 672 * the keys. Hopefully it's OK. 673 */ 674 ent->keys.len = 0; 675 ent->keys.val = NULL; 676 #else 677 ret = HDB_ERR_NOENTRY; 678 goto out; 679 #endif 680 } 681 682 ret = 683 LDAP_get_generalized_time_value(db, msg, "createTimestamp", 684 &ent->created_by.time); 685 if (ret != 0) { 686 ent->created_by.time = time(NULL); 687 } 688 689 ent->created_by.principal = NULL; 690 691 ret = LDAP_get_string_value(db, msg, "creatorsName", &dn); 692 if (ret == 0) { 693 if (LDAP_dn2principal(context, db, dn, &ent->created_by.principal) 694 != 0) { 695 ent->created_by.principal = NULL; 696 } 697 free(dn); 698 } 699 700 ent->modified_by = (Event *) malloc(sizeof(Event)); 701 if (ent->modified_by == NULL) { 702 ret = ENOMEM; 703 goto out; 704 } 705 ret = 706 LDAP_get_generalized_time_value(db, msg, "modifyTimestamp", 707 &ent->modified_by->time); 708 if (ret == 0) { 709 ret = LDAP_get_string_value(db, msg, "modifiersName", &dn); 710 if (LDAP_dn2principal 711 (context, db, dn, &ent->modified_by->principal) != 0) { 712 ent->modified_by->principal = NULL; 713 } 714 free(dn); 715 } else { 716 free(ent->modified_by); 717 ent->modified_by = NULL; 718 } 719 720 if ((ent->valid_start = (KerberosTime *) malloc(sizeof(KerberosTime))) 721 == NULL) { 722 ret = ENOMEM; 723 goto out; 724 } 725 ret = 726 LDAP_get_generalized_time_value(db, msg, "krb5ValidStart", 727 ent->valid_start); 728 if (ret != 0) { 729 /* OPTIONAL */ 730 free(ent->valid_start); 731 ent->valid_start = NULL; 732 } 733 734 if ((ent->valid_end = (KerberosTime *) malloc(sizeof(KerberosTime))) == 735 NULL) {ret = ENOMEM; 736 goto out; 737 } 738 ret = 739 LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd", 740 ent->valid_end); 741 if (ret != 0) { 742 /* OPTIONAL */ 743 free(ent->valid_end); 744 ent->valid_end = NULL; 745 } 746 747 if ((ent->pw_end = (KerberosTime *) malloc(sizeof(KerberosTime))) == 748 NULL) {ret = ENOMEM; 749 goto out; 750 } 751 ret = 752 LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd", 753 ent->pw_end); 754 if (ret != 0) { 755 /* OPTIONAL */ 756 free(ent->pw_end); 757 ent->pw_end = NULL; 758 } 759 760 ent->max_life = (int *) malloc(sizeof(int)); 761 if (ent->max_life == NULL) { 762 ret = ENOMEM; 763 goto out; 764 } 765 ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", ent->max_life); 766 if (ret != 0) { 767 free(ent->max_life); 768 ent->max_life = NULL; 769 } 770 771 ent->max_renew = (int *) malloc(sizeof(int)); 772 if (ent->max_renew == NULL) { 773 ret = ENOMEM; 774 goto out; 775 } 776 ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", ent->max_renew); 777 if (ret != 0) { 778 free(ent->max_renew); 779 ent->max_renew = NULL; 780 } 781 782 values = ldap_get_values((LDAP *) db->db, msg, "krb5KDCFlags"); 783 if (values != NULL) { 784 tmp = strtoul(values[0], (char **) NULL, 10); 785 if (tmp == ULONG_MAX && errno == ERANGE) { 786 ret = ERANGE; 787 goto out; 788 } 789 } else { 790 tmp = 0; 791 } 792 memcpy(&ent->flags, &tmp, sizeof(HDBFlags)); 793 794 values = ldap_get_values((LDAP *) db->db, msg, "krb5EncryptionType"); 795 if (values != NULL) { 796 int i; 797 798 ent->etypes = malloc(sizeof(*(ent->etypes))); 799 if (ent->etypes == NULL) { 800 ret = ENOMEM; 801 goto out; 802 } 803 ent->etypes->len = ldap_count_values(values); 804 ent->etypes->val = calloc(ent->etypes->len, sizeof(int)); 805 for (i = 0; i < ent->etypes->len; i++) { 806 ent->etypes->val[i] = atoi(values[i]); 807 } 808 ldap_value_free(values); 809 } 810 811 ret = 0; 812 813 out: 814 if (unparsed_name != NULL) { 815 free(unparsed_name); 816 } 817 818 if (ret != 0) { 819 /* I don't think this frees ent itself. */ 820 hdb_free_entry(context, ent); 821 } 822 823 return ret; 824 } 825 826 static krb5_error_code LDAP_close(krb5_context context, HDB * db) 827 { 828 LDAP *ld = (LDAP *) db->db; 829 830 ldap_unbind(ld); 831 db->db = NULL; 832 return 0; 833 } 834 835 static krb5_error_code 836 LDAP_lock(krb5_context context, HDB * db, int operation) 837 { 838 return 0; 839 } 840 841 static krb5_error_code LDAP_unlock(krb5_context context, HDB * db) 842 { 843 return 0; 844 } 845 846 static krb5_error_code 847 LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry * entry) 848 { 849 int msgid, rc, parserc; 850 krb5_error_code ret; 851 LDAPMessage *e; 852 853 msgid = db->openp; /* BOGUS OVERLOADING */ 854 if (msgid < 0) { 855 return HDB_ERR_NOENTRY; 856 } 857 858 do { 859 rc = ldap_result((LDAP *) db->db, msgid, LDAP_MSG_ONE, NULL, &e); 860 switch (rc) { 861 case LDAP_RES_SEARCH_ENTRY: 862 /* We have an entry. Parse it. */ 863 ret = LDAP_message2entry(context, db, e, entry); 864 ldap_msgfree(e); 865 break; 866 case LDAP_RES_SEARCH_RESULT: 867 /* We're probably at the end of the results. If not, abandon. */ 868 parserc = 869 ldap_parse_result((LDAP *) db->db, e, NULL, NULL, NULL, 870 NULL, NULL, 1); 871 if (parserc != LDAP_SUCCESS 872 && parserc != LDAP_MORE_RESULTS_TO_RETURN) { 873 ldap_abandon((LDAP *) db->db, msgid); 874 } 875 ret = HDB_ERR_NOENTRY; 876 db->openp = -1; 877 break; 878 case 0: 879 case -1: 880 default: 881 /* Some unspecified error (timeout?). Abandon. */ 882 ldap_msgfree(e); 883 ldap_abandon((LDAP *) db->db, msgid); 884 ret = HDB_ERR_NOENTRY; 885 db->openp = -1; 886 break; 887 } 888 } while (rc == LDAP_RES_SEARCH_REFERENCE); 889 890 if (ret == 0) { 891 if (db->master_key_set && (flags & HDB_F_DECRYPT)) { 892 ret = hdb_unseal_keys(context, db, entry); 893 if (ret) 894 hdb_free_entry(context,entry); 895 } 896 } 897 898 return ret; 899 } 900 901 static krb5_error_code 902 LDAP_firstkey(krb5_context context, HDB * db, unsigned flags, 903 hdb_entry * entry) 904 { 905 int msgid; 906 907 (void) LDAP__connect(context, db); 908 909 msgid = LDAP_NO_LIMIT; 910 (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, &msgid); 911 912 msgid = ldap_search((LDAP *) db->db, db->name, 913 LDAP_SCOPE_ONELEVEL, "(objectclass=krb5KDCEntry)", 914 krb5kdcentry_attrs, 0); 915 if (msgid < 0) { 916 return HDB_ERR_NOENTRY; 917 } 918 919 db->openp = msgid; 920 921 return LDAP_seq(context, db, flags, entry); 922 } 923 924 static krb5_error_code 925 LDAP_nextkey(krb5_context context, HDB * db, unsigned flags, 926 hdb_entry * entry) 927 { 928 return LDAP_seq(context, db, flags, entry); 929 } 930 931 static krb5_error_code 932 LDAP_rename(krb5_context context, HDB * db, const char *new_name) 933 { 934 return HDB_ERR_DB_INUSE; 935 } 936 937 static krb5_boolean LDAP__is_user_namingcontext(const char *ctx, 938 char *const *subschema) 939 { 940 char *const *p; 941 942 if (!strcasecmp(ctx, "CN=MONITOR") 943 || !strcasecmp(ctx, "CN=CONFIG")) { 944 return FALSE; 945 } 946 947 if (subschema != NULL) { 948 for (p = subschema; *p != NULL; p++) { 949 if (!strcasecmp(ctx, *p)) { 950 return FALSE; 951 } 952 } 953 } 954 955 return TRUE; 956 } 957 958 static krb5_error_code LDAP__connect(krb5_context context, HDB * db) 959 { 960 int rc; 961 krb5_error_code ret; 962 char *attrs[] = { "namingContexts", "subschemaSubentry", NULL }; 963 LDAPMessage *res = NULL, *e; 964 965 if (db->db != NULL) { 966 /* connection has been opened. ping server. */ 967 struct sockaddr_un addr; 968 socklen_t len; 969 int sd; 970 971 if (ldap_get_option((LDAP *) db->db, LDAP_OPT_DESC, &sd) == 0 && 972 getpeername(sd, (struct sockaddr *) &addr, &len) < 0) { 973 /* the other end has died. reopen. */ 974 LDAP_close(context, db); 975 } 976 } 977 978 if (db->db != NULL) { 979 /* server is UP */ 980 return 0; 981 } 982 983 rc = ldap_initialize((LDAP **) & db->db, "ldapi:///"); 984 if (rc != LDAP_SUCCESS) { 985 return HDB_ERR_NOENTRY; 986 } 987 988 rc = LDAP_VERSION3; 989 (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_PROTOCOL_VERSION, &rc); 990 991 /* XXX set db->name to the search base */ 992 rc = ldap_search_s((LDAP *) db->db, "", LDAP_SCOPE_BASE, 993 "(objectclass=*)", attrs, 0, &res); 994 if (rc != LDAP_SUCCESS) { 995 ret = HDB_ERR_BADVERSION; 996 goto out; 997 } 998 999 e = ldap_first_entry((LDAP *) db->db, res); 1000 if (e == NULL) { 1001 ret = HDB_ERR_NOENTRY; 1002 goto out; 1003 } 1004 1005 if (db->name == NULL) { 1006 char **contexts = NULL, **schema_contexts, **p; 1007 1008 contexts = ldap_get_values((LDAP *) db->db, e, "namingContexts"); 1009 if (contexts == NULL) { 1010 ret = HDB_ERR_NOENTRY; 1011 goto out; 1012 } 1013 1014 schema_contexts = 1015 ldap_get_values((LDAP *) db->db, e, "subschemaSubentry"); 1016 1017 if (db->name != NULL) { 1018 free(db->name); 1019 db->name = NULL; 1020 } 1021 1022 for (p = contexts; *p != NULL; p++) { 1023 if (LDAP__is_user_namingcontext(*p, schema_contexts)) { 1024 break; 1025 } 1026 } 1027 1028 db->name = strdup(*p); 1029 if (db->name == NULL) { 1030 ldap_value_free(contexts); 1031 ret = ENOMEM; 1032 goto out; 1033 } 1034 1035 ldap_value_free(contexts); 1036 if (schema_contexts != NULL) { 1037 ldap_value_free(schema_contexts); 1038 } 1039 } 1040 1041 ret = 0; 1042 1043 out: 1044 1045 if (res != NULL) { 1046 ldap_msgfree(res); 1047 } 1048 1049 if (ret != 0) { 1050 if (db->db != NULL) { 1051 ldap_unbind((LDAP *) db->db); 1052 db->db = NULL; 1053 } 1054 } 1055 1056 return ret; 1057 } 1058 1059 static krb5_error_code 1060 LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode) 1061 { 1062 krb5_error_code ret; 1063 1064 /* Not the right place for this. */ 1065 #ifdef HAVE_SIGACTION 1066 { 1067 struct sigaction sa; 1068 1069 sa.sa_flags = 0; 1070 sa.sa_handler = SIG_IGN; 1071 sigemptyset(&sa.sa_mask); 1072 1073 sigaction(SIGPIPE, &sa, NULL); 1074 } 1075 #else 1076 signal(SIGPIPE, SIG_IGN); 1077 #endif 1078 1079 if (db->name != NULL) { 1080 free(db->name); 1081 db->name = NULL; 1082 } 1083 1084 ret = LDAP__connect(context, db); 1085 if (ret != 0) { 1086 return ret; 1087 } 1088 1089 return ret; 1090 } 1091 1092 static krb5_error_code 1093 LDAP_fetch(krb5_context context, HDB * db, unsigned flags, 1094 hdb_entry * entry) 1095 { 1096 LDAPMessage *msg, *e; 1097 krb5_error_code ret; 1098 1099 ret = LDAP_principal2message(context, db, entry->principal, &msg); 1100 if (ret != 0) { 1101 return ret; 1102 } 1103 1104 e = ldap_first_entry((LDAP *) db->db, msg); 1105 if (e == NULL) { 1106 ret = HDB_ERR_NOENTRY; 1107 goto out; 1108 } 1109 1110 ret = LDAP_message2entry(context, db, e, entry); 1111 if (ret == 0) { 1112 if (db->master_key_set && (flags & HDB_F_DECRYPT)) { 1113 ret = hdb_unseal_keys(context, db, entry); 1114 if (ret) 1115 hdb_free_entry(context,entry); 1116 } 1117 } 1118 1119 out: 1120 ldap_msgfree(msg); 1121 1122 return ret; 1123 } 1124 1125 static krb5_error_code 1126 LDAP_store(krb5_context context, HDB * db, unsigned flags, 1127 hdb_entry * entry) 1128 { 1129 LDAPMod **mods = NULL; 1130 krb5_error_code ret; 1131 LDAPMessage *msg = NULL, *e = NULL; 1132 char *dn = NULL, *name = NULL; 1133 1134 ret = krb5_unparse_name(context, entry->principal, &name); 1135 if (ret != 0) { 1136 goto out; 1137 } 1138 1139 ret = LDAP__lookup_princ(context, db, name, &msg); 1140 if (ret == 0) { 1141 e = ldap_first_entry((LDAP *) db->db, msg); 1142 } 1143 1144 ret = hdb_seal_keys(context, db, entry); 1145 if (ret) 1146 goto out; 1147 1148 /* turn new entry into LDAPMod array */ 1149 ret = LDAP_entry2mods(context, db, entry, e, &mods); 1150 if (ret != 0) { 1151 goto out; 1152 } 1153 1154 if (e == NULL) { 1155 /* Doesn't exist yet. */ 1156 char *p; 1157 1158 e = NULL; 1159 1160 /* normalize the naming attribute */ 1161 for (p = name; *p != '\0'; p++) { 1162 *p = (char) tolower((int) *p); 1163 } 1164 1165 /* 1166 * We could do getpwnam() on the local component of 1167 * the principal to find cn/sn but that's probably 1168 * bad thing to do from inside a KDC. Better leave 1169 * it to management tools. 1170 */ 1171 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "cn", name); 1172 if (ret < 0) { 1173 goto out; 1174 } 1175 1176 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "sn", name); 1177 if (ret < 0) { 1178 goto out; 1179 } 1180 1181 ret = asprintf(&dn, "cn=%s,%s", name, db->name); 1182 if (ret < 0) { 1183 ret = ENOMEM; 1184 goto out; 1185 } 1186 } else if (flags & HDB_F_REPLACE) { 1187 /* Entry exists, and we're allowed to replace it. */ 1188 dn = ldap_get_dn((LDAP *) db->db, e); 1189 } else { 1190 /* Entry exists, but we're not allowed to replace it. Bail. */ 1191 ret = HDB_ERR_EXISTS; 1192 goto out; 1193 } 1194 1195 /* write entry into directory */ 1196 if (e == NULL) { 1197 /* didn't exist before */ 1198 ret = ldap_add_s((LDAP *) db->db, dn, mods); 1199 } else { 1200 /* already existed, send deltas only */ 1201 ret = ldap_modify_s((LDAP *) db->db, dn, mods); 1202 } 1203 1204 if (ret == LDAP_SUCCESS) { 1205 ret = 0; 1206 } else { 1207 ret = HDB_ERR_CANT_LOCK_DB; 1208 } 1209 1210 out: 1211 /* free stuff */ 1212 if (dn != NULL) { 1213 free(dn); 1214 } 1215 1216 if (msg != NULL) { 1217 ldap_msgfree(msg); 1218 } 1219 1220 if (mods != NULL) { 1221 ldap_mods_free(mods, 1); 1222 } 1223 1224 if (name != NULL) { 1225 free(name); 1226 } 1227 1228 return ret; 1229 } 1230 1231 static krb5_error_code 1232 LDAP_remove(krb5_context context, HDB * db, hdb_entry * entry) 1233 { 1234 krb5_error_code ret; 1235 LDAPMessage *msg, *e; 1236 char *dn = NULL; 1237 1238 ret = LDAP_principal2message(context, db, entry->principal, &msg); 1239 if (ret != 0) { 1240 goto out; 1241 } 1242 1243 e = ldap_first_entry((LDAP *) db->db, msg); 1244 if (e == NULL) { 1245 ret = HDB_ERR_NOENTRY; 1246 goto out; 1247 } 1248 1249 dn = ldap_get_dn((LDAP *) db->db, e); 1250 if (dn == NULL) { 1251 ret = HDB_ERR_NOENTRY; 1252 goto out; 1253 } 1254 1255 ret = LDAP_NO_LIMIT; 1256 (void) ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, &ret); 1257 1258 ret = ldap_delete_s((LDAP *) db->db, dn); 1259 if (ret == LDAP_SUCCESS) { 1260 ret = 0; 1261 } else { 1262 ret = HDB_ERR_CANT_LOCK_DB; 1263 } 1264 1265 out: 1266 if (dn != NULL) { 1267 free(dn); 1268 } 1269 1270 if (msg != NULL) { 1271 ldap_msgfree(msg); 1272 } 1273 1274 return ret; 1275 } 1276 1277 static krb5_error_code 1278 LDAP__get(krb5_context context, HDB * db, krb5_data key, krb5_data * reply) 1279 { 1280 fprintf(stderr, "LDAP__get not implemented\n"); 1281 abort(); 1282 return 0; 1283 } 1284 1285 static krb5_error_code 1286 LDAP__put(krb5_context context, HDB * db, int replace, 1287 krb5_data key, krb5_data value) 1288 { 1289 fprintf(stderr, "LDAP__put not implemented\n"); 1290 abort(); 1291 return 0; 1292 } 1293 1294 static krb5_error_code 1295 LDAP__del(krb5_context context, HDB * db, krb5_data key) 1296 { 1297 fprintf(stderr, "LDAP__del not implemented\n"); 1298 abort(); 1299 return 0; 1300 } 1301 1302 static krb5_error_code LDAP_destroy(krb5_context context, HDB * db) 1303 { 1304 krb5_error_code ret; 1305 1306 ret = hdb_clear_master_key(context, db); 1307 free(db->name); 1308 free(db); 1309 1310 return ret; 1311 } 1312 1313 krb5_error_code 1314 hdb_ldap_create(krb5_context context, HDB ** db, const char *filename) 1315 { 1316 *db = malloc(sizeof(**db)); 1317 if (*db == NULL) 1318 return ENOMEM; 1319 1320 (*db)->db = NULL; 1321 /* (*db)->name = strdup(filename); */ 1322 (*db)->name = NULL; 1323 (*db)->master_key_set = 0; 1324 (*db)->openp = 0; 1325 (*db)->open = LDAP_open; 1326 (*db)->close = LDAP_close; 1327 (*db)->fetch = LDAP_fetch; 1328 (*db)->store = LDAP_store; 1329 (*db)->remove = LDAP_remove; 1330 (*db)->firstkey = LDAP_firstkey; 1331 (*db)->nextkey = LDAP_nextkey; 1332 (*db)->lock = LDAP_lock; 1333 (*db)->unlock = LDAP_unlock; 1334 (*db)->rename = LDAP_rename; 1335 /* can we ditch these? */ 1336 (*db)->_get = LDAP__get; 1337 (*db)->_put = LDAP__put; 1338 (*db)->_del = LDAP__del; 1339 (*db)->destroy = LDAP_destroy; 1340 1341 return 0; 1342 } 1343 1344 #endif /* OPENLDAP */ 1345