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