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