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