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