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