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.9 2001/08/31 18:19:49 joda 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 krb5_set_error_string(context, "asprintf: out of memory"); 339 ret = ENOMEM; 340 goto out; 341 } 342 ret = 343 LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5KeyVersionNumber", 344 tmp); 345 free(tmp); 346 if (ret != 0) { 347 goto out; 348 } 349 } 350 351 if (ent->valid_start) { 352 if (orig.valid_end == NULL 353 || (*(ent->valid_start) != *(orig.valid_start))) { 354 ret = 355 LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 356 "krb5ValidStart", 357 ent->valid_start); 358 if (ret != 0) { 359 goto out; 360 } 361 } 362 } 363 364 if (ent->valid_end) { 365 if (orig.valid_end == NULL 366 || (*(ent->valid_end) != *(orig.valid_end))) { 367 ret = 368 LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 369 "krb5ValidEnd", 370 ent->valid_end); 371 if (ret != 0) { 372 goto out; 373 } 374 } 375 } 376 377 if (ent->pw_end) { 378 if (orig.pw_end == NULL || (*(ent->pw_end) != *(orig.pw_end))) { 379 ret = 380 LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 381 "krb5PasswordEnd", 382 ent->pw_end); 383 if (ret != 0) { 384 goto out; 385 } 386 } 387 } 388 389 if (ent->max_life) { 390 if (orig.max_life == NULL 391 || (*(ent->max_life) != *(orig.max_life))) { 392 rc = asprintf(&tmp, "%d", *(ent->max_life)); 393 if (rc < 0) { 394 krb5_set_error_string(context, "asprintf: out of memory"); 395 ret = ENOMEM; 396 goto out; 397 } 398 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5MaxLife", tmp); 399 free(tmp); 400 if (ret != 0) { 401 goto out; 402 } 403 } 404 } 405 406 if (ent->max_renew) { 407 if (orig.max_renew == NULL 408 || (*(ent->max_renew) != *(orig.max_renew))) { 409 rc = asprintf(&tmp, "%d", *(ent->max_renew)); 410 if (rc < 0) { 411 krb5_set_error_string(context, "asprintf: out of memory"); 412 ret = ENOMEM; 413 goto out; 414 } 415 ret = 416 LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5MaxRenew", tmp); 417 free(tmp); 418 if (ret != 0) { 419 goto out; 420 } 421 } 422 } 423 424 memset(&oflags, 0, sizeof(oflags)); 425 memcpy(&oflags, &orig.flags, sizeof(HDBFlags)); 426 memset(&nflags, 0, sizeof(nflags)); 427 memcpy(&nflags, &ent->flags, sizeof(HDBFlags)); 428 429 if (memcmp(&oflags, &nflags, sizeof(HDBFlags))) { 430 rc = asprintf(&tmp, "%lu", nflags); 431 if (rc < 0) { 432 krb5_set_error_string(context, "asprintf: out of memory"); 433 ret = ENOMEM; 434 goto out; 435 } 436 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5KDCFlags", tmp); 437 free(tmp); 438 if (ret != 0) { 439 goto out; 440 } 441 } 442 443 if (is_new_entry == FALSE && orig.keys.len > 0) { 444 /* for the moment, clobber and replace keys. */ 445 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL); 446 if (ret != 0) { 447 goto out; 448 } 449 } 450 451 for (i = 0; i < ent->keys.len; i++) { 452 unsigned char *buf; 453 size_t len; 454 Key new; 455 456 ret = copy_Key(&ent->keys.val[i], &new); 457 if (ret != 0) { 458 goto out; 459 } 460 461 len = length_Key(&new); 462 buf = malloc(len); 463 if (buf == NULL) { 464 krb5_set_error_string(context, "malloc: out of memory"); 465 ret = ENOMEM; 466 free_Key(&new); 467 goto out; 468 } 469 470 ret = encode_Key(buf + len - 1, len, &new, &len); 471 if (ret != 0) { 472 free(buf); 473 free_Key(&new); 474 goto out; 475 } 476 free_Key(&new); 477 478 /* addmod_len _owns_ the key, doesn't need to copy it */ 479 ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len); 480 if (ret != 0) { 481 goto out; 482 } 483 } 484 485 if (ent->etypes) { 486 /* clobber and replace encryption types. */ 487 if (is_new_entry == FALSE) { 488 ret = 489 LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType", 490 NULL); 491 } 492 for (i = 0; i < ent->etypes->len; i++) { 493 rc = asprintf(&tmp, "%d", ent->etypes->val[i]); 494 if (rc < 0) { 495 krb5_set_error_string(context, "asprintf: out of memory"); 496 ret = ENOMEM; 497 goto out; 498 } 499 free(tmp); 500 ret = 501 LDAP_addmod(&mods, LDAP_MOD_ADD, "krb5EncryptionType", 502 tmp); 503 if (ret != 0) { 504 goto out; 505 } 506 } 507 } 508 509 /* for clarity */ 510 ret = 0; 511 512 out: 513 514 if (ret == 0) { 515 *pmods = mods; 516 } else if (mods != NULL) { 517 ldap_mods_free(mods, 1); 518 *pmods = NULL; 519 } 520 521 if (msg != NULL) { 522 hdb_free_entry(context, &orig); 523 } 524 525 return ret; 526 } 527 528 static krb5_error_code 529 LDAP_dn2principal(krb5_context context, HDB * db, const char *dn, 530 krb5_principal * principal) 531 { 532 krb5_error_code ret; 533 int rc, limit = 1; 534 char **values; 535 LDAPMessage *res = NULL, *e; 536 537 rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (const void *)&limit); 538 if (rc != LDAP_SUCCESS) { 539 krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc)); 540 ret = HDB_ERR_BADVERSION; 541 goto out; 542 } 543 544 rc = ldap_search_s((LDAP *) db->db, dn, LDAP_SCOPE_BASE, 545 "(objectclass=krb5Principal)", krb5principal_attrs, 546 0, &res); 547 if (rc != LDAP_SUCCESS) { 548 krb5_set_error_string(context, "ldap_search_s: %s", ldap_err2string(rc)); 549 ret = HDB_ERR_NOENTRY; 550 goto out; 551 } 552 553 e = ldap_first_entry((LDAP *) db->db, res); 554 if (e == NULL) { 555 ret = HDB_ERR_NOENTRY; 556 goto out; 557 } 558 559 values = ldap_get_values((LDAP *) db->db, e, "krb5PrincipalName"); 560 if (values == NULL) { 561 ret = HDB_ERR_NOENTRY; 562 goto out; 563 } 564 565 ret = krb5_parse_name(context, values[0], principal); 566 ldap_value_free(values); 567 568 out: 569 if (res != NULL) { 570 ldap_msgfree(res); 571 } 572 return ret; 573 } 574 575 static krb5_error_code 576 LDAP__lookup_princ(krb5_context context, HDB * db, const char *princname, 577 LDAPMessage ** msg) 578 { 579 krb5_error_code ret; 580 int rc, limit = 1; 581 char *filter = NULL; 582 583 (void) LDAP__connect(context, db); 584 585 rc = 586 asprintf(&filter, 587 "(&(objectclass=krb5KDCEntry)(krb5PrincipalName=%s))", 588 princname); 589 if (rc < 0) { 590 krb5_set_error_string(context, "asprintf: out of memory"); 591 ret = ENOMEM; 592 goto out; 593 } 594 595 rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (const void *)&limit); 596 if (rc != LDAP_SUCCESS) { 597 krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc)); 598 ret = HDB_ERR_BADVERSION; 599 goto out; 600 } 601 602 rc = ldap_search_s((LDAP *) db->db, db->name, LDAP_SCOPE_ONELEVEL, filter, 603 krb5kdcentry_attrs, 0, msg); 604 if (rc != LDAP_SUCCESS) { 605 krb5_set_error_string(context, "ldap_search_s: %s", ldap_err2string(rc)); 606 ret = HDB_ERR_NOENTRY; 607 goto out; 608 } 609 610 ret = 0; 611 612 out: 613 if (filter != NULL) { 614 free(filter); 615 } 616 return ret; 617 } 618 619 static krb5_error_code 620 LDAP_principal2message(krb5_context context, HDB * db, 621 krb5_principal princ, LDAPMessage ** msg) 622 { 623 char *princname = NULL; 624 krb5_error_code ret; 625 626 ret = krb5_unparse_name(context, princ, &princname); 627 if (ret != 0) { 628 return ret; 629 } 630 631 ret = LDAP__lookup_princ(context, db, princname, msg); 632 free(princname); 633 634 return ret; 635 } 636 637 /* 638 * Construct an hdb_entry from a directory entry. 639 */ 640 static krb5_error_code 641 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, 642 hdb_entry * ent) 643 { 644 char *unparsed_name = NULL, *dn = NULL; 645 int ret; 646 unsigned long tmp; 647 struct berval **keys; 648 char **values; 649 650 memset(ent, 0, sizeof(*ent)); 651 memset(&ent->flags, 0, sizeof(HDBFlags)); 652 653 ret = 654 LDAP_get_string_value(db, msg, "krb5PrincipalName", 655 &unparsed_name); 656 if (ret != 0) { 657 return ret; 658 } 659 660 ret = krb5_parse_name(context, unparsed_name, &ent->principal); 661 if (ret != 0) { 662 goto out; 663 } 664 665 ret = 666 LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber", 667 &ent->kvno); 668 if (ret != 0) { 669 ent->kvno = 0; 670 } 671 672 keys = ldap_get_values_len((LDAP *) db->db, msg, "krb5Key"); 673 if (keys != NULL) { 674 int i; 675 size_t l; 676 677 ent->keys.len = ldap_count_values_len(keys); 678 ent->keys.val = (Key *) calloc(ent->keys.len, sizeof(Key)); 679 if (ent->keys.val == NULL) { 680 krb5_set_error_string(context, "calloc: out of memory"); 681 ret = ENOMEM; 682 goto out; 683 } 684 for (i = 0; i < ent->keys.len; i++) { 685 decode_Key((unsigned char *) keys[i]->bv_val, 686 (size_t) keys[i]->bv_len, &ent->keys.val[i], &l); 687 } 688 ber_bvecfree(keys); 689 } else { 690 #if 1 691 /* 692 * This violates the ASN1 but it allows a principal to 693 * be related to a general directory entry without creating 694 * the keys. Hopefully it's OK. 695 */ 696 ent->keys.len = 0; 697 ent->keys.val = NULL; 698 #else 699 ret = HDB_ERR_NOENTRY; 700 goto out; 701 #endif 702 } 703 704 ret = 705 LDAP_get_generalized_time_value(db, msg, "createTimestamp", 706 &ent->created_by.time); 707 if (ret != 0) { 708 ent->created_by.time = time(NULL); 709 } 710 711 ent->created_by.principal = NULL; 712 713 ret = LDAP_get_string_value(db, msg, "creatorsName", &dn); 714 if (ret == 0) { 715 if (LDAP_dn2principal(context, db, dn, &ent->created_by.principal) 716 != 0) { 717 ent->created_by.principal = NULL; 718 } 719 free(dn); 720 } 721 722 ent->modified_by = (Event *) malloc(sizeof(Event)); 723 if (ent->modified_by == NULL) { 724 krb5_set_error_string(context, "malloc: out of memory"); 725 ret = ENOMEM; 726 goto out; 727 } 728 ret = 729 LDAP_get_generalized_time_value(db, msg, "modifyTimestamp", 730 &ent->modified_by->time); 731 if (ret == 0) { 732 ret = LDAP_get_string_value(db, msg, "modifiersName", &dn); 733 if (LDAP_dn2principal 734 (context, db, dn, &ent->modified_by->principal) != 0) { 735 ent->modified_by->principal = NULL; 736 } 737 free(dn); 738 } else { 739 free(ent->modified_by); 740 ent->modified_by = NULL; 741 } 742 743 if ((ent->valid_start = (KerberosTime *) malloc(sizeof(KerberosTime))) 744 == NULL) { 745 krb5_set_error_string(context, "malloc: out of memory"); 746 ret = ENOMEM; 747 goto out; 748 } 749 ret = 750 LDAP_get_generalized_time_value(db, msg, "krb5ValidStart", 751 ent->valid_start); 752 if (ret != 0) { 753 /* OPTIONAL */ 754 free(ent->valid_start); 755 ent->valid_start = NULL; 756 } 757 758 if ((ent->valid_end = (KerberosTime *) malloc(sizeof(KerberosTime))) == 759 NULL) { 760 krb5_set_error_string(context, "malloc: out of memory"); 761 ret = ENOMEM; 762 goto out; 763 } 764 ret = 765 LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd", 766 ent->valid_end); 767 if (ret != 0) { 768 /* OPTIONAL */ 769 free(ent->valid_end); 770 ent->valid_end = NULL; 771 } 772 773 if ((ent->pw_end = (KerberosTime *) malloc(sizeof(KerberosTime))) == 774 NULL) { 775 krb5_set_error_string(context, "malloc: out of memory"); 776 ret = ENOMEM; 777 goto out; 778 } 779 ret = 780 LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd", 781 ent->pw_end); 782 if (ret != 0) { 783 /* OPTIONAL */ 784 free(ent->pw_end); 785 ent->pw_end = NULL; 786 } 787 788 ent->max_life = (int *) malloc(sizeof(int)); 789 if (ent->max_life == NULL) { 790 krb5_set_error_string(context, "malloc: out of memory"); 791 ret = ENOMEM; 792 goto out; 793 } 794 ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", ent->max_life); 795 if (ret != 0) { 796 free(ent->max_life); 797 ent->max_life = NULL; 798 } 799 800 ent->max_renew = (int *) malloc(sizeof(int)); 801 if (ent->max_renew == NULL) { 802 krb5_set_error_string(context, "malloc: out of memory"); 803 ret = ENOMEM; 804 goto out; 805 } 806 ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", ent->max_renew); 807 if (ret != 0) { 808 free(ent->max_renew); 809 ent->max_renew = NULL; 810 } 811 812 values = ldap_get_values((LDAP *) db->db, msg, "krb5KDCFlags"); 813 if (values != NULL) { 814 tmp = strtoul(values[0], (char **) NULL, 10); 815 if (tmp == ULONG_MAX && errno == ERANGE) { 816 krb5_set_error_string(context, "strtoul: could not convert flag"); 817 ret = ERANGE; 818 goto out; 819 } 820 } else { 821 tmp = 0; 822 } 823 memcpy(&ent->flags, &tmp, sizeof(HDBFlags)); 824 825 values = ldap_get_values((LDAP *) db->db, msg, "krb5EncryptionType"); 826 if (values != NULL) { 827 int i; 828 829 ent->etypes = malloc(sizeof(*(ent->etypes))); 830 if (ent->etypes == NULL) { 831 krb5_set_error_string(context, "malloc: out of memory"); 832 ret = ENOMEM; 833 goto out; 834 } 835 ent->etypes->len = ldap_count_values(values); 836 ent->etypes->val = calloc(ent->etypes->len, sizeof(int)); 837 for (i = 0; i < ent->etypes->len; i++) { 838 ent->etypes->val[i] = atoi(values[i]); 839 } 840 ldap_value_free(values); 841 } 842 843 ret = 0; 844 845 out: 846 if (unparsed_name != NULL) { 847 free(unparsed_name); 848 } 849 850 if (ret != 0) { 851 /* I don't think this frees ent itself. */ 852 hdb_free_entry(context, ent); 853 } 854 855 return ret; 856 } 857 858 static krb5_error_code LDAP_close(krb5_context context, HDB * db) 859 { 860 ldap_unbind_ext((LDAP *) db->db, NULL, NULL); 861 db->db = NULL; 862 863 return 0; 864 } 865 866 static krb5_error_code 867 LDAP_lock(krb5_context context, HDB * db, int operation) 868 { 869 return 0; 870 } 871 872 static krb5_error_code LDAP_unlock(krb5_context context, HDB * db) 873 { 874 return 0; 875 } 876 877 static krb5_error_code 878 LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry * entry) 879 { 880 int msgid, rc, parserc; 881 krb5_error_code ret; 882 LDAPMessage *e; 883 884 msgid = db->openp; /* BOGUS OVERLOADING */ 885 if (msgid < 0) { 886 return HDB_ERR_NOENTRY; 887 } 888 889 do { 890 rc = ldap_result((LDAP *) db->db, msgid, LDAP_MSG_ONE, NULL, &e); 891 switch (rc) { 892 case LDAP_RES_SEARCH_ENTRY: 893 /* We have an entry. Parse it. */ 894 ret = LDAP_message2entry(context, db, e, entry); 895 ldap_msgfree(e); 896 break; 897 case LDAP_RES_SEARCH_RESULT: 898 /* We're probably at the end of the results. If not, abandon. */ 899 parserc = 900 ldap_parse_result((LDAP *) db->db, e, NULL, NULL, NULL, 901 NULL, NULL, 1); 902 if (parserc != LDAP_SUCCESS 903 && parserc != LDAP_MORE_RESULTS_TO_RETURN) { 904 krb5_set_error_string(context, "ldap_parse_result: %s", ldap_err2string(parserc)); 905 ldap_abandon((LDAP *) db->db, msgid); 906 } 907 ret = HDB_ERR_NOENTRY; 908 db->openp = -1; 909 break; 910 case 0: 911 case -1: 912 default: 913 /* Some unspecified error (timeout?). Abandon. */ 914 ldap_msgfree(e); 915 ldap_abandon((LDAP *) db->db, msgid); 916 ret = HDB_ERR_NOENTRY; 917 db->openp = -1; 918 break; 919 } 920 } while (rc == LDAP_RES_SEARCH_REFERENCE); 921 922 if (ret == 0) { 923 if (db->master_key_set && (flags & HDB_F_DECRYPT)) { 924 ret = hdb_unseal_keys(context, db, entry); 925 if (ret) 926 hdb_free_entry(context,entry); 927 } 928 } 929 930 return ret; 931 } 932 933 static krb5_error_code 934 LDAP_firstkey(krb5_context context, HDB * db, unsigned flags, 935 hdb_entry * entry) 936 { 937 int msgid, limit = LDAP_NO_LIMIT, rc; 938 939 (void) LDAP__connect(context, db); 940 941 rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (const void *)&limit); 942 if (rc != LDAP_SUCCESS) { 943 krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc)); 944 return HDB_ERR_BADVERSION; 945 } 946 947 msgid = ldap_search((LDAP *) db->db, db->name, 948 LDAP_SCOPE_ONELEVEL, "(objectclass=krb5KDCEntry)", 949 krb5kdcentry_attrs, 0); 950 if (msgid < 0) { 951 return HDB_ERR_NOENTRY; 952 } 953 954 db->openp = msgid; 955 956 return LDAP_seq(context, db, flags, entry); 957 } 958 959 static krb5_error_code 960 LDAP_nextkey(krb5_context context, HDB * db, unsigned flags, 961 hdb_entry * entry) 962 { 963 return LDAP_seq(context, db, flags, entry); 964 } 965 966 static krb5_error_code 967 LDAP_rename(krb5_context context, HDB * db, const char *new_name) 968 { 969 return HDB_ERR_DB_INUSE; 970 } 971 972 static krb5_error_code LDAP__connect(krb5_context context, HDB * db) 973 { 974 int rc, version = LDAP_VERSION3; 975 976 if (db->db != NULL) { 977 /* connection has been opened. ping server. */ 978 struct sockaddr_un addr; 979 socklen_t len; 980 int sd; 981 982 if (ldap_get_option((LDAP *) db->db, LDAP_OPT_DESC, &sd) == 0 && 983 getpeername(sd, (struct sockaddr *) &addr, &len) < 0) { 984 /* the other end has died. reopen. */ 985 LDAP_close(context, db); 986 } 987 } 988 989 if (db->db != NULL) { 990 /* server is UP */ 991 return 0; 992 } 993 994 rc = ldap_initialize((LDAP **) & db->db, "ldapi:///"); 995 if (rc != LDAP_SUCCESS) { 996 krb5_set_error_string(context, "ldap_initialize: %s", ldap_err2string(rc)); 997 return HDB_ERR_NOENTRY; 998 } 999 1000 rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_PROTOCOL_VERSION, (const void *)&version); 1001 if (rc != LDAP_SUCCESS) { 1002 krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc)); 1003 ldap_unbind_ext((LDAP *) db->db, NULL, NULL); 1004 db->db = NULL; 1005 return HDB_ERR_BADVERSION; 1006 } 1007 1008 return 0; 1009 } 1010 1011 static krb5_error_code 1012 LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode) 1013 { 1014 /* Not the right place for this. */ 1015 #ifdef HAVE_SIGACTION 1016 struct sigaction sa; 1017 1018 sa.sa_flags = 0; 1019 sa.sa_handler = SIG_IGN; 1020 sigemptyset(&sa.sa_mask); 1021 1022 sigaction(SIGPIPE, &sa, NULL); 1023 #else 1024 signal(SIGPIPE, SIG_IGN); 1025 #endif /* HAVE_SIGACTION */ 1026 1027 return LDAP__connect(context, db); 1028 } 1029 1030 static krb5_error_code 1031 LDAP_fetch(krb5_context context, HDB * db, unsigned flags, 1032 hdb_entry * entry) 1033 { 1034 LDAPMessage *msg, *e; 1035 krb5_error_code ret; 1036 1037 ret = LDAP_principal2message(context, db, entry->principal, &msg); 1038 if (ret != 0) { 1039 return ret; 1040 } 1041 1042 e = ldap_first_entry((LDAP *) db->db, msg); 1043 if (e == NULL) { 1044 ret = HDB_ERR_NOENTRY; 1045 goto out; 1046 } 1047 1048 ret = LDAP_message2entry(context, db, e, entry); 1049 if (ret == 0) { 1050 if (db->master_key_set && (flags & HDB_F_DECRYPT)) { 1051 ret = hdb_unseal_keys(context, db, entry); 1052 if (ret) 1053 hdb_free_entry(context,entry); 1054 } 1055 } 1056 1057 out: 1058 ldap_msgfree(msg); 1059 1060 return ret; 1061 } 1062 1063 static krb5_error_code 1064 LDAP_store(krb5_context context, HDB * db, unsigned flags, 1065 hdb_entry * entry) 1066 { 1067 LDAPMod **mods = NULL; 1068 krb5_error_code ret; 1069 const char *errfn; 1070 int rc; 1071 LDAPMessage *msg = NULL, *e = NULL; 1072 char *dn = NULL, *name = NULL; 1073 1074 ret = krb5_unparse_name(context, entry->principal, &name); 1075 if (ret != 0) { 1076 goto out; 1077 } 1078 1079 ret = LDAP__lookup_princ(context, db, name, &msg); 1080 if (ret == 0) { 1081 e = ldap_first_entry((LDAP *) db->db, msg); 1082 } 1083 1084 ret = hdb_seal_keys(context, db, entry); 1085 if (ret != 0) { 1086 goto out; 1087 } 1088 1089 /* turn new entry into LDAPMod array */ 1090 ret = LDAP_entry2mods(context, db, entry, e, &mods); 1091 if (ret != 0) { 1092 goto out; 1093 } 1094 1095 if (e == NULL) { 1096 /* Doesn't exist yet. */ 1097 char *p; 1098 1099 e = NULL; 1100 1101 /* normalize the naming attribute */ 1102 for (p = name; *p != '\0'; p++) { 1103 *p = (char) tolower((int) *p); 1104 } 1105 1106 /* 1107 * We could do getpwnam() on the local component of 1108 * the principal to find cn/sn but that's probably 1109 * bad thing to do from inside a KDC. Better leave 1110 * it to management tools. 1111 */ 1112 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "cn", name); 1113 if (ret < 0) { 1114 goto out; 1115 } 1116 1117 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "sn", name); 1118 if (ret < 0) { 1119 goto out; 1120 } 1121 1122 if (db->name != NULL) { 1123 ret = asprintf(&dn, "cn=%s,%s", name, db->name); 1124 } else { 1125 /* A bit bogus, but we don't have a search base */ 1126 ret = asprintf(&dn, "cn=%s", name, db->name); 1127 } 1128 if (ret < 0) { 1129 krb5_set_error_string(context, "asprintf: out of memory"); 1130 ret = ENOMEM; 1131 goto out; 1132 } 1133 } else if (flags & HDB_F_REPLACE) { 1134 /* Entry exists, and we're allowed to replace it. */ 1135 dn = ldap_get_dn((LDAP *) db->db, e); 1136 } else { 1137 /* Entry exists, but we're not allowed to replace it. Bail. */ 1138 ret = HDB_ERR_EXISTS; 1139 goto out; 1140 } 1141 1142 /* write entry into directory */ 1143 if (e == NULL) { 1144 /* didn't exist before */ 1145 rc = ldap_add_s((LDAP *) db->db, dn, mods); 1146 errfn = "ldap_add_s"; 1147 } else { 1148 /* already existed, send deltas only */ 1149 rc = ldap_modify_s((LDAP *) db->db, dn, mods); 1150 errfn = "ldap_modify_s"; 1151 } 1152 1153 if (rc == LDAP_SUCCESS) { 1154 ret = 0; 1155 } else { 1156 krb5_set_error_string(context, "%s: %s", errfn, ldap_err2string(rc)); 1157 ret = HDB_ERR_CANT_LOCK_DB; 1158 } 1159 1160 out: 1161 /* free stuff */ 1162 if (dn != NULL) { 1163 free(dn); 1164 } 1165 1166 if (msg != NULL) { 1167 ldap_msgfree(msg); 1168 } 1169 1170 if (mods != NULL) { 1171 ldap_mods_free(mods, 1); 1172 } 1173 1174 if (name != NULL) { 1175 free(name); 1176 } 1177 1178 return ret; 1179 } 1180 1181 static krb5_error_code 1182 LDAP_remove(krb5_context context, HDB * db, hdb_entry * entry) 1183 { 1184 krb5_error_code ret; 1185 LDAPMessage *msg, *e; 1186 char *dn = NULL; 1187 int rc, limit = LDAP_NO_LIMIT; 1188 1189 ret = LDAP_principal2message(context, db, entry->principal, &msg); 1190 if (ret != 0) { 1191 goto out; 1192 } 1193 1194 e = ldap_first_entry((LDAP *) db->db, msg); 1195 if (e == NULL) { 1196 ret = HDB_ERR_NOENTRY; 1197 goto out; 1198 } 1199 1200 dn = ldap_get_dn((LDAP *) db->db, e); 1201 if (dn == NULL) { 1202 ret = HDB_ERR_NOENTRY; 1203 goto out; 1204 } 1205 1206 rc = ldap_set_option((LDAP *) db->db, LDAP_OPT_SIZELIMIT, (const void *)&limit); 1207 if (rc != LDAP_SUCCESS) { 1208 krb5_set_error_string(context, "ldap_set_option: %s", ldap_err2string(rc)); 1209 ret = HDB_ERR_BADVERSION; 1210 goto out; 1211 } 1212 1213 rc = ldap_delete_s((LDAP *) db->db, dn); 1214 if (rc == LDAP_SUCCESS) { 1215 ret = 0; 1216 } else { 1217 krb5_set_error_string(context, "ldap_delete_s: %s", ldap_err2string(rc)); 1218 ret = HDB_ERR_CANT_LOCK_DB; 1219 } 1220 1221 out: 1222 if (dn != NULL) { 1223 free(dn); 1224 } 1225 1226 if (msg != NULL) { 1227 ldap_msgfree(msg); 1228 } 1229 1230 return ret; 1231 } 1232 1233 static krb5_error_code 1234 LDAP__get(krb5_context context, HDB * db, krb5_data key, krb5_data * reply) 1235 { 1236 fprintf(stderr, "LDAP__get not implemented\n"); 1237 abort(); 1238 return 0; 1239 } 1240 1241 static krb5_error_code 1242 LDAP__put(krb5_context context, HDB * db, int replace, 1243 krb5_data key, krb5_data value) 1244 { 1245 fprintf(stderr, "LDAP__put not implemented\n"); 1246 abort(); 1247 return 0; 1248 } 1249 1250 static krb5_error_code 1251 LDAP__del(krb5_context context, HDB * db, krb5_data key) 1252 { 1253 fprintf(stderr, "LDAP__del not implemented\n"); 1254 abort(); 1255 return 0; 1256 } 1257 1258 static krb5_error_code LDAP_destroy(krb5_context context, HDB * db) 1259 { 1260 krb5_error_code ret; 1261 1262 ret = hdb_clear_master_key(context, db); 1263 if (db->name != NULL) { 1264 free(db->name); 1265 } 1266 free(db); 1267 1268 return ret; 1269 } 1270 1271 krb5_error_code 1272 hdb_ldap_create(krb5_context context, HDB ** db, const char *arg) 1273 { 1274 *db = malloc(sizeof(**db)); 1275 if (*db == NULL) { 1276 krb5_set_error_string(context, "malloc: out of memory"); 1277 return ENOMEM; 1278 } 1279 1280 (*db)->db = NULL; 1281 1282 if (arg == NULL || arg[0] == '\0') { 1283 /* 1284 * if no argument specified in the configuration file 1285 * then use NULL, which tells OpenLDAP to look in 1286 * the ldap.conf file. This doesn't work for 1287 * writing entries because we don't know where to 1288 * put new principals. 1289 */ 1290 (*db)->name = NULL; 1291 } else { 1292 (*db)->name = strdup(arg); 1293 if ((*db)->name == NULL) { 1294 krb5_set_error_string(context, "strdup: out of memory"); 1295 free(*db); 1296 *db = NULL; 1297 return ENOMEM; 1298 } 1299 } 1300 1301 (*db)->master_key_set = 0; 1302 (*db)->openp = 0; 1303 (*db)->open = LDAP_open; 1304 (*db)->close = LDAP_close; 1305 (*db)->fetch = LDAP_fetch; 1306 (*db)->store = LDAP_store; 1307 (*db)->remove = LDAP_remove; 1308 (*db)->firstkey = LDAP_firstkey; 1309 (*db)->nextkey = LDAP_nextkey; 1310 (*db)->lock = LDAP_lock; 1311 (*db)->unlock = LDAP_unlock; 1312 (*db)->rename = LDAP_rename; 1313 /* can we ditch these? */ 1314 (*db)->_get = LDAP__get; 1315 (*db)->_put = LDAP__put; 1316 (*db)->_del = LDAP__del; 1317 (*db)->destroy = LDAP_destroy; 1318 1319 return 0; 1320 } 1321 1322 #endif /* OPENLDAP */ 1323