/ lib / kadm5 / ad.c
ad.c
   1  /*
   2   * Copyright (c) 2004 Kungliga Tekniska Högskolan
   3   * (Royal Institute of Technology, Stockholm, Sweden).
   4   * All rights reserved.
   5   *
   6   * Redistribution and use in source and binary forms, with or without
   7   * modification, are permitted provided that the following conditions
   8   * are met:
   9   *
  10   * 1. Redistributions of source code must retain the above copyright
  11   *    notice, this list of conditions and the following disclaimer.
  12   *
  13   * 2. Redistributions in binary form must reproduce the above copyright
  14   *    notice, this list of conditions and the following disclaimer in the
  15   *    documentation and/or other materials provided with the distribution.
  16   *
  17   * 3. Neither the name of the Institute nor the names of its contributors
  18   *    may be used to endorse or promote products derived from this software
  19   *    without specific prior written permission.
  20   *
  21   * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24   * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  25   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31   * SUCH DAMAGE.
  32   */
  33  
  34  #define HAVE_TSASL 1
  35  
  36  #include "kadm5_locl.h"
  37  #if 1
  38  #undef OPENLDAP
  39  #undef HAVE_TSASL
  40  #endif
  41  #ifdef OPENLDAP
  42  #include <ldap.h>
  43  #ifdef HAVE_TSASL
  44  #include <tsasl.h>
  45  #endif
  46  #include <resolve.h>
  47  #include <base64.h>
  48  #endif
  49  
  50  RCSID("$Id$");
  51  
  52  #ifdef OPENLDAP
  53  
  54  #define CTX2LP(context) ((LDAP *)((context)->ldap_conn))
  55  #define CTX2BASE(context) ((context)->base_dn)
  56  
  57  /*
  58   * userAccountControl
  59   */
  60  
  61  #define UF_SCRIPT	 			0x00000001
  62  #define UF_ACCOUNTDISABLE			0x00000002
  63  #define UF_UNUSED_0	 			0x00000004
  64  #define UF_HOMEDIR_REQUIRED			0x00000008
  65  #define UF_LOCKOUT	 			0x00000010
  66  #define UF_PASSWD_NOTREQD 			0x00000020
  67  #define UF_PASSWD_CANT_CHANGE 			0x00000040
  68  #define UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED	0x00000080
  69  #define UF_TEMP_DUPLICATE_ACCOUNT       	0x00000100
  70  #define UF_NORMAL_ACCOUNT               	0x00000200
  71  #define UF_UNUSED_1	 			0x00000400
  72  #define UF_INTERDOMAIN_TRUST_ACCOUNT    	0x00000800
  73  #define UF_WORKSTATION_TRUST_ACCOUNT    	0x00001000
  74  #define UF_SERVER_TRUST_ACCOUNT         	0x00002000
  75  #define UF_UNUSED_2	 			0x00004000
  76  #define UF_UNUSED_3	 			0x00008000
  77  #define UF_PASSWD_NOT_EXPIRE			0x00010000
  78  #define UF_MNS_LOGON_ACCOUNT			0x00020000
  79  #define UF_SMARTCARD_REQUIRED			0x00040000
  80  #define UF_TRUSTED_FOR_DELEGATION		0x00080000
  81  #define UF_NOT_DELEGATED			0x00100000
  82  #define UF_USE_DES_KEY_ONLY			0x00200000
  83  #define UF_DONT_REQUIRE_PREAUTH			0x00400000
  84  #define UF_UNUSED_4				0x00800000
  85  #define UF_UNUSED_5				0x01000000
  86  #define UF_UNUSED_6				0x02000000
  87  #define UF_UNUSED_7				0x04000000
  88  #define UF_UNUSED_8				0x08000000
  89  #define UF_UNUSED_9				0x10000000
  90  #define UF_UNUSED_10				0x20000000
  91  #define UF_UNUSED_11				0x40000000
  92  #define UF_UNUSED_12				0x80000000
  93  
  94  /*
  95   *
  96   */
  97  
  98  #ifndef HAVE_TSASL
  99  static int
 100  sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *interact)
 101  {
 102      return LDAP_SUCCESS;
 103  }
 104  #endif
 105  
 106  #if 0
 107  static Sockbuf_IO ldap_tsasl_io = {
 108      NULL,			/* sbi_setup */
 109      NULL,			/* sbi_remove */
 110      NULL,			/* sbi_ctrl */
 111      NULL,			/* sbi_read */
 112      NULL,			/* sbi_write */
 113      NULL			/* sbi_close */
 114  };
 115  #endif
 116  
 117  #ifdef HAVE_TSASL
 118  static int
 119  ldap_tsasl_bind_s(LDAP *ld,
 120  		  LDAP_CONST char *dn,
 121  		  LDAPControl **serverControls,
 122  		  LDAPControl **clientControls,
 123  		  const char *host)
 124  {
 125      char *attrs[] = { "supportedSASLMechanisms", NULL };
 126      struct tsasl_peer *peer = NULL;
 127      struct tsasl_buffer in, out;
 128      struct berval ccred, *scred;
 129      LDAPMessage *m, *m0;
 130      const char *mech;
 131      char **vals;
 132      int ret, rc;
 133  
 134      ret = tsasl_peer_init(TSASL_FLAGS_INITIATOR | TSASL_FLAGS_CLEAR,
 135  			  "ldap", host, &peer);
 136      if (ret != TSASL_DONE) {
 137  	rc = LDAP_LOCAL_ERROR;
 138  	goto out;
 139      }
 140  
 141      rc = ldap_search_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, &m0);
 142      if (rc != LDAP_SUCCESS)
 143  	goto out;
 144  
 145      m = ldap_first_entry(ld, m0);
 146      if (m == NULL) {
 147  	ldap_msgfree(m0);
 148  	goto out;
 149      }
 150  
 151      vals = ldap_get_values(ld, m, "supportedSASLMechanisms");
 152      if (vals == NULL) {
 153  	ldap_msgfree(m0);
 154  	goto out;
 155      }
 156  
 157      ret = tsasl_find_best_mech(peer, vals, &mech);
 158      if (ret) {
 159  	ldap_msgfree(m0);
 160  	goto out;
 161      }
 162  
 163      ldap_msgfree(m0);
 164  
 165      ret = tsasl_select_mech(peer, mech);
 166      if (ret != TSASL_DONE) {
 167  	rc = LDAP_LOCAL_ERROR;
 168  	goto out;
 169      }
 170  
 171      in.tb_data = NULL;
 172      in.tb_size = 0;
 173  
 174      do {
 175  	ret = tsasl_request(peer, &in, &out);
 176  	if (in.tb_size != 0) {
 177  	    free(in.tb_data);
 178  	    in.tb_data = NULL;
 179  	    in.tb_size = 0;
 180  	}
 181  	if (ret != TSASL_DONE && ret != TSASL_CONTINUE) {
 182  	    rc = LDAP_AUTH_UNKNOWN;
 183  	    goto out;
 184  	}
 185  
 186  	ccred.bv_val = out.tb_data;
 187  	ccred.bv_len = out.tb_size;
 188  
 189  	rc = ldap_sasl_bind_s(ld, dn, mech, &ccred,
 190  			      serverControls, clientControls, &scred);
 191  	tsasl_buffer_free(&out);
 192  
 193  	if (rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS) {
 194  	    if(scred && scred->bv_len)
 195  		ber_bvfree(scred);
 196  	    goto out;
 197  	}
 198  
 199  	in.tb_data = malloc(scred->bv_len);
 200  	if (in.tb_data == NULL) {
 201  	    rc = LDAP_LOCAL_ERROR;
 202  	    goto out;
 203  	}
 204  	memcpy(in.tb_data, scred->bv_val, scred->bv_len);
 205  	in.tb_size = scred->bv_len;
 206  	ber_bvfree(scred);
 207  
 208      } while (rc == LDAP_SASL_BIND_IN_PROGRESS);
 209  
 210   out:
 211      if (rc == LDAP_SUCCESS) {
 212  #if 0
 213  	ber_sockbuf_add_io(ld->ld_conns->lconn_sb, &ldap_tsasl_io,
 214  			   LBER_SBIOD_LEVEL_APPLICATION, peer);
 215  
 216  #endif
 217      } else if (peer != NULL)
 218  	tsasl_peer_free(peer);
 219  
 220      return rc;
 221  }
 222  #endif /* HAVE_TSASL */
 223  
 224  
 225  static int
 226  check_ldap(kadm5_ad_context *context, int ret)
 227  {
 228      switch (ret) {
 229      case LDAP_SUCCESS:
 230  	return 0;
 231      case LDAP_SERVER_DOWN: {
 232  	LDAP *lp = CTX2LP(context);
 233  	ldap_unbind(lp);
 234  	context->ldap_conn = NULL;
 235  	free(context->base_dn);
 236  	context->base_dn = NULL;
 237  	return 1;
 238      }
 239      default:
 240  	return 1;
 241      }
 242  }
 243  
 244  /*
 245   *
 246   */
 247  
 248  static void
 249  laddattr(char ***al, int *attrlen, char *attr)
 250  {
 251      char **a;
 252      a = realloc(*al, (*attrlen + 2) * sizeof(**al));
 253      if (a == NULL)
 254  	return;
 255      a[*attrlen] = attr;
 256      a[*attrlen + 1] = NULL;
 257      (*attrlen)++;
 258      *al = a;
 259  }
 260  
 261  static kadm5_ret_t
 262  _kadm5_ad_connect(void *server_handle)
 263  {
 264      kadm5_ad_context *context = server_handle;
 265      struct {
 266  	char *server;
 267  	int port;
 268      } *s, *servers = NULL;
 269      int i, num_servers = 0;
 270  
 271      if (context->ldap_conn)
 272  	return 0;
 273  
 274      {
 275  	struct dns_reply *r;
 276  	struct resource_record *rr;
 277  	char *domain;
 278  
 279  	asprintf(&domain, "_ldap._tcp.%s", context->realm);
 280  	if (domain == NULL) {
 281  	    krb5_set_error_message(context->context, KADM5_NO_SRV, "malloc");
 282  	    return KADM5_NO_SRV;
 283  	}
 284  
 285  	r = dns_lookup(domain, "SRV");
 286  	free(domain);
 287  	if (r == NULL) {
 288  	    krb5_set_error_message(context->context, KADM5_NO_SRV, "Didn't find ldap dns");
 289  	    return KADM5_NO_SRV;
 290  	}
 291  
 292  	for (rr = r->head ; rr != NULL; rr = rr->next) {
 293  	    if (rr->type != rk_ns_t_srv)
 294  		continue;
 295  	    s = realloc(servers, sizeof(*servers) * (num_servers + 1));
 296  	    if (s == NULL) {
 297  		krb5_set_error_message(context->context, KADM5_RPC_ERROR, "malloc");
 298  		dns_free_data(r);
 299  		goto fail;
 300  	    }
 301  	    servers = s;
 302  	    num_servers++;
 303  	    servers[num_servers - 1].port =  rr->u.srv->port;
 304  	    servers[num_servers - 1].server =  strdup(rr->u.srv->target);
 305  	}
 306  	dns_free_data(r);
 307      }
 308  
 309      if (num_servers == 0) {
 310  	krb5_set_error_message(context->context, KADM5_NO_SRV, "No AD server found in DNS");
 311  	return KADM5_NO_SRV;
 312      }
 313  
 314      for (i = 0; i < num_servers; i++) {
 315  	int lret, version = LDAP_VERSION3;
 316  	LDAP *lp;
 317  
 318  	lp = ldap_init(servers[i].server, servers[i].port);
 319  	if (lp == NULL)
 320  	    continue;
 321  
 322  	if (ldap_set_option(lp, LDAP_OPT_PROTOCOL_VERSION, &version)) {
 323  	    ldap_unbind(lp);
 324  	    continue;
 325  	}
 326  
 327  	if (ldap_set_option(lp, LDAP_OPT_REFERRALS, LDAP_OPT_OFF)) {
 328  	    ldap_unbind(lp);
 329  	    continue;
 330  	}
 331  
 332  #ifdef HAVE_TSASL
 333  	lret = ldap_tsasl_bind_s(lp, NULL, NULL, NULL, servers[i].server);
 334  
 335  #else
 336  	lret = ldap_sasl_interactive_bind_s(lp, NULL, NULL, NULL, NULL,
 337  					    LDAP_SASL_QUIET,
 338  					    sasl_interact, NULL);
 339  #endif
 340  	if (lret != LDAP_SUCCESS) {
 341  	    krb5_set_error_message(context->context, 0,
 342  				   "Couldn't contact any AD servers: %s",
 343  				   ldap_err2string(lret));
 344  	    ldap_unbind(lp);
 345  	    continue;
 346  	}
 347  
 348  	context->ldap_conn = lp;
 349  	break;
 350      }
 351      if (i >= num_servers) {
 352  	goto fail;
 353      }
 354  
 355      {
 356  	LDAPMessage *m, *m0;
 357  	char **attr = NULL;
 358  	int attrlen = 0;
 359  	char **vals;
 360  	int ret;
 361  
 362  	laddattr(&attr, &attrlen, "defaultNamingContext");
 363  
 364  	ret = ldap_search_s(CTX2LP(context), "", LDAP_SCOPE_BASE,
 365  			    "objectclass=*", attr, 0, &m);
 366  	free(attr);
 367  	if (check_ldap(context, ret))
 368  	    goto fail;
 369  
 370  	if (ldap_count_entries(CTX2LP(context), m) > 0) {
 371  	    m0 = ldap_first_entry(CTX2LP(context), m);
 372  	    if (m0 == NULL) {
 373  		krb5_set_error_message(context->context, KADM5_RPC_ERROR,
 374  				       "Error in AD ldap responce");
 375  		ldap_msgfree(m);
 376  		goto fail;
 377  	    }
 378  	    vals = ldap_get_values(CTX2LP(context),
 379  				   m0, "defaultNamingContext");
 380  	    if (vals == NULL) {
 381  		krb5_set_error_message(context->context, KADM5_RPC_ERROR,
 382  				       "No naming context found");
 383  		goto fail;
 384  	    }
 385  	    context->base_dn = strdup(vals[0]);
 386  	} else
 387  	    goto fail;
 388  	ldap_msgfree(m);
 389      }
 390  
 391      for (i = 0; i < num_servers; i++)
 392  	free(servers[i].server);
 393      free(servers);
 394  
 395      return 0;
 396  
 397   fail:
 398      for (i = 0; i < num_servers; i++)
 399  	free(servers[i].server);
 400      free(servers);
 401  
 402      if (context->ldap_conn) {
 403  	ldap_unbind(CTX2LP(context));
 404  	context->ldap_conn = NULL;
 405      }
 406      return KADM5_RPC_ERROR;
 407  }
 408  
 409  #define NTTIME_EPOCH 0x019DB1DED53E8000LL
 410  
 411  static time_t
 412  nt2unixtime(const char *str)
 413  {
 414      unsigned long long t;
 415      t = strtoll(str, NULL, 10);
 416      t = ((t - NTTIME_EPOCH) / (long long)10000000);
 417      if (t > (((time_t)(~(long long)0)) >> 1))
 418  	return 0;
 419      return (time_t)t;
 420  }
 421  
 422  static long long
 423  unix2nttime(time_t unix_time)
 424  {
 425      long long wt;
 426      wt = unix_time * (long long)10000000 + (long long)NTTIME_EPOCH;
 427      return wt;
 428  }
 429  
 430  /* XXX create filter in a better way */
 431  
 432  static int
 433  ad_find_entry(kadm5_ad_context *context,
 434  	      const char *fqdn,
 435  	      const char *pn,
 436  	      char **name)
 437  {
 438      LDAPMessage *m, *m0;
 439      char *attr[] = { "distinguishedName", NULL };
 440      char *filter;
 441      int ret;
 442  
 443      if (name)
 444  	*name = NULL;
 445  
 446      if (fqdn)
 447  	asprintf(&filter,
 448  		 "(&(objectClass=computer)(|(dNSHostName=%s)(servicePrincipalName=%s)))",
 449  		 fqdn, pn);
 450      else if(pn)
 451  	asprintf(&filter, "(&(objectClass=account)(userPrincipalName=%s))", pn);
 452      else
 453  	return KADM5_RPC_ERROR;
 454  
 455      ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
 456  			LDAP_SCOPE_SUBTREE,
 457  			filter, attr, 0, &m);
 458      free(filter);
 459      if (check_ldap(context, ret))
 460  	return KADM5_RPC_ERROR;
 461  
 462      if (ldap_count_entries(CTX2LP(context), m) > 0) {
 463  	char **vals;
 464  	m0 = ldap_first_entry(CTX2LP(context), m);
 465  	vals = ldap_get_values(CTX2LP(context), m0, "distinguishedName");
 466  	if (vals == NULL || vals[0] == NULL) {
 467  	    ldap_msgfree(m);
 468  	    return KADM5_RPC_ERROR;
 469  	}
 470  	if (name)
 471  	    *name = strdup(vals[0]);
 472  	ldap_msgfree(m);
 473      } else
 474  	return KADM5_UNK_PRINC;
 475  
 476      return 0;
 477  }
 478  
 479  #endif /* OPENLDAP */
 480  
 481  static kadm5_ret_t
 482  ad_get_cred(kadm5_ad_context *context, const char *password)
 483  {
 484      kadm5_ret_t ret;
 485      krb5_ccache cc;
 486      char *service;
 487  
 488      if (context->ccache)
 489  	return 0;
 490  
 491      asprintf(&service, "%s/%s@%s", KRB5_TGS_NAME,
 492  	     context->realm, context->realm);
 493      if (service == NULL)
 494  	return ENOMEM;
 495  
 496      ret = _kadm5_c_get_cred_cache(context->context,
 497  				  context->client_name,
 498  				  service,
 499  				  password, krb5_prompter_posix,
 500  				  NULL, NULL, &cc);
 501      free(service);
 502      if(ret)
 503  	return ret; /* XXX */
 504      context->ccache = cc;
 505      return 0;
 506  }
 507  
 508  static kadm5_ret_t
 509  kadm5_ad_chpass_principal(void *server_handle,
 510  			  krb5_principal principal,
 511  			  int keepold,
 512  			  const char *password,
 513  			  int n_ks_tuple,
 514  			  krb5_key_salt_tuple *ks_tuple)
 515  {
 516      kadm5_ad_context *context = server_handle;
 517      krb5_data result_code_string, result_string;
 518      int result_code;
 519      kadm5_ret_t ret;
 520  
 521      if (keepold)
 522  	return KADM5_KEEPOLD_NOSUPP;
 523  
 524      ret = ad_get_cred(context, NULL);
 525      if (ret)
 526  	return ret;
 527  
 528      krb5_data_zero (&result_code_string);
 529      krb5_data_zero (&result_string);
 530  
 531      ret = krb5_set_password_using_ccache (context->context,
 532  					  context->ccache,
 533  					  password,
 534  					  principal,
 535  					  &result_code,
 536  					  &result_code_string,
 537  					  &result_string);
 538  
 539      krb5_data_free (&result_code_string);
 540      krb5_data_free (&result_string);
 541  
 542      /* XXX do mapping here on error codes */
 543  
 544      return ret;
 545  }
 546  
 547  #ifdef OPENLDAP
 548  static const char *
 549  get_fqdn(krb5_context context, const krb5_principal p)
 550  {
 551      const char *s, *hosttypes[] = { "host", "ldap", "gc", "cifs", "dns" };
 552      int i;
 553  
 554      s = krb5_principal_get_comp_string(context, p, 0);
 555      if (p == NULL)
 556  	return NULL;
 557  
 558      for (i = 0; i < sizeof(hosttypes)/sizeof(hosttypes[0]); i++) {
 559  	if (strcasecmp(s, hosttypes[i]) == 0)
 560  	    return krb5_principal_get_comp_string(context, p, 1);
 561      }
 562      return 0;
 563  }
 564  #endif
 565  
 566  
 567  static kadm5_ret_t
 568  kadm5_ad_create_principal(void *server_handle,
 569  			  kadm5_principal_ent_t entry,
 570  			  uint32_t mask,
 571  			  const char *password,
 572  			  int n_ks_tuple,
 573  			  krb5_key_salt_tuple *ks_tuple)
 574  {
 575      kadm5_ad_context *context = server_handle;
 576  
 577      /*
 578       * KADM5_PRINC_EXPIRE_TIME
 579       *
 580       * return 0 || KADM5_DUP;
 581       */
 582  
 583  #ifdef OPENLDAP
 584      LDAPMod *attrs[8], rattrs[7], *a;
 585      char *useraccvals[2] = { NULL, NULL },
 586  	*samvals[2], *dnsvals[2], *spnvals[5], *upnvals[2], *tv[2];
 587      char *ocvals_spn[] = { "top", "person", "organizationalPerson",
 588  			   "user", "computer", NULL};
 589      char *p, *realmless_p, *p_msrealm = NULL, *dn = NULL;
 590      const char *fqdn;
 591      char *s, *samname = NULL, *short_spn = NULL;
 592      int ret, i;
 593      int32_t uf_flags = 0;
 594  
 595      if ((mask & KADM5_PRINCIPAL) == 0)
 596  	return KADM5_BAD_MASK;
 597  
 598      for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++)
 599  	attrs[i] = &rattrs[i];
 600      attrs[i] = NULL;
 601  
 602      ret = ad_get_cred(context, NULL);
 603      if (ret)
 604  	return ret;
 605  
 606      ret = _kadm5_ad_connect(server_handle);
 607      if (ret)
 608  	return ret;
 609  
 610      fqdn = get_fqdn(context->context, entry->principal);
 611  
 612      ret = krb5_unparse_name(context->context, entry->principal, &p);
 613      if (ret)
 614  	return ret;
 615  
 616      if (ad_find_entry(context, fqdn, p, NULL) == 0) {
 617  	free(p);
 618  	return KADM5_DUP;
 619      }
 620  
 621      if (mask & KADM5_ATTRIBUTES) {
 622  	if (entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX)
 623  	    uf_flags |= UF_ACCOUNTDISABLE|UF_LOCKOUT;
 624  	if ((entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) == 0)
 625  	    uf_flags |= UF_DONT_REQUIRE_PREAUTH;
 626  	if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH)
 627  	    uf_flags |= UF_SMARTCARD_REQUIRED;
 628      }
 629  
 630      realmless_p = strdup(p);
 631      if (realmless_p == NULL) {
 632  	ret = ENOMEM;
 633  	goto out;
 634      }
 635      s = strrchr(realmless_p, '@');
 636      if (s)
 637  	*s = '\0';
 638  
 639      if (fqdn) {
 640  	/* create computer account */
 641  	asprintf(&samname, "%s$", fqdn);
 642  	if (samname == NULL) {
 643  	    ret = ENOMEM;
 644  	    goto out;
 645  	}
 646  	s = strchr(samname, '.');
 647  	if (s) {
 648  	    s[0] = '$';
 649  	    s[1] = '\0';
 650  	}
 651  
 652  	short_spn = strdup(p);
 653  	if (short_spn == NULL) {
 654  	    errno = ENOMEM;
 655  	    goto out;
 656  	}
 657  	s = strchr(short_spn, '.');
 658  	if (s) {
 659  	    *s = '\0';
 660  	} else {
 661  	    free(short_spn);
 662  	    short_spn = NULL;
 663  	}
 664  
 665  	p_msrealm = strdup(p);
 666  	if (p_msrealm == NULL) {
 667  	    errno = ENOMEM;
 668  	    goto out;
 669  	}
 670  	s = strrchr(p_msrealm, '@');
 671  	if (s) {
 672  	    *s = '/';
 673  	} else {
 674  	    free(p_msrealm);
 675  	    p_msrealm = NULL;
 676  	}
 677  
 678  	asprintf(&dn, "cn=%s, cn=Computers, %s", fqdn, CTX2BASE(context));
 679  	if (dn == NULL) {
 680  	    ret = ENOMEM;
 681  	    goto out;
 682  	}
 683  
 684  	a = &rattrs[0];
 685  	a->mod_op = LDAP_MOD_ADD;
 686  	a->mod_type = "objectClass";
 687  	a->mod_values = ocvals_spn;
 688  	a++;
 689  
 690  	a->mod_op = LDAP_MOD_ADD;
 691  	a->mod_type = "userAccountControl";
 692  	a->mod_values = useraccvals;
 693  	asprintf(&useraccvals[0], "%d",
 694  		 uf_flags |
 695  		 UF_PASSWD_NOT_EXPIRE |
 696  		 UF_WORKSTATION_TRUST_ACCOUNT);
 697  	useraccvals[1] = NULL;
 698  	a++;
 699  
 700  	a->mod_op = LDAP_MOD_ADD;
 701  	a->mod_type = "sAMAccountName";
 702  	a->mod_values = samvals;
 703  	samvals[0] = samname;
 704  	samvals[1] = NULL;
 705  	a++;
 706  
 707  	a->mod_op = LDAP_MOD_ADD;
 708  	a->mod_type = "dNSHostName";
 709  	a->mod_values = dnsvals;
 710  	dnsvals[0] = (char *)fqdn;
 711  	dnsvals[1] = NULL;
 712  	a++;
 713  
 714  	/* XXX  add even more spn's */
 715  	a->mod_op = LDAP_MOD_ADD;
 716  	a->mod_type = "servicePrincipalName";
 717  	a->mod_values = spnvals;
 718  	i = 0;
 719  	spnvals[i++] = p;
 720  	spnvals[i++] = realmless_p;
 721  	if (short_spn)
 722  	    spnvals[i++] = short_spn;
 723  	if (p_msrealm)
 724  	    spnvals[i++] = p_msrealm;
 725  	spnvals[i++] = NULL;
 726  	a++;
 727  
 728  	a->mod_op = LDAP_MOD_ADD;
 729  	a->mod_type = "userPrincipalName";
 730  	a->mod_values = upnvals;
 731  	upnvals[0] = p;
 732  	upnvals[1] = NULL;
 733  	a++;
 734  
 735  	a->mod_op = LDAP_MOD_ADD;
 736  	a->mod_type = "accountExpires";
 737  	a->mod_values = tv;
 738  	tv[0] = "9223372036854775807"; /* "never" */
 739  	tv[1] = NULL;
 740  	a++;
 741  
 742      } else {
 743  	/* create user account */
 744  
 745  	a = &rattrs[0];
 746  	a->mod_op = LDAP_MOD_ADD;
 747  	a->mod_type = "userAccountControl";
 748  	a->mod_values = useraccvals;
 749  	asprintf(&useraccvals[0], "%d",
 750  		 uf_flags |
 751  		 UF_PASSWD_NOT_EXPIRE);
 752  	useraccvals[1] = NULL;
 753  	a++;
 754  
 755  	a->mod_op = LDAP_MOD_ADD;
 756  	a->mod_type = "sAMAccountName";
 757  	a->mod_values = samvals;
 758  	samvals[0] = realmless_p;
 759  	samvals[1] = NULL;
 760  	a++;
 761  
 762  	a->mod_op = LDAP_MOD_ADD;
 763  	a->mod_type = "userPrincipalName";
 764  	a->mod_values = upnvals;
 765  	upnvals[0] = p;
 766  	upnvals[1] = NULL;
 767  	a++;
 768  
 769  	a->mod_op = LDAP_MOD_ADD;
 770  	a->mod_type = "accountExpires";
 771  	a->mod_values = tv;
 772  	tv[0] = "9223372036854775807"; /* "never" */
 773  	tv[1] = NULL;
 774  	a++;
 775      }
 776  
 777      attrs[a - &rattrs[0]] = NULL;
 778  
 779      ret = ldap_add_s(CTX2LP(context), dn, attrs);
 780  
 781   out:
 782      if (useraccvals[0])
 783  	free(useraccvals[0]);
 784      if (realmless_p)
 785  	free(realmless_p);
 786      if (samname)
 787  	free(samname);
 788      if (short_spn)
 789  	free(short_spn);
 790      if (p_msrealm)
 791  	free(p_msrealm);
 792      free(p);
 793  
 794      if (check_ldap(context, ret))
 795  	return KADM5_RPC_ERROR;
 796  
 797      return 0;
 798  #else
 799      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
 800      return KADM5_RPC_ERROR;
 801  #endif
 802  }
 803  
 804  static kadm5_ret_t
 805  kadm5_ad_delete_principal(void *server_handle, krb5_principal principal)
 806  {
 807      kadm5_ad_context *context = server_handle;
 808  #ifdef OPENLDAP
 809      char *p, *dn = NULL;
 810      const char *fqdn;
 811      int ret;
 812  
 813      ret = ad_get_cred(context, NULL);
 814      if (ret)
 815  	return ret;
 816  
 817      ret = _kadm5_ad_connect(server_handle);
 818      if (ret)
 819  	return ret;
 820  
 821      fqdn = get_fqdn(context->context, principal);
 822  
 823      ret = krb5_unparse_name(context->context, principal, &p);
 824      if (ret)
 825  	return ret;
 826  
 827      if (ad_find_entry(context, fqdn, p, &dn) != 0) {
 828  	free(p);
 829  	return KADM5_UNK_PRINC;
 830      }
 831  
 832      ret = ldap_delete_s(CTX2LP(context), dn);
 833  
 834      free(dn);
 835      free(p);
 836  
 837      if (check_ldap(context, ret))
 838  	return KADM5_RPC_ERROR;
 839      return 0;
 840  #else
 841      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
 842      return KADM5_RPC_ERROR;
 843  #endif
 844  }
 845  
 846  static kadm5_ret_t
 847  kadm5_ad_destroy(void *server_handle)
 848  {
 849      kadm5_ad_context *context = server_handle;
 850  
 851      if (context->ccache)
 852  	krb5_cc_destroy(context->context, context->ccache);
 853  
 854  #ifdef OPENLDAP
 855      {
 856  	LDAP *lp = CTX2LP(context);
 857  	if (lp)
 858  	    ldap_unbind(lp);
 859  	if (context->base_dn)
 860  	    free(context->base_dn);
 861      }
 862  #endif
 863      free(context->realm);
 864      free(context->client_name);
 865      krb5_free_principal(context->context, context->caller);
 866      if(context->my_context)
 867  	krb5_free_context(context->context);
 868      return 0;
 869  }
 870  
 871  static kadm5_ret_t
 872  kadm5_ad_flush(void *server_handle)
 873  {
 874      kadm5_ad_context *context = server_handle;
 875      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
 876      return KADM5_RPC_ERROR;
 877  }
 878  
 879  static kadm5_ret_t
 880  kadm5_ad_get_principal(void *server_handle,
 881  		       krb5_principal principal,
 882  		       kadm5_principal_ent_t entry,
 883  		       uint32_t mask)
 884  {
 885      kadm5_ad_context *context = server_handle;
 886  #ifdef OPENLDAP
 887      LDAPMessage *m, *m0;
 888      char **attr = NULL;
 889      int attrlen = 0;
 890      char *filter, *p, *q, *u;
 891      int ret;
 892  
 893      /*
 894       * principal
 895       * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES
 896       */
 897  
 898      /*
 899       * return 0 || KADM5_DUP;
 900       */
 901  
 902      memset(entry, 0, sizeof(*entry));
 903  
 904      if (mask & KADM5_KVNO)
 905  	laddattr(&attr, &attrlen, "msDS-KeyVersionNumber");
 906  
 907      if (mask & KADM5_PRINCIPAL) {
 908  	laddattr(&attr, &attrlen, "userPrincipalName");
 909  	laddattr(&attr, &attrlen, "servicePrincipalName");
 910      }
 911      laddattr(&attr, &attrlen, "objectClass");
 912      laddattr(&attr, &attrlen, "lastLogon");
 913      laddattr(&attr, &attrlen, "badPwdCount");
 914      laddattr(&attr, &attrlen, "badPasswordTime");
 915      laddattr(&attr, &attrlen, "pwdLastSet");
 916      laddattr(&attr, &attrlen, "accountExpires");
 917      laddattr(&attr, &attrlen, "userAccountControl");
 918  
 919      krb5_unparse_name_short(context->context, principal, &p);
 920      krb5_unparse_name(context->context, principal, &u);
 921  
 922      /* replace @ in domain part with a / */
 923      q = strrchr(p, '@');
 924      if (q && (p != q && *(q - 1) != '\\'))
 925  	*q = '/';
 926  
 927      asprintf(&filter,
 928  	     "(|(userPrincipalName=%s)(servicePrincipalName=%s)(servicePrincipalName=%s))",
 929  	     u, p, u);
 930      free(p);
 931      free(u);
 932  
 933      ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
 934  			LDAP_SCOPE_SUBTREE,
 935  			filter, attr, 0, &m);
 936      free(attr);
 937      if (check_ldap(context, ret))
 938  	return KADM5_RPC_ERROR;
 939  
 940      if (ldap_count_entries(CTX2LP(context), m) > 0) {
 941  	char **vals;
 942  	m0 = ldap_first_entry(CTX2LP(context), m);
 943  	if (m0 == NULL) {
 944  	    ldap_msgfree(m);
 945  	    goto fail;
 946  	}
 947  #if 0
 948  	vals = ldap_get_values(CTX2LP(context), m0, "servicePrincipalName");
 949  	if (vals)
 950  	    printf("servicePrincipalName %s\n", vals[0]);
 951  	vals = ldap_get_values(CTX2LP(context), m0, "userPrincipalName");
 952  	if (vals)
 953  	    printf("userPrincipalName %s\n", vals[0]);
 954  	vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
 955  	if (vals)
 956  	    printf("userAccountControl %s\n", vals[0]);
 957  #endif
 958  	entry->princ_expire_time = 0;
 959  	if (mask & KADM5_PRINC_EXPIRE_TIME) {
 960  	    vals = ldap_get_values(CTX2LP(context), m0, "accountExpires");
 961  	    if (vals)
 962  		entry->princ_expire_time = nt2unixtime(vals[0]);
 963  	}
 964  	entry->last_success = 0;
 965  	if (mask & KADM5_LAST_SUCCESS) {
 966  	    vals = ldap_get_values(CTX2LP(context), m0, "lastLogon");
 967  	    if (vals)
 968  		entry->last_success = nt2unixtime(vals[0]);
 969  	}
 970  	if (mask & KADM5_LAST_FAILED) {
 971  	    vals = ldap_get_values(CTX2LP(context), m0, "badPasswordTime");
 972  	    if (vals)
 973  		entry->last_failed = nt2unixtime(vals[0]);
 974  	}
 975  	if (mask & KADM5_LAST_PWD_CHANGE) {
 976  	    vals = ldap_get_values(CTX2LP(context), m0, "pwdLastSet");
 977  	    if (vals)
 978  		entry->last_pwd_change = nt2unixtime(vals[0]);
 979  	}
 980  	if (mask & KADM5_FAIL_AUTH_COUNT) {
 981  	    vals = ldap_get_values(CTX2LP(context), m0, "badPwdCount");
 982  	    if (vals)
 983  		entry->fail_auth_count = atoi(vals[0]);
 984  	}
 985   	if (mask & KADM5_ATTRIBUTES) {
 986  	    vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
 987  	    if (vals) {
 988  		uint32_t i;
 989  		i = atoi(vals[0]);
 990  		if (i & (UF_ACCOUNTDISABLE|UF_LOCKOUT))
 991  		    entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
 992  		if ((i & UF_DONT_REQUIRE_PREAUTH) == 0)
 993  		    entry->attributes |= KRB5_KDB_REQUIRES_PRE_AUTH;
 994  		if (i & UF_SMARTCARD_REQUIRED)
 995  		    entry->attributes |= KRB5_KDB_REQUIRES_HW_AUTH;
 996  		if ((i & UF_WORKSTATION_TRUST_ACCOUNT) == 0)
 997  		    entry->attributes |= KRB5_KDB_DISALLOW_SVR;
 998  	    }
 999  	}
1000  	if (mask & KADM5_KVNO) {
1001  	    vals = ldap_get_values(CTX2LP(context), m0,
1002  				   "msDS-KeyVersionNumber");
1003  	    if (vals)
1004  		entry->kvno = atoi(vals[0]);
1005  	    else
1006  		entry->kvno = 0;
1007  	}
1008  	ldap_msgfree(m);
1009      } else {
1010  	return KADM5_UNK_PRINC;
1011      }
1012  
1013      if (mask & KADM5_PRINCIPAL)
1014  	krb5_copy_principal(context->context, principal, &entry->principal);
1015  
1016      return 0;
1017   fail:
1018      return KADM5_RPC_ERROR;
1019  #else
1020      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1021      return KADM5_RPC_ERROR;
1022  #endif
1023  }
1024  
1025  static kadm5_ret_t
1026  kadm5_ad_get_principals(void *server_handle,
1027  			const char *expression,
1028  			char ***principals,
1029  			int *count)
1030  {
1031      kadm5_ad_context *context = server_handle;
1032  
1033      /*
1034       * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES
1035       */
1036  
1037  #ifdef OPENLDAP
1038      kadm5_ret_t ret;
1039  
1040      ret = ad_get_cred(context, NULL);
1041      if (ret)
1042  	return ret;
1043  
1044      ret = _kadm5_ad_connect(server_handle);
1045      if (ret)
1046  	return ret;
1047  
1048      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1049      return KADM5_RPC_ERROR;
1050  #else
1051      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1052      return KADM5_RPC_ERROR;
1053  #endif
1054  }
1055  
1056  static kadm5_ret_t
1057  kadm5_ad_get_privs(void *server_handle, uint32_t*privs)
1058  {
1059      kadm5_ad_context *context = server_handle;
1060      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1061      return KADM5_RPC_ERROR;
1062  }
1063  
1064  static kadm5_ret_t
1065  kadm5_ad_modify_principal(void *server_handle,
1066  			  kadm5_principal_ent_t entry,
1067  			  uint32_t mask)
1068  {
1069      kadm5_ad_context *context = server_handle;
1070  
1071      /*
1072       * KADM5_ATTRIBUTES
1073       * KRB5_KDB_DISALLOW_ALL_TIX (| KADM5_KVNO)
1074       */
1075  
1076  #ifdef OPENLDAP
1077      LDAPMessage *m = NULL, *m0;
1078      kadm5_ret_t ret;
1079      char **attr = NULL;
1080      int attrlen = 0;
1081      char *p = NULL, *s = NULL, *q;
1082      char **vals;
1083      LDAPMod *attrs[4], rattrs[3], *a;
1084      char *uaf[2] = { NULL, NULL };
1085      char *kvno[2] = { NULL, NULL };
1086      char *tv[2] = { NULL, NULL };
1087      char *filter, *dn;
1088      int i;
1089  
1090      for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++)
1091  	attrs[i] = &rattrs[i];
1092      attrs[i] = NULL;
1093      a = &rattrs[0];
1094  
1095      ret = _kadm5_ad_connect(server_handle);
1096      if (ret)
1097  	return ret;
1098  
1099      if (mask & KADM5_KVNO)
1100  	laddattr(&attr, &attrlen, "msDS-KeyVersionNumber");
1101      if (mask & KADM5_PRINC_EXPIRE_TIME)
1102  	laddattr(&attr, &attrlen, "accountExpires");
1103      if (mask & KADM5_ATTRIBUTES)
1104  	laddattr(&attr, &attrlen, "userAccountControl");
1105      laddattr(&attr, &attrlen, "distinguishedName");
1106  
1107      krb5_unparse_name(context->context, entry->principal, &p);
1108  
1109      s = strdup(p);
1110  
1111      q = strrchr(s, '@');
1112      if (q && (p != q && *(q - 1) != '\\'))
1113  	*q = '\0';
1114  
1115      asprintf(&filter,
1116  	     "(|(userPrincipalName=%s)(servicePrincipalName=%s))",
1117  	     s, s);
1118      free(p);
1119      free(s);
1120  
1121      ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
1122  			LDAP_SCOPE_SUBTREE,
1123  			filter, attr, 0, &m);
1124      free(attr);
1125      free(filter);
1126      if (check_ldap(context, ret))
1127  	return KADM5_RPC_ERROR;
1128  
1129      if (ldap_count_entries(CTX2LP(context), m) <= 0) {
1130  	ret = KADM5_RPC_ERROR;
1131  	goto out;
1132      }
1133  
1134      m0 = ldap_first_entry(CTX2LP(context), m);
1135  
1136      if (mask & KADM5_ATTRIBUTES) {
1137  	int32_t i;
1138  
1139  	vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
1140  	if (vals == NULL) {
1141  	    ret = KADM5_RPC_ERROR;
1142  	    goto out;
1143  	}
1144  
1145  	i = atoi(vals[0]);
1146  	if (i == 0)
1147  	    return KADM5_RPC_ERROR;
1148  
1149  	if (entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX)
1150  	    i |= (UF_ACCOUNTDISABLE|UF_LOCKOUT);
1151  	else
1152  	    i &= ~(UF_ACCOUNTDISABLE|UF_LOCKOUT);
1153  	if (entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH)
1154  	    i &= ~UF_DONT_REQUIRE_PREAUTH;
1155  	else
1156  	    i |= UF_DONT_REQUIRE_PREAUTH;
1157  	if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH)
1158  	    i |= UF_SMARTCARD_REQUIRED;
1159  	else
1160  	    i &= UF_SMARTCARD_REQUIRED;
1161  	if (entry->attributes & KRB5_KDB_DISALLOW_SVR)
1162  	    i &= ~UF_WORKSTATION_TRUST_ACCOUNT;
1163  	else
1164  	    i |= UF_WORKSTATION_TRUST_ACCOUNT;
1165  
1166  	asprintf(&uaf[0], "%d", i);
1167  
1168  	a->mod_op = LDAP_MOD_REPLACE;
1169  	a->mod_type = "userAccountControl";
1170  	a->mod_values = uaf;
1171  	a++;
1172      }
1173  
1174      if (mask & KADM5_KVNO) {
1175  	vals = ldap_get_values(CTX2LP(context), m0, "msDS-KeyVersionNumber");
1176  	if (vals == NULL) {
1177  	    entry->kvno = 0;
1178  	} else {
1179  	    asprintf(&kvno[0], "%d", entry->kvno);
1180  
1181  	    a->mod_op = LDAP_MOD_REPLACE;
1182  	    a->mod_type = "msDS-KeyVersionNumber";
1183  	    a->mod_values = kvno;
1184  	    a++;
1185  	}
1186      }
1187  
1188      if (mask & KADM5_PRINC_EXPIRE_TIME) {
1189  	long long wt;
1190  	vals = ldap_get_values(CTX2LP(context), m0, "accountExpires");
1191  	if (vals == NULL) {
1192  	    ret = KADM5_RPC_ERROR;
1193  	    goto out;
1194  	}
1195  
1196  	wt = unix2nttime(entry->princ_expire_time);
1197  
1198  	asprintf(&tv[0], "%llu", wt);
1199  
1200  	a->mod_op = LDAP_MOD_REPLACE;
1201  	a->mod_type = "accountExpires";
1202  	a->mod_values = tv;
1203  	a++;
1204      }
1205  
1206      vals = ldap_get_values(CTX2LP(context), m0, "distinguishedName");
1207      if (vals == NULL) {
1208  	ret = KADM5_RPC_ERROR;
1209  	goto out;
1210      }
1211      dn = vals[0];
1212  
1213      attrs[a - &rattrs[0]] = NULL;
1214  
1215      ret = ldap_modify_s(CTX2LP(context), dn, attrs);
1216      if (check_ldap(context, ret))
1217  	return KADM5_RPC_ERROR;
1218  
1219   out:
1220      if (m)
1221  	ldap_msgfree(m);
1222      if (uaf[0])
1223  	free(uaf[0]);
1224      if (kvno[0])
1225  	free(kvno[0]);
1226      if (tv[0])
1227  	free(tv[0]);
1228      return ret;
1229  #else
1230      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1231      return KADM5_RPC_ERROR;
1232  #endif
1233  }
1234  
1235  /*ARGSUSED*/
1236  static kadm5_ret_t
1237  kadm5_ad_randkey_principal(void *server_handle,
1238  			   krb5_principal principal,
1239  			   krb5_boolean keepold,
1240  			   int n_ks_tuple,
1241  			   krb5_key_salt_tuple *ks_tuple,
1242  			   krb5_keyblock **keys,
1243  			   int *n_keys)
1244  {
1245      kadm5_ad_context *context = server_handle;
1246  
1247      if (keepold)
1248  	return KADM5_KEEPOLD_NOSUPP;
1249  
1250      /*
1251       * random key
1252       */
1253  
1254  #ifdef OPENLDAP
1255      krb5_data result_code_string, result_string;
1256      int result_code, plen;
1257      kadm5_ret_t ret;
1258      char *password;
1259  
1260      *keys = NULL;
1261      *n_keys = 0;
1262  
1263      {
1264  	char p[64];
1265  	krb5_generate_random_block(p, sizeof(p));
1266  	plen = base64_encode(p, sizeof(p), &password);
1267  	if (plen < 0)
1268  	    return ENOMEM;
1269      }
1270  
1271      ret = ad_get_cred(context, NULL);
1272      if (ret) {
1273  	free(password);
1274  	return ret;
1275      }
1276  
1277      krb5_data_zero (&result_code_string);
1278      krb5_data_zero (&result_string);
1279  
1280      ret = krb5_set_password_using_ccache (context->context,
1281  					  context->ccache,
1282  					  password,
1283  					  principal,
1284  					  &result_code,
1285  					  &result_code_string,
1286  					  &result_string);
1287  
1288      krb5_data_free (&result_code_string);
1289      krb5_data_free (&result_string);
1290  
1291      if (ret == 0) {
1292  
1293  	*keys = malloc(sizeof(**keys) * 1);
1294  	if (*keys == NULL) {
1295  	    ret = ENOMEM;
1296  	    goto out;
1297  	}
1298  	*n_keys = 1;
1299  
1300  	ret = krb5_string_to_key(context->context,
1301  				 ENCTYPE_ARCFOUR_HMAC_MD5,
1302  				 password,
1303  				 principal,
1304  				 &(*keys)[0]);
1305  	memset(password, 0, sizeof(password));
1306  	if (ret) {
1307  	    free(*keys);
1308  	    *keys = NULL;
1309  	    *n_keys = 0;
1310  	    goto out;
1311  	}
1312      }
1313      memset(password, 0, plen);
1314      free(password);
1315   out:
1316      return ret;
1317  #else
1318      *keys = NULL;
1319      *n_keys = 0;
1320  
1321      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1322      return KADM5_RPC_ERROR;
1323  #endif
1324  }
1325  
1326  static kadm5_ret_t
1327  kadm5_ad_rename_principal(void *server_handle,
1328  			  krb5_principal from,
1329  			  krb5_principal to)
1330  {
1331      kadm5_ad_context *context = server_handle;
1332      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1333      return KADM5_RPC_ERROR;
1334  }
1335  
1336  static kadm5_ret_t
1337  kadm5_ad_chpass_principal_with_key(void *server_handle,
1338  				   krb5_principal princ,
1339  				   int keepold,
1340  				   int n_key_data,
1341  				   krb5_key_data *key_data)
1342  {
1343      kadm5_ad_context *context = server_handle;
1344      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1345      return KADM5_RPC_ERROR;
1346  }
1347  
1348  static kadm5_ret_t
1349  kadm5_ad_lock(void *server_handle)
1350  {
1351      return ENOTSUP;
1352  }
1353  
1354  static kadm5_ret_t
1355  kadm5_ad_unlock(void *server_handle)
1356  {
1357      return ENOTSUP;
1358  }
1359  
1360  static void
1361  set_funcs(kadm5_ad_context *c)
1362  {
1363  #define SET(C, F) (C)->funcs.F = kadm5_ad_ ## F
1364      SET(c, chpass_principal);
1365      SET(c, chpass_principal_with_key);
1366      SET(c, create_principal);
1367      SET(c, delete_principal);
1368      SET(c, destroy);
1369      SET(c, flush);
1370      SET(c, get_principal);
1371      SET(c, get_principals);
1372      SET(c, get_privs);
1373      SET(c, modify_principal);
1374      SET(c, randkey_principal);
1375      SET(c, rename_principal);
1376      SET(c, lock);
1377      SET(c, unlock);
1378  }
1379  
1380  kadm5_ret_t
1381  kadm5_ad_init_with_password_ctx(krb5_context context,
1382  				const char *client_name,
1383  				const char *password,
1384  				const char *service_name,
1385  				kadm5_config_params *realm_params,
1386  				unsigned long struct_version,
1387  				unsigned long api_version,
1388  				void **server_handle)
1389  {
1390      kadm5_ret_t ret;
1391      kadm5_ad_context *ctx;
1392  
1393      ctx = malloc(sizeof(*ctx));
1394      if(ctx == NULL)
1395  	return ENOMEM;
1396      memset(ctx, 0, sizeof(*ctx));
1397      set_funcs(ctx);
1398  
1399      ctx->context = context;
1400      krb5_add_et_list (context, initialize_kadm5_error_table_r);
1401  
1402      ret = krb5_parse_name(ctx->context, client_name, &ctx->caller);
1403      if(ret) {
1404  	free(ctx);
1405  	return ret;
1406      }
1407  
1408      if(realm_params->mask & KADM5_CONFIG_REALM) {
1409  	ret = 0;
1410  	ctx->realm = strdup(realm_params->realm);
1411  	if (ctx->realm == NULL)
1412  	    ret = ENOMEM;
1413      } else
1414  	ret = krb5_get_default_realm(ctx->context, &ctx->realm);
1415      if (ret) {
1416  	free(ctx);
1417  	return ret;
1418      }
1419  
1420      ctx->client_name = strdup(client_name);
1421  
1422      if(password != NULL && *password != '\0')
1423  	ret = ad_get_cred(ctx, password);
1424      else
1425  	ret = ad_get_cred(ctx, NULL);
1426      if(ret) {
1427  	kadm5_ad_destroy(ctx);
1428  	return ret;
1429      }
1430  
1431  #ifdef OPENLDAP
1432      ret = _kadm5_ad_connect(ctx);
1433      if (ret) {
1434  	kadm5_ad_destroy(ctx);
1435  	return ret;
1436      }
1437  #endif
1438  
1439      *server_handle = ctx;
1440      return 0;
1441  }
1442  
1443  kadm5_ret_t
1444  kadm5_ad_init_with_password(const char *client_name,
1445  			    const char *password,
1446  			    const char *service_name,
1447  			    kadm5_config_params *realm_params,
1448  			    unsigned long struct_version,
1449  			    unsigned long api_version,
1450  			    void **server_handle)
1451  {
1452      krb5_context context;
1453      kadm5_ret_t ret;
1454      kadm5_ad_context *ctx;
1455  
1456      ret = krb5_init_context(&context);
1457      if (ret)
1458  	return ret;
1459      ret = kadm5_ad_init_with_password_ctx(context,
1460  					  client_name,
1461  					  password,
1462  					  service_name,
1463  					  realm_params,
1464  					  struct_version,
1465  					  api_version,
1466  					  server_handle);
1467      if(ret) {
1468  	krb5_free_context(context);
1469  	return ret;
1470      }
1471      ctx = *server_handle;
1472      ctx->my_context = 1;
1473      return 0;
1474  }