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