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