1 /* 2 * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd. 3 * Copyright (c) 2004, Andrew Bartlett. 4 * Copyright (c) 2003 - 2007, Kungliga Tekniska H�gskolan. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * 3. Neither the name of PADL Software nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "hdb_locl.h" 36 37 RCSID("$Id: hdb-ldap.c 22071 2007-11-14 20:04:50Z lha $"); 38 39 #ifdef OPENLDAP 40 41 #include <lber.h> 42 #include <ldap.h> 43 #include <sys/un.h> 44 #include <hex.h> 45 46 static krb5_error_code LDAP__connect(krb5_context context, HDB *); 47 static krb5_error_code LDAP_close(krb5_context context, HDB *); 48 49 static krb5_error_code 50 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, 51 hdb_entry_ex * ent); 52 53 static const char *default_structural_object = "account"; 54 static char *structural_object; 55 static krb5_boolean samba_forwardable; 56 57 struct hdbldapdb { 58 LDAP *h_lp; 59 int h_msgid; 60 char *h_base; 61 char *h_url; 62 char *h_createbase; 63 }; 64 65 #define HDB2LDAP(db) (((struct hdbldapdb *)(db)->hdb_db)->h_lp) 66 #define HDB2MSGID(db) (((struct hdbldapdb *)(db)->hdb_db)->h_msgid) 67 #define HDBSETMSGID(db,msgid) \ 68 do { ((struct hdbldapdb *)(db)->hdb_db)->h_msgid = msgid; } while(0) 69 #define HDB2BASE(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_base) 70 #define HDB2URL(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_url) 71 #define HDB2CREATE(db) (((struct hdbldapdb *)(db)->hdb_db)->h_createbase) 72 73 /* 74 * 75 */ 76 77 static char * krb5kdcentry_attrs[] = { 78 "cn", 79 "createTimestamp", 80 "creatorsName", 81 "krb5EncryptionType", 82 "krb5KDCFlags", 83 "krb5Key", 84 "krb5KeyVersionNumber", 85 "krb5MaxLife", 86 "krb5MaxRenew", 87 "krb5PasswordEnd", 88 "krb5PrincipalName", 89 "krb5PrincipalRealm", 90 "krb5ValidEnd", 91 "krb5ValidStart", 92 "modifiersName", 93 "modifyTimestamp", 94 "objectClass", 95 "sambaAcctFlags", 96 "sambaKickoffTime", 97 "sambaNTPassword", 98 "sambaPwdLastSet", 99 "sambaPwdMustChange", 100 "uid", 101 NULL 102 }; 103 104 static char *krb5principal_attrs[] = { 105 "cn", 106 "createTimestamp", 107 "creatorsName", 108 "krb5PrincipalName", 109 "krb5PrincipalRealm", 110 "modifiersName", 111 "modifyTimestamp", 112 "objectClass", 113 "uid", 114 NULL 115 }; 116 117 static int 118 LDAP_no_size_limit(krb5_context context, LDAP *lp) 119 { 120 int ret, limit = LDAP_NO_LIMIT; 121 122 ret = ldap_set_option(lp, LDAP_OPT_SIZELIMIT, (const void *)&limit); 123 if (ret != LDAP_SUCCESS) { 124 krb5_set_error_string(context, "ldap_set_option: %s", 125 ldap_err2string(ret)); 126 return HDB_ERR_BADVERSION; 127 } 128 return 0; 129 } 130 131 static int 132 check_ldap(krb5_context context, HDB *db, int ret) 133 { 134 switch (ret) { 135 case LDAP_SUCCESS: 136 return 0; 137 case LDAP_SERVER_DOWN: 138 LDAP_close(context, db); 139 return 1; 140 default: 141 return 1; 142 } 143 } 144 145 static krb5_error_code 146 LDAP__setmod(LDAPMod *** modlist, int modop, const char *attribute, 147 int *pIndex) 148 { 149 int cMods; 150 151 if (*modlist == NULL) { 152 *modlist = (LDAPMod **)ber_memcalloc(1, sizeof(LDAPMod *)); 153 if (*modlist == NULL) 154 return ENOMEM; 155 } 156 157 for (cMods = 0; (*modlist)[cMods] != NULL; cMods++) { 158 if ((*modlist)[cMods]->mod_op == modop && 159 strcasecmp((*modlist)[cMods]->mod_type, attribute) == 0) { 160 break; 161 } 162 } 163 164 *pIndex = cMods; 165 166 if ((*modlist)[cMods] == NULL) { 167 LDAPMod *mod; 168 169 *modlist = (LDAPMod **)ber_memrealloc(*modlist, 170 (cMods + 2) * sizeof(LDAPMod *)); 171 if (*modlist == NULL) 172 return ENOMEM; 173 174 (*modlist)[cMods] = (LDAPMod *)ber_memalloc(sizeof(LDAPMod)); 175 if ((*modlist)[cMods] == NULL) 176 return ENOMEM; 177 178 mod = (*modlist)[cMods]; 179 mod->mod_op = modop; 180 mod->mod_type = ber_strdup(attribute); 181 if (mod->mod_type == NULL) { 182 ber_memfree(mod); 183 (*modlist)[cMods] = NULL; 184 return ENOMEM; 185 } 186 187 if (modop & LDAP_MOD_BVALUES) { 188 mod->mod_bvalues = NULL; 189 } else { 190 mod->mod_values = NULL; 191 } 192 193 (*modlist)[cMods + 1] = NULL; 194 } 195 196 return 0; 197 } 198 199 static krb5_error_code 200 LDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute, 201 unsigned char *value, size_t len) 202 { 203 krb5_error_code ret; 204 int cMods, i = 0; 205 206 ret = LDAP__setmod(modlist, modop | LDAP_MOD_BVALUES, attribute, &cMods); 207 if (ret) 208 return ret; 209 210 if (value != NULL) { 211 struct berval **bv; 212 213 bv = (*modlist)[cMods]->mod_bvalues; 214 if (bv != NULL) { 215 for (i = 0; bv[i] != NULL; i++) 216 ; 217 bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv)); 218 } else 219 bv = ber_memalloc(2 * sizeof(*bv)); 220 if (bv == NULL) 221 return ENOMEM; 222 223 (*modlist)[cMods]->mod_bvalues = bv; 224 225 bv[i] = ber_memalloc(sizeof(*bv));; 226 if (bv[i] == NULL) 227 return ENOMEM; 228 229 bv[i]->bv_val = (void *)value; 230 bv[i]->bv_len = len; 231 232 bv[i + 1] = NULL; 233 } 234 235 return 0; 236 } 237 238 static krb5_error_code 239 LDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute, 240 const char *value) 241 { 242 int cMods, i = 0; 243 krb5_error_code ret; 244 245 ret = LDAP__setmod(modlist, modop, attribute, &cMods); 246 if (ret) 247 return ret; 248 249 if (value != NULL) { 250 char **bv; 251 252 bv = (*modlist)[cMods]->mod_values; 253 if (bv != NULL) { 254 for (i = 0; bv[i] != NULL; i++) 255 ; 256 bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv)); 257 } else 258 bv = ber_memalloc(2 * sizeof(*bv)); 259 if (bv == NULL) 260 return ENOMEM; 261 262 (*modlist)[cMods]->mod_values = bv; 263 264 bv[i] = ber_strdup(value); 265 if (bv[i] == NULL) 266 return ENOMEM; 267 268 bv[i + 1] = NULL; 269 } 270 271 return 0; 272 } 273 274 static krb5_error_code 275 LDAP_addmod_generalized_time(LDAPMod *** mods, int modop, 276 const char *attribute, KerberosTime * time) 277 { 278 char buf[22]; 279 struct tm *tm; 280 281 /* XXX not threadsafe */ 282 tm = gmtime(time); 283 strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm); 284 285 return LDAP_addmod(mods, modop, attribute, buf); 286 } 287 288 static krb5_error_code 289 LDAP_addmod_integer(krb5_context context, 290 LDAPMod *** mods, int modop, 291 const char *attribute, unsigned long l) 292 { 293 krb5_error_code ret; 294 char *buf; 295 296 ret = asprintf(&buf, "%ld", l); 297 if (ret < 0) { 298 krb5_set_error_string(context, "asprintf: out of memory:"); 299 return ret; 300 } 301 ret = LDAP_addmod(mods, modop, attribute, buf); 302 free (buf); 303 return ret; 304 } 305 306 static krb5_error_code 307 LDAP_get_string_value(HDB * db, LDAPMessage * entry, 308 const char *attribute, char **ptr) 309 { 310 char **vals; 311 int ret; 312 313 vals = ldap_get_values(HDB2LDAP(db), entry, (char *) attribute); 314 if (vals == NULL) { 315 *ptr = NULL; 316 return HDB_ERR_NOENTRY; 317 } 318 319 *ptr = strdup(vals[0]); 320 if (*ptr == NULL) 321 ret = ENOMEM; 322 else 323 ret = 0; 324 325 ldap_value_free(vals); 326 327 return ret; 328 } 329 330 static krb5_error_code 331 LDAP_get_integer_value(HDB * db, LDAPMessage * entry, 332 const char *attribute, int *ptr) 333 { 334 char **vals; 335 336 vals = ldap_get_values(HDB2LDAP(db), entry, (char *) attribute); 337 if (vals == NULL) 338 return HDB_ERR_NOENTRY; 339 340 *ptr = atoi(vals[0]); 341 ldap_value_free(vals); 342 return 0; 343 } 344 345 static krb5_error_code 346 LDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry, 347 const char *attribute, KerberosTime * kt) 348 { 349 char *tmp, *gentime; 350 struct tm tm; 351 int ret; 352 353 *kt = 0; 354 355 ret = LDAP_get_string_value(db, entry, attribute, &gentime); 356 if (ret) 357 return ret; 358 359 tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm); 360 if (tmp == NULL) { 361 free(gentime); 362 return HDB_ERR_NOENTRY; 363 } 364 365 free(gentime); 366 367 *kt = timegm(&tm); 368 369 return 0; 370 } 371 372 static krb5_error_code 373 LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, 374 LDAPMessage * msg, LDAPMod *** pmods) 375 { 376 krb5_error_code ret; 377 krb5_boolean is_new_entry; 378 char *tmp = NULL; 379 LDAPMod **mods = NULL; 380 hdb_entry_ex orig; 381 unsigned long oflags, nflags; 382 int i; 383 384 krb5_boolean is_samba_account = FALSE; 385 krb5_boolean is_account = FALSE; 386 krb5_boolean is_heimdal_entry = FALSE; 387 krb5_boolean is_heimdal_principal = FALSE; 388 389 char **values; 390 391 *pmods = NULL; 392 393 if (msg != NULL) { 394 395 ret = LDAP_message2entry(context, db, msg, &orig); 396 if (ret) 397 goto out; 398 399 is_new_entry = FALSE; 400 401 values = ldap_get_values(HDB2LDAP(db), msg, "objectClass"); 402 if (values) { 403 int num_objectclasses = ldap_count_values(values); 404 for (i=0; i < num_objectclasses; i++) { 405 if (strcasecmp(values[i], "sambaSamAccount") == 0) { 406 is_samba_account = TRUE; 407 } else if (strcasecmp(values[i], structural_object) == 0) { 408 is_account = TRUE; 409 } else if (strcasecmp(values[i], "krb5Principal") == 0) { 410 is_heimdal_principal = TRUE; 411 } else if (strcasecmp(values[i], "krb5KDCEntry") == 0) { 412 is_heimdal_entry = TRUE; 413 } 414 } 415 ldap_value_free(values); 416 } 417 418 /* 419 * If this is just a "account" entry and no other objectclass 420 * is hanging on this entry, it's really a new entry. 421 */ 422 if (is_samba_account == FALSE && is_heimdal_principal == FALSE && 423 is_heimdal_entry == FALSE) { 424 if (is_account == TRUE) { 425 is_new_entry = TRUE; 426 } else { 427 ret = HDB_ERR_NOENTRY; 428 goto out; 429 } 430 } 431 } else 432 is_new_entry = TRUE; 433 434 if (is_new_entry) { 435 436 /* to make it perfectly obvious we're depending on 437 * orig being intiialized to zero */ 438 memset(&orig, 0, sizeof(orig)); 439 440 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top"); 441 if (ret) 442 goto out; 443 444 /* account is the structural object class */ 445 if (is_account == FALSE) { 446 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", 447 structural_object); 448 is_account = TRUE; 449 if (ret) 450 goto out; 451 } 452 453 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5Principal"); 454 is_heimdal_principal = TRUE; 455 if (ret) 456 goto out; 457 458 ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5KDCEntry"); 459 is_heimdal_entry = TRUE; 460 if (ret) 461 goto out; 462 } 463 464 if (is_new_entry || 465 krb5_principal_compare(context, ent->entry.principal, orig.entry.principal) 466 == FALSE) 467 { 468 if (is_heimdal_principal || is_heimdal_entry) { 469 470 ret = krb5_unparse_name(context, ent->entry.principal, &tmp); 471 if (ret) 472 goto out; 473 474 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, 475 "krb5PrincipalName", tmp); 476 if (ret) { 477 free(tmp); 478 goto out; 479 } 480 free(tmp); 481 } 482 483 if (is_account || is_samba_account) { 484 ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp); 485 if (ret) 486 goto out; 487 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp); 488 if (ret) { 489 free(tmp); 490 goto out; 491 } 492 free(tmp); 493 } 494 } 495 496 if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) { 497 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 498 "krb5KeyVersionNumber", 499 ent->entry.kvno); 500 if (ret) 501 goto out; 502 } 503 504 if (is_heimdal_entry && ent->entry.valid_start) { 505 if (orig.entry.valid_end == NULL 506 || (*(ent->entry.valid_start) != *(orig.entry.valid_start))) { 507 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 508 "krb5ValidStart", 509 ent->entry.valid_start); 510 if (ret) 511 goto out; 512 } 513 } 514 515 if (ent->entry.valid_end) { 516 if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) { 517 if (is_heimdal_entry) { 518 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 519 "krb5ValidEnd", 520 ent->entry.valid_end); 521 if (ret) 522 goto out; 523 } 524 if (is_samba_account) { 525 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 526 "sambaKickoffTime", 527 *(ent->entry.valid_end)); 528 if (ret) 529 goto out; 530 } 531 } 532 } 533 534 if (ent->entry.pw_end) { 535 if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) { 536 if (is_heimdal_entry) { 537 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 538 "krb5PasswordEnd", 539 ent->entry.pw_end); 540 if (ret) 541 goto out; 542 } 543 544 if (is_samba_account) { 545 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 546 "sambaPwdMustChange", 547 *(ent->entry.pw_end)); 548 if (ret) 549 goto out; 550 } 551 } 552 } 553 554 555 #if 0 /* we we have last_pw_change */ 556 if (is_samba_account && ent->entry.last_pw_change) { 557 if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) { 558 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 559 "sambaPwdLastSet", 560 *(ent->entry.last_pw_change)); 561 if (ret) 562 goto out; 563 } 564 } 565 #endif 566 567 if (is_heimdal_entry && ent->entry.max_life) { 568 if (orig.entry.max_life == NULL 569 || (*(ent->entry.max_life) != *(orig.entry.max_life))) { 570 571 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 572 "krb5MaxLife", 573 *(ent->entry.max_life)); 574 if (ret) 575 goto out; 576 } 577 } 578 579 if (is_heimdal_entry && ent->entry.max_renew) { 580 if (orig.entry.max_renew == NULL 581 || (*(ent->entry.max_renew) != *(orig.entry.max_renew))) { 582 583 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 584 "krb5MaxRenew", 585 *(ent->entry.max_renew)); 586 if (ret) 587 goto out; 588 } 589 } 590 591 oflags = HDBFlags2int(orig.entry.flags); 592 nflags = HDBFlags2int(ent->entry.flags); 593 594 if (is_heimdal_entry && oflags != nflags) { 595 596 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 597 "krb5KDCFlags", 598 nflags); 599 if (ret) 600 goto out; 601 } 602 603 /* Remove keys if they exists, and then replace keys. */ 604 if (!is_new_entry && orig.entry.keys.len > 0) { 605 values = ldap_get_values(HDB2LDAP(db), msg, "krb5Key"); 606 if (values) { 607 ldap_value_free(values); 608 609 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL); 610 if (ret) 611 goto out; 612 } 613 } 614 615 for (i = 0; i < ent->entry.keys.len; i++) { 616 617 if (is_samba_account 618 && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { 619 char *ntHexPassword; 620 char *nt; 621 622 /* the key might have been 'sealed', but samba passwords 623 are clear in the directory */ 624 ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]); 625 if (ret) 626 goto out; 627 628 nt = ent->entry.keys.val[i].key.keyvalue.data; 629 /* store in ntPassword, not krb5key */ 630 ret = hex_encode(nt, 16, &ntHexPassword); 631 if (ret < 0) { 632 krb5_set_error_string(context, "hdb-ldap: failed to " 633 "hex encode key"); 634 ret = ENOMEM; 635 goto out; 636 } 637 ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "sambaNTPassword", 638 ntHexPassword); 639 free(ntHexPassword); 640 if (ret) 641 goto out; 642 643 /* have to kill the LM passwod if it exists */ 644 values = ldap_get_values(HDB2LDAP(db), msg, "sambaLMPassword"); 645 if (values) { 646 ldap_value_free(values); 647 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, 648 "sambaLMPassword", NULL); 649 if (ret) 650 goto out; 651 } 652 653 } else if (is_heimdal_entry) { 654 unsigned char *buf; 655 size_t len, buf_size; 656 657 ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret); 658 if (ret) 659 goto out; 660 if(buf_size != len) 661 krb5_abortx(context, "internal error in ASN.1 encoder"); 662 663 /* addmod_len _owns_ the key, doesn't need to copy it */ 664 ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len); 665 if (ret) 666 goto out; 667 } 668 } 669 670 if (ent->entry.etypes) { 671 int add_krb5EncryptionType = 0; 672 673 /* 674 * Only add/modify krb5EncryptionType if it's a new heimdal 675 * entry or krb5EncryptionType already exists on the entry. 676 */ 677 678 if (!is_new_entry) { 679 values = ldap_get_values(HDB2LDAP(db), msg, "krb5EncryptionType"); 680 if (values) { 681 ldap_value_free(values); 682 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType", 683 NULL); 684 if (ret) 685 goto out; 686 add_krb5EncryptionType = 1; 687 } 688 } else if (is_heimdal_entry) 689 add_krb5EncryptionType = 1; 690 691 if (add_krb5EncryptionType) { 692 for (i = 0; i < ent->entry.etypes->len; i++) { 693 if (is_samba_account && 694 ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) 695 { 696 ; 697 } else if (is_heimdal_entry) { 698 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD, 699 "krb5EncryptionType", 700 ent->entry.etypes->val[i]); 701 if (ret) 702 goto out; 703 } 704 } 705 } 706 } 707 708 /* for clarity */ 709 ret = 0; 710 711 out: 712 713 if (ret == 0) 714 *pmods = mods; 715 else if (mods != NULL) { 716 ldap_mods_free(mods, 1); 717 *pmods = NULL; 718 } 719 720 if (msg) 721 hdb_free_entry(context, &orig); 722 723 return ret; 724 } 725 726 static krb5_error_code 727 LDAP_dn2principal(krb5_context context, HDB * db, const char *dn, 728 krb5_principal * principal) 729 { 730 krb5_error_code ret; 731 int rc; 732 const char *filter = "(objectClass=krb5Principal)"; 733 char **values; 734 LDAPMessage *res = NULL, *e; 735 736 ret = LDAP_no_size_limit(context, HDB2LDAP(db)); 737 if (ret) 738 goto out; 739 740 rc = ldap_search_s(HDB2LDAP(db), dn, LDAP_SCOPE_SUBTREE, 741 filter, krb5principal_attrs, 742 0, &res); 743 if (check_ldap(context, db, rc)) { 744 krb5_set_error_string(context, "ldap_search_s: filter: %s error: %s", 745 filter, ldap_err2string(rc)); 746 ret = HDB_ERR_NOENTRY; 747 goto out; 748 } 749 750 e = ldap_first_entry(HDB2LDAP(db), res); 751 if (e == NULL) { 752 ret = HDB_ERR_NOENTRY; 753 goto out; 754 } 755 756 values = ldap_get_values(HDB2LDAP(db), e, "krb5PrincipalName"); 757 if (values == NULL) { 758 ret = HDB_ERR_NOENTRY; 759 goto out; 760 } 761 762 ret = krb5_parse_name(context, values[0], principal); 763 ldap_value_free(values); 764 765 out: 766 if (res) 767 ldap_msgfree(res); 768 769 return ret; 770 } 771 772 static krb5_error_code 773 LDAP__lookup_princ(krb5_context context, 774 HDB *db, 775 const char *princname, 776 const char *userid, 777 LDAPMessage **msg) 778 { 779 krb5_error_code ret; 780 int rc; 781 char *filter = NULL; 782 783 ret = LDAP__connect(context, db); 784 if (ret) 785 return ret; 786 787 rc = asprintf(&filter, 788 "(&(objectClass=krb5Principal)(krb5PrincipalName=%s))", 789 princname); 790 if (rc < 0) { 791 krb5_set_error_string(context, "asprintf: out of memory"); 792 ret = ENOMEM; 793 goto out; 794 } 795 796 ret = LDAP_no_size_limit(context, HDB2LDAP(db)); 797 if (ret) 798 goto out; 799 800 rc = ldap_search_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE, filter, 801 krb5kdcentry_attrs, 0, msg); 802 if (check_ldap(context, db, rc)) { 803 krb5_set_error_string(context, "ldap_search_s: filter: %s - error: %s", 804 filter, ldap_err2string(rc)); 805 ret = HDB_ERR_NOENTRY; 806 goto out; 807 } 808 809 if (userid && ldap_count_entries(HDB2LDAP(db), *msg) == 0) { 810 free(filter); 811 filter = NULL; 812 ldap_msgfree(*msg); 813 *msg = NULL; 814 815 rc = asprintf(&filter, 816 "(&(|(objectClass=sambaSamAccount)(objectClass=%s))(uid=%s))", 817 structural_object, userid); 818 if (rc < 0) { 819 krb5_set_error_string(context, "asprintf: out of memory"); 820 ret = ENOMEM; 821 goto out; 822 } 823 824 ret = LDAP_no_size_limit(context, HDB2LDAP(db)); 825 if (ret) 826 goto out; 827 828 rc = ldap_search_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE, 829 filter, krb5kdcentry_attrs, 0, msg); 830 if (check_ldap(context, db, rc)) { 831 krb5_set_error_string(context, 832 "ldap_search_s: filter: %s error: %s", 833 filter, ldap_err2string(rc)); 834 ret = HDB_ERR_NOENTRY; 835 goto out; 836 } 837 } 838 839 ret = 0; 840 841 out: 842 if (filter) 843 free(filter); 844 845 return ret; 846 } 847 848 static krb5_error_code 849 LDAP_principal2message(krb5_context context, HDB * db, 850 krb5_const_principal princ, LDAPMessage ** msg) 851 { 852 char *name, *name_short = NULL; 853 krb5_error_code ret; 854 krb5_realm *r, *r0; 855 856 *msg = NULL; 857 858 ret = krb5_unparse_name(context, princ, &name); 859 if (ret) 860 return ret; 861 862 ret = krb5_get_default_realms(context, &r0); 863 if(ret) { 864 free(name); 865 return ret; 866 } 867 for (r = r0; *r != NULL; r++) { 868 if(strcmp(krb5_principal_get_realm(context, princ), *r) == 0) { 869 ret = krb5_unparse_name_short(context, princ, &name_short); 870 if (ret) { 871 krb5_free_host_realm(context, r0); 872 free(name); 873 return ret; 874 } 875 break; 876 } 877 } 878 krb5_free_host_realm(context, r0); 879 880 ret = LDAP__lookup_princ(context, db, name, name_short, msg); 881 free(name); 882 free(name_short); 883 884 return ret; 885 } 886 887 /* 888 * Construct an hdb_entry from a directory entry. 889 */ 890 static krb5_error_code 891 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, 892 hdb_entry_ex * ent) 893 { 894 char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL; 895 char *samba_acct_flags = NULL; 896 unsigned long tmp; 897 struct berval **keys; 898 char **values; 899 int tmp_time, i, ret, have_arcfour = 0; 900 901 memset(ent, 0, sizeof(*ent)); 902 ent->entry.flags = int2HDBFlags(0); 903 904 ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name); 905 if (ret == 0) { 906 ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal); 907 if (ret) 908 goto out; 909 } else { 910 ret = LDAP_get_string_value(db, msg, "uid", 911 &unparsed_name); 912 if (ret == 0) { 913 ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal); 914 if (ret) 915 goto out; 916 } else { 917 krb5_set_error_string(context, "hdb-ldap: ldap entry missing" 918 "principal name"); 919 return HDB_ERR_NOENTRY; 920 } 921 } 922 923 { 924 int integer; 925 ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber", 926 &integer); 927 if (ret) 928 ent->entry.kvno = 0; 929 else 930 ent->entry.kvno = integer; 931 } 932 933 keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key"); 934 if (keys != NULL) { 935 int i; 936 size_t l; 937 938 ent->entry.keys.len = ldap_count_values_len(keys); 939 ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key)); 940 if (ent->entry.keys.val == NULL) { 941 krb5_set_error_string(context, "calloc: out of memory"); 942 ret = ENOMEM; 943 goto out; 944 } 945 for (i = 0; i < ent->entry.keys.len; i++) { 946 decode_Key((unsigned char *) keys[i]->bv_val, 947 (size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l); 948 } 949 ber_bvecfree(keys); 950 } else { 951 #if 1 952 /* 953 * This violates the ASN1 but it allows a principal to 954 * be related to a general directory entry without creating 955 * the keys. Hopefully it's OK. 956 */ 957 ent->entry.keys.len = 0; 958 ent->entry.keys.val = NULL; 959 #else 960 ret = HDB_ERR_NOENTRY; 961 goto out; 962 #endif 963 } 964 965 values = ldap_get_values(HDB2LDAP(db), msg, "krb5EncryptionType"); 966 if (values != NULL) { 967 int i; 968 969 ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes))); 970 if (ent->entry.etypes == NULL) { 971 krb5_set_error_string(context, "malloc: out of memory"); 972 ret = ENOMEM; 973 goto out; 974 } 975 ent->entry.etypes->len = ldap_count_values(values); 976 ent->entry.etypes->val = calloc(ent->entry.etypes->len, sizeof(int)); 977 if (ent->entry.etypes->val == NULL) { 978 krb5_set_error_string(context, "malloc: out of memory"); 979 ret = ENOMEM; 980 goto out; 981 } 982 for (i = 0; i < ent->entry.etypes->len; i++) { 983 ent->entry.etypes->val[i] = atoi(values[i]); 984 } 985 ldap_value_free(values); 986 } 987 988 for (i = 0; i < ent->entry.keys.len; i++) { 989 if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { 990 have_arcfour = 1; 991 break; 992 } 993 } 994 995 /* manually construct the NT (type 23) key */ 996 ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN); 997 if (ret == 0 && have_arcfour == 0) { 998 unsigned *etypes; 999 Key *keys; 1000 int i; 1001 1002 keys = realloc(ent->entry.keys.val, 1003 (ent->entry.keys.len + 1) * sizeof(ent->entry.keys.val[0])); 1004 if (keys == NULL) { 1005 free(ntPasswordIN); 1006 krb5_set_error_string(context, "malloc: out of memory"); 1007 ret = ENOMEM; 1008 goto out; 1009 } 1010 ent->entry.keys.val = keys; 1011 memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key)); 1012 ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5; 1013 ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16); 1014 if (ret) { 1015 krb5_set_error_string(context, "malloc: out of memory"); 1016 free(ntPasswordIN); 1017 ret = ENOMEM; 1018 goto out; 1019 } 1020 ret = hex_decode(ntPasswordIN, 1021 ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16); 1022 ent->entry.keys.len++; 1023 1024 if (ent->entry.etypes == NULL) { 1025 ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes))); 1026 if (ent->entry.etypes == NULL) { 1027 krb5_set_error_string(context, "malloc: out of memory"); 1028 ret = ENOMEM; 1029 goto out; 1030 } 1031 ent->entry.etypes->val = NULL; 1032 ent->entry.etypes->len = 0; 1033 } 1034 1035 for (i = 0; i < ent->entry.etypes->len; i++) 1036 if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5) 1037 break; 1038 /* If there is no ARCFOUR enctype, add one */ 1039 if (i == ent->entry.etypes->len) { 1040 etypes = realloc(ent->entry.etypes->val, 1041 (ent->entry.etypes->len + 1) * 1042 sizeof(ent->entry.etypes->val[0])); 1043 if (etypes == NULL) { 1044 krb5_set_error_string(context, "malloc: out of memory"); 1045 ret = ENOMEM; 1046 goto out; 1047 } 1048 ent->entry.etypes->val = etypes; 1049 ent->entry.etypes->val[ent->entry.etypes->len] = 1050 ETYPE_ARCFOUR_HMAC_MD5; 1051 ent->entry.etypes->len++; 1052 } 1053 } 1054 1055 ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp", 1056 &ent->entry.created_by.time); 1057 if (ret) 1058 ent->entry.created_by.time = time(NULL); 1059 1060 ent->entry.created_by.principal = NULL; 1061 1062 ret = LDAP_get_string_value(db, msg, "creatorsName", &dn); 1063 if (ret == 0) { 1064 if (LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal) 1065 != 0) { 1066 ent->entry.created_by.principal = NULL; 1067 } 1068 free(dn); 1069 } 1070 1071 ent->entry.modified_by = (Event *) malloc(sizeof(Event)); 1072 if (ent->entry.modified_by == NULL) { 1073 krb5_set_error_string(context, "malloc: out of memory"); 1074 ret = ENOMEM; 1075 goto out; 1076 } 1077 ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp", 1078 &ent->entry.modified_by->time); 1079 if (ret == 0) { 1080 ret = LDAP_get_string_value(db, msg, "modifiersName", &dn); 1081 if (LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal)) 1082 ent->entry.modified_by->principal = NULL; 1083 free(dn); 1084 } else { 1085 free(ent->entry.modified_by); 1086 ent->entry.modified_by = NULL; 1087 } 1088 1089 ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start)); 1090 if (ent->entry.valid_start == NULL) { 1091 krb5_set_error_string(context, "malloc: out of memory"); 1092 ret = ENOMEM; 1093 goto out; 1094 } 1095 ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart", 1096 ent->entry.valid_start); 1097 if (ret) { 1098 /* OPTIONAL */ 1099 free(ent->entry.valid_start); 1100 ent->entry.valid_start = NULL; 1101 } 1102 1103 ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end)); 1104 if (ent->entry.valid_end == NULL) { 1105 krb5_set_error_string(context, "malloc: out of memory"); 1106 ret = ENOMEM; 1107 goto out; 1108 } 1109 ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd", 1110 ent->entry.valid_end); 1111 if (ret) { 1112 /* OPTIONAL */ 1113 free(ent->entry.valid_end); 1114 ent->entry.valid_end = NULL; 1115 } 1116 1117 ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time); 1118 if (ret == 0) { 1119 if (ent->entry.valid_end == NULL) { 1120 ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end)); 1121 if (ent->entry.valid_end == NULL) { 1122 krb5_set_error_string(context, "malloc: out of memory"); 1123 ret = ENOMEM; 1124 goto out; 1125 } 1126 } 1127 *ent->entry.valid_end = tmp_time; 1128 } 1129 1130 ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); 1131 if (ent->entry.pw_end == NULL) { 1132 krb5_set_error_string(context, "malloc: out of memory"); 1133 ret = ENOMEM; 1134 goto out; 1135 } 1136 ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd", 1137 ent->entry.pw_end); 1138 if (ret) { 1139 /* OPTIONAL */ 1140 free(ent->entry.pw_end); 1141 ent->entry.pw_end = NULL; 1142 } 1143 1144 ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time); 1145 if (ret == 0) { 1146 if (ent->entry.pw_end == NULL) { 1147 ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); 1148 if (ent->entry.pw_end == NULL) { 1149 krb5_set_error_string(context, "malloc: out of memory"); 1150 ret = ENOMEM; 1151 goto out; 1152 } 1153 } 1154 *ent->entry.pw_end = tmp_time; 1155 } 1156 1157 /* OPTIONAL */ 1158 ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time); 1159 if (ret == 0) 1160 hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time); 1161 1162 { 1163 int max_life; 1164 1165 ent->entry.max_life = malloc(sizeof(*ent->entry.max_life)); 1166 if (ent->entry.max_life == NULL) { 1167 krb5_set_error_string(context, "malloc: out of memory"); 1168 ret = ENOMEM; 1169 goto out; 1170 } 1171 ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life); 1172 if (ret) { 1173 free(ent->entry.max_life); 1174 ent->entry.max_life = NULL; 1175 } else 1176 *ent->entry.max_life = max_life; 1177 } 1178 1179 { 1180 int max_renew; 1181 1182 ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew)); 1183 if (ent->entry.max_renew == NULL) { 1184 krb5_set_error_string(context, "malloc: out of memory"); 1185 ret = ENOMEM; 1186 goto out; 1187 } 1188 ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew); 1189 if (ret) { 1190 free(ent->entry.max_renew); 1191 ent->entry.max_renew = NULL; 1192 } else 1193 *ent->entry.max_renew = max_renew; 1194 } 1195 1196 values = ldap_get_values(HDB2LDAP(db), msg, "krb5KDCFlags"); 1197 if (values != NULL) { 1198 errno = 0; 1199 tmp = strtoul(values[0], (char **) NULL, 10); 1200 if (tmp == ULONG_MAX && errno == ERANGE) { 1201 krb5_set_error_string(context, "strtoul: could not convert flag"); 1202 ret = ERANGE; 1203 goto out; 1204 } 1205 } else { 1206 tmp = 0; 1207 } 1208 1209 ent->entry.flags = int2HDBFlags(tmp); 1210 1211 /* Try and find Samba flags to put into the mix */ 1212 ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags); 1213 if (ret == 0) { 1214 /* parse the [UXW...] string: 1215 1216 'N' No password 1217 'D' Disabled 1218 'H' Homedir required 1219 'T' Temp account. 1220 'U' User account (normal) 1221 'M' MNS logon user account - what is this ? 1222 'W' Workstation account 1223 'S' Server account 1224 'L' Locked account 1225 'X' No Xpiry on password 1226 'I' Interdomain trust account 1227 1228 */ 1229 1230 int i; 1231 int flags_len = strlen(samba_acct_flags); 1232 1233 if (flags_len < 2) 1234 goto out2; 1235 1236 if (samba_acct_flags[0] != '[' 1237 || samba_acct_flags[flags_len - 1] != ']') 1238 goto out2; 1239 1240 /* Allow forwarding */ 1241 if (samba_forwardable) 1242 ent->entry.flags.forwardable = TRUE; 1243 1244 for (i=0; i < flags_len; i++) { 1245 switch (samba_acct_flags[i]) { 1246 case ' ': 1247 case '[': 1248 case ']': 1249 break; 1250 case 'N': 1251 /* how to handle no password in kerberos? */ 1252 break; 1253 case 'D': 1254 ent->entry.flags.invalid = TRUE; 1255 break; 1256 case 'H': 1257 break; 1258 case 'T': 1259 /* temp duplicate */ 1260 ent->entry.flags.invalid = TRUE; 1261 break; 1262 case 'U': 1263 ent->entry.flags.client = TRUE; 1264 break; 1265 case 'M': 1266 break; 1267 case 'W': 1268 case 'S': 1269 ent->entry.flags.server = TRUE; 1270 ent->entry.flags.client = TRUE; 1271 break; 1272 case 'L': 1273 ent->entry.flags.invalid = TRUE; 1274 break; 1275 case 'X': 1276 if (ent->entry.pw_end) { 1277 free(ent->entry.pw_end); 1278 ent->entry.pw_end = NULL; 1279 } 1280 break; 1281 case 'I': 1282 ent->entry.flags.server = TRUE; 1283 ent->entry.flags.client = TRUE; 1284 break; 1285 } 1286 } 1287 out2: 1288 free(samba_acct_flags); 1289 } 1290 1291 ret = 0; 1292 1293 out: 1294 if (unparsed_name) 1295 free(unparsed_name); 1296 1297 if (ret) 1298 hdb_free_entry(context, ent); 1299 1300 return ret; 1301 } 1302 1303 static krb5_error_code 1304 LDAP_close(krb5_context context, HDB * db) 1305 { 1306 if (HDB2LDAP(db)) { 1307 ldap_unbind_ext(HDB2LDAP(db), NULL, NULL); 1308 ((struct hdbldapdb *)db->hdb_db)->h_lp = NULL; 1309 } 1310 1311 return 0; 1312 } 1313 1314 static krb5_error_code 1315 LDAP_lock(krb5_context context, HDB * db, int operation) 1316 { 1317 return 0; 1318 } 1319 1320 static krb5_error_code 1321 LDAP_unlock(krb5_context context, HDB * db) 1322 { 1323 return 0; 1324 } 1325 1326 static krb5_error_code 1327 LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry) 1328 { 1329 int msgid, rc, parserc; 1330 krb5_error_code ret; 1331 LDAPMessage *e; 1332 1333 msgid = HDB2MSGID(db); 1334 if (msgid < 0) 1335 return HDB_ERR_NOENTRY; 1336 1337 do { 1338 rc = ldap_result(HDB2LDAP(db), msgid, LDAP_MSG_ONE, NULL, &e); 1339 switch (rc) { 1340 case LDAP_RES_SEARCH_REFERENCE: 1341 ldap_msgfree(e); 1342 ret = 0; 1343 break; 1344 case LDAP_RES_SEARCH_ENTRY: 1345 /* We have an entry. Parse it. */ 1346 ret = LDAP_message2entry(context, db, e, entry); 1347 ldap_msgfree(e); 1348 break; 1349 case LDAP_RES_SEARCH_RESULT: 1350 /* We're probably at the end of the results. If not, abandon. */ 1351 parserc = 1352 ldap_parse_result(HDB2LDAP(db), e, NULL, NULL, NULL, 1353 NULL, NULL, 1); 1354 if (parserc != LDAP_SUCCESS 1355 && parserc != LDAP_MORE_RESULTS_TO_RETURN) { 1356 krb5_set_error_string(context, "ldap_parse_result: %s", 1357 ldap_err2string(parserc)); 1358 ldap_abandon(HDB2LDAP(db), msgid); 1359 } 1360 ret = HDB_ERR_NOENTRY; 1361 HDBSETMSGID(db, -1); 1362 break; 1363 case LDAP_SERVER_DOWN: 1364 ldap_msgfree(e); 1365 LDAP_close(context, db); 1366 HDBSETMSGID(db, -1); 1367 ret = ENETDOWN; 1368 break; 1369 default: 1370 /* Some unspecified error (timeout?). Abandon. */ 1371 ldap_msgfree(e); 1372 ldap_abandon(HDB2LDAP(db), msgid); 1373 ret = HDB_ERR_NOENTRY; 1374 HDBSETMSGID(db, -1); 1375 break; 1376 } 1377 } while (rc == LDAP_RES_SEARCH_REFERENCE); 1378 1379 if (ret == 0) { 1380 if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 1381 ret = hdb_unseal_keys(context, db, &entry->entry); 1382 if (ret) 1383 hdb_free_entry(context, entry); 1384 } 1385 } 1386 1387 return ret; 1388 } 1389 1390 static krb5_error_code 1391 LDAP_firstkey(krb5_context context, HDB *db, unsigned flags, 1392 hdb_entry_ex *entry) 1393 { 1394 krb5_error_code ret; 1395 int msgid; 1396 1397 ret = LDAP__connect(context, db); 1398 if (ret) 1399 return ret; 1400 1401 ret = LDAP_no_size_limit(context, HDB2LDAP(db)); 1402 if (ret) 1403 return ret; 1404 1405 msgid = ldap_search(HDB2LDAP(db), HDB2BASE(db), 1406 LDAP_SCOPE_SUBTREE, 1407 "(|(objectClass=krb5Principal)(objectClass=sambaSamAccount))", 1408 krb5kdcentry_attrs, 0); 1409 if (msgid < 0) 1410 return HDB_ERR_NOENTRY; 1411 1412 HDBSETMSGID(db, msgid); 1413 1414 return LDAP_seq(context, db, flags, entry); 1415 } 1416 1417 static krb5_error_code 1418 LDAP_nextkey(krb5_context context, HDB * db, unsigned flags, 1419 hdb_entry_ex * entry) 1420 { 1421 return LDAP_seq(context, db, flags, entry); 1422 } 1423 1424 static krb5_error_code 1425 LDAP__connect(krb5_context context, HDB * db) 1426 { 1427 int rc, version = LDAP_VERSION3; 1428 /* 1429 * Empty credentials to do a SASL bind with LDAP. Note that empty 1430 * different from NULL credentials. If you provide NULL 1431 * credentials instead of empty credentials you will get a SASL 1432 * bind in progress message. 1433 */ 1434 struct berval bv = { 0, "" }; 1435 1436 if (HDB2LDAP(db)) { 1437 /* connection has been opened. ping server. */ 1438 struct sockaddr_un addr; 1439 socklen_t len = sizeof(addr); 1440 int sd; 1441 1442 if (ldap_get_option(HDB2LDAP(db), LDAP_OPT_DESC, &sd) == 0 && 1443 getpeername(sd, (struct sockaddr *) &addr, &len) < 0) { 1444 /* the other end has died. reopen. */ 1445 LDAP_close(context, db); 1446 } 1447 } 1448 1449 if (HDB2LDAP(db) != NULL) /* server is UP */ 1450 return 0; 1451 1452 rc = ldap_initialize(&((struct hdbldapdb *)db->hdb_db)->h_lp, HDB2URL(db)); 1453 if (rc != LDAP_SUCCESS) { 1454 krb5_set_error_string(context, "ldap_initialize: %s", 1455 ldap_err2string(rc)); 1456 return HDB_ERR_NOENTRY; 1457 } 1458 1459 rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_PROTOCOL_VERSION, 1460 (const void *)&version); 1461 if (rc != LDAP_SUCCESS) { 1462 krb5_set_error_string(context, "ldap_set_option: %s", 1463 ldap_err2string(rc)); 1464 LDAP_close(context, db); 1465 return HDB_ERR_BADVERSION; 1466 } 1467 1468 rc = ldap_sasl_bind_s(HDB2LDAP(db), NULL, "EXTERNAL", &bv, 1469 NULL, NULL, NULL); 1470 if (rc != LDAP_SUCCESS) { 1471 krb5_set_error_string(context, "ldap_sasl_bind_s: %s", 1472 ldap_err2string(rc)); 1473 LDAP_close(context, db); 1474 return HDB_ERR_BADVERSION; 1475 } 1476 1477 return 0; 1478 } 1479 1480 static krb5_error_code 1481 LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode) 1482 { 1483 /* Not the right place for this. */ 1484 #ifdef HAVE_SIGACTION 1485 struct sigaction sa; 1486 1487 sa.sa_flags = 0; 1488 sa.sa_handler = SIG_IGN; 1489 sigemptyset(&sa.sa_mask); 1490 1491 sigaction(SIGPIPE, &sa, NULL); 1492 #else 1493 signal(SIGPIPE, SIG_IGN); 1494 #endif /* HAVE_SIGACTION */ 1495 1496 return LDAP__connect(context, db); 1497 } 1498 1499 static krb5_error_code 1500 LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal, 1501 unsigned flags, hdb_entry_ex * entry) 1502 { 1503 LDAPMessage *msg, *e; 1504 krb5_error_code ret; 1505 1506 ret = LDAP_principal2message(context, db, principal, &msg); 1507 if (ret) 1508 return ret; 1509 1510 e = ldap_first_entry(HDB2LDAP(db), msg); 1511 if (e == NULL) { 1512 ret = HDB_ERR_NOENTRY; 1513 goto out; 1514 } 1515 1516 ret = LDAP_message2entry(context, db, e, entry); 1517 if (ret == 0) { 1518 if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 1519 ret = hdb_unseal_keys(context, db, &entry->entry); 1520 if (ret) 1521 hdb_free_entry(context, entry); 1522 } 1523 } 1524 1525 out: 1526 ldap_msgfree(msg); 1527 1528 return ret; 1529 } 1530 1531 static krb5_error_code 1532 LDAP_store(krb5_context context, HDB * db, unsigned flags, 1533 hdb_entry_ex * entry) 1534 { 1535 LDAPMod **mods = NULL; 1536 krb5_error_code ret; 1537 const char *errfn; 1538 int rc; 1539 LDAPMessage *msg = NULL, *e = NULL; 1540 char *dn = NULL, *name = NULL; 1541 1542 ret = LDAP_principal2message(context, db, entry->entry.principal, &msg); 1543 if (ret == 0) 1544 e = ldap_first_entry(HDB2LDAP(db), msg); 1545 1546 ret = krb5_unparse_name(context, entry->entry.principal, &name); 1547 if (ret) { 1548 free(name); 1549 return ret; 1550 } 1551 1552 ret = hdb_seal_keys(context, db, &entry->entry); 1553 if (ret) 1554 goto out; 1555 1556 /* turn new entry into LDAPMod array */ 1557 ret = LDAP_entry2mods(context, db, entry, e, &mods); 1558 if (ret) 1559 goto out; 1560 1561 if (e == NULL) { 1562 ret = asprintf(&dn, "krb5PrincipalName=%s,%s", name, HDB2CREATE(db)); 1563 if (ret < 0) { 1564 krb5_set_error_string(context, "asprintf: out of memory"); 1565 ret = ENOMEM; 1566 goto out; 1567 } 1568 } else if (flags & HDB_F_REPLACE) { 1569 /* Entry exists, and we're allowed to replace it. */ 1570 dn = ldap_get_dn(HDB2LDAP(db), e); 1571 } else { 1572 /* Entry exists, but we're not allowed to replace it. Bail. */ 1573 ret = HDB_ERR_EXISTS; 1574 goto out; 1575 } 1576 1577 /* write entry into directory */ 1578 if (e == NULL) { 1579 /* didn't exist before */ 1580 rc = ldap_add_s(HDB2LDAP(db), dn, mods); 1581 errfn = "ldap_add_s"; 1582 } else { 1583 /* already existed, send deltas only */ 1584 rc = ldap_modify_s(HDB2LDAP(db), dn, mods); 1585 errfn = "ldap_modify_s"; 1586 } 1587 1588 if (check_ldap(context, db, rc)) { 1589 char *ld_error = NULL; 1590 ldap_get_option(HDB2LDAP(db), LDAP_OPT_ERROR_STRING, 1591 &ld_error); 1592 krb5_set_error_string(context, "%s: %s (DN=%s) %s: %s", 1593 errfn, name, dn, ldap_err2string(rc), ld_error); 1594 ret = HDB_ERR_CANT_LOCK_DB; 1595 } else 1596 ret = 0; 1597 1598 out: 1599 /* free stuff */ 1600 if (dn) 1601 free(dn); 1602 if (msg) 1603 ldap_msgfree(msg); 1604 if (mods) 1605 ldap_mods_free(mods, 1); 1606 if (name) 1607 free(name); 1608 1609 return ret; 1610 } 1611 1612 static krb5_error_code 1613 LDAP_remove(krb5_context context, HDB *db, krb5_const_principal principal) 1614 { 1615 krb5_error_code ret; 1616 LDAPMessage *msg, *e; 1617 char *dn = NULL; 1618 int rc, limit = LDAP_NO_LIMIT; 1619 1620 ret = LDAP_principal2message(context, db, principal, &msg); 1621 if (ret) 1622 goto out; 1623 1624 e = ldap_first_entry(HDB2LDAP(db), msg); 1625 if (e == NULL) { 1626 ret = HDB_ERR_NOENTRY; 1627 goto out; 1628 } 1629 1630 dn = ldap_get_dn(HDB2LDAP(db), e); 1631 if (dn == NULL) { 1632 ret = HDB_ERR_NOENTRY; 1633 goto out; 1634 } 1635 1636 rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_SIZELIMIT, (const void *)&limit); 1637 if (rc != LDAP_SUCCESS) { 1638 krb5_set_error_string(context, "ldap_set_option: %s", 1639 ldap_err2string(rc)); 1640 ret = HDB_ERR_BADVERSION; 1641 goto out; 1642 } 1643 1644 rc = ldap_delete_s(HDB2LDAP(db), dn); 1645 if (check_ldap(context, db, rc)) { 1646 krb5_set_error_string(context, "ldap_delete_s: %s", 1647 ldap_err2string(rc)); 1648 ret = HDB_ERR_CANT_LOCK_DB; 1649 } else 1650 ret = 0; 1651 1652 out: 1653 if (dn != NULL) 1654 free(dn); 1655 if (msg != NULL) 1656 ldap_msgfree(msg); 1657 1658 return ret; 1659 } 1660 1661 static krb5_error_code 1662 LDAP_destroy(krb5_context context, HDB * db) 1663 { 1664 krb5_error_code ret; 1665 1666 LDAP_close(context, db); 1667 1668 ret = hdb_clear_master_key(context, db); 1669 if (HDB2BASE(db)) 1670 free(HDB2BASE(db)); 1671 if (HDB2CREATE(db)) 1672 free(HDB2CREATE(db)); 1673 if (HDB2URL(db)) 1674 free(HDB2URL(db)); 1675 if (db->hdb_name) 1676 free(db->hdb_name); 1677 free(db->hdb_db); 1678 free(db); 1679 1680 return ret; 1681 } 1682 1683 krb5_error_code 1684 hdb_ldap_common(krb5_context context, 1685 HDB ** db, 1686 const char *search_base, 1687 const char *url) 1688 { 1689 struct hdbldapdb *h; 1690 const char *create_base = NULL; 1691 1692 if (search_base == NULL && search_base[0] == '\0') { 1693 krb5_set_error_string(context, "ldap search base not configured"); 1694 return ENOMEM; /* XXX */ 1695 } 1696 1697 if (structural_object == NULL) { 1698 const char *p; 1699 1700 p = krb5_config_get_string(context, NULL, "kdc", 1701 "hdb-ldap-structural-object", NULL); 1702 if (p == NULL) 1703 p = default_structural_object; 1704 structural_object = strdup(p); 1705 if (structural_object == NULL) { 1706 krb5_set_error_string(context, "malloc: out of memory"); 1707 return ENOMEM; 1708 } 1709 } 1710 1711 samba_forwardable = 1712 krb5_config_get_bool_default(context, NULL, TRUE, 1713 "kdc", "hdb-samba-forwardable", NULL); 1714 1715 *db = calloc(1, sizeof(**db)); 1716 if (*db == NULL) { 1717 krb5_set_error_string(context, "malloc: out of memory"); 1718 return ENOMEM; 1719 } 1720 memset(*db, 0, sizeof(**db)); 1721 1722 h = calloc(1, sizeof(*h)); 1723 if (h == NULL) { 1724 krb5_set_error_string(context, "malloc: out of memory"); 1725 free(*db); 1726 *db = NULL; 1727 return ENOMEM; 1728 } 1729 (*db)->hdb_db = h; 1730 1731 /* XXX */ 1732 if (asprintf(&(*db)->hdb_name, "ldap:%s", search_base) == -1) { 1733 LDAP_destroy(context, *db); 1734 krb5_set_error_string(context, "strdup: out of memory"); 1735 *db = NULL; 1736 return ENOMEM; 1737 } 1738 1739 h->h_url = strdup(url); 1740 h->h_base = strdup(search_base); 1741 if (h->h_url == NULL || h->h_base == NULL) { 1742 LDAP_destroy(context, *db); 1743 krb5_set_error_string(context, "strdup: out of memory"); 1744 *db = NULL; 1745 return ENOMEM; 1746 } 1747 1748 create_base = krb5_config_get_string(context, NULL, "kdc", 1749 "hdb-ldap-create-base", NULL); 1750 if (create_base == NULL) 1751 create_base = h->h_base; 1752 1753 h->h_createbase = strdup(create_base); 1754 if (h->h_createbase == NULL) { 1755 LDAP_destroy(context, *db); 1756 krb5_set_error_string(context, "strdup: out of memory"); 1757 *db = NULL; 1758 return ENOMEM; 1759 } 1760 1761 (*db)->hdb_master_key_set = 0; 1762 (*db)->hdb_openp = 0; 1763 (*db)->hdb_open = LDAP_open; 1764 (*db)->hdb_close = LDAP_close; 1765 (*db)->hdb_fetch = LDAP_fetch; 1766 (*db)->hdb_store = LDAP_store; 1767 (*db)->hdb_remove = LDAP_remove; 1768 (*db)->hdb_firstkey = LDAP_firstkey; 1769 (*db)->hdb_nextkey = LDAP_nextkey; 1770 (*db)->hdb_lock = LDAP_lock; 1771 (*db)->hdb_unlock = LDAP_unlock; 1772 (*db)->hdb_rename = NULL; 1773 (*db)->hdb__get = NULL; 1774 (*db)->hdb__put = NULL; 1775 (*db)->hdb__del = NULL; 1776 (*db)->hdb_destroy = LDAP_destroy; 1777 1778 return 0; 1779 } 1780 1781 krb5_error_code 1782 hdb_ldap_create(krb5_context context, HDB ** db, const char *arg) 1783 { 1784 return hdb_ldap_common(context, db, arg, "ldapi:///"); 1785 } 1786 1787 krb5_error_code 1788 hdb_ldapi_create(krb5_context context, HDB ** db, const char *arg) 1789 { 1790 krb5_error_code ret; 1791 char *search_base, *p; 1792 1793 asprintf(&p, "ldapi:%s", arg); 1794 if (p == NULL) { 1795 krb5_set_error_string(context, "out of memory"); 1796 *db = NULL; 1797 return ENOMEM; 1798 } 1799 search_base = strchr(p + strlen("ldapi://"), ':'); 1800 if (search_base == NULL) { 1801 krb5_set_error_string(context, "search base missing"); 1802 *db = NULL; 1803 return HDB_ERR_BADVERSION; 1804 } 1805 *search_base = '\0'; 1806 search_base++; 1807 1808 ret = hdb_ldap_common(context, db, search_base, p); 1809 free(p); 1810 return ret; 1811 } 1812 1813 #ifdef OPENLDAP_MODULE 1814 1815 struct hdb_so_method hdb_ldap_interface = { 1816 HDB_INTERFACE_VERSION, 1817 "ldap", 1818 hdb_ldap_create 1819 }; 1820 1821 struct hdb_so_method hdb_ldapi_interface = { 1822 HDB_INTERFACE_VERSION, 1823 "ldapi", 1824 hdb_ldapi_create 1825 }; 1826 1827 #endif 1828 1829 #endif /* OPENLDAP */ 1830