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