xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/kadm5/ad.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
1 /*	$NetBSD: ad.c,v 1.6 2023/06/19 21:41:44 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 2004 Kungliga Tekniska Högskolan
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
sasl_interact(LDAP * ld,unsigned flags,void * defaults,void * interact)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
ldap_tsasl_bind_s(LDAP * ld,LDAP_CONST char * dn,LDAPControl ** serverControls,LDAPControl ** clientControls,const char * host)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
check_ldap(kadm5_ad_context * context,int ret)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
laddattr(char *** al,int * attrlen,char * attr)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
_kadm5_ad_connect(void * server_handle)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
nt2unixtime(const char * str)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
unix2nttime(time_t unix_time)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
ad_find_entry(kadm5_ad_context * context,const char * fqdn,const char * pn,char ** name)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
ad_get_cred(kadm5_ad_context * context,const char * password)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
kadm5_ad_chpass_principal(void * server_handle,krb5_principal principal,int keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,const char * password)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 *
get_fqdn(krb5_context context,const krb5_principal p)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
kadm5_ad_create_principal(void * server_handle,kadm5_principal_ent_t entry,uint32_t mask,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,const char * password)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
kadm5_ad_delete_principal(void * server_handle,krb5_principal principal)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
kadm5_ad_destroy(void * server_handle)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
kadm5_ad_flush(void * server_handle)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
kadm5_ad_get_principal(void * server_handle,krb5_principal principal,kadm5_principal_ent_t entry,uint32_t mask)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
kadm5_ad_get_principals(void * server_handle,const char * expression,char *** principals,int * count)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
kadm5_ad_get_privs(void * server_handle,uint32_t * privs)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
kadm5_ad_modify_principal(void * server_handle,kadm5_principal_ent_t entry,uint32_t mask)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
kadm5_ad_randkey_principal(void * server_handle,krb5_principal principal,krb5_boolean keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,krb5_keyblock ** keys,int * n_keys)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
kadm5_ad_rename_principal(void * server_handle,krb5_principal from,krb5_principal to)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
kadm5_ad_chpass_principal_with_key(void * server_handle,krb5_principal princ,int keepold,int n_key_data,krb5_key_data * key_data)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
kadm5_ad_lock(void * server_handle)1362 kadm5_ad_lock(void *server_handle)
1363 {
1364     return ENOTSUP;
1365 }
1366 
1367 static kadm5_ret_t
kadm5_ad_unlock(void * server_handle)1368 kadm5_ad_unlock(void *server_handle)
1369 {
1370     return ENOTSUP;
1371 }
1372 
1373 static void
set_funcs(kadm5_ad_context * c)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
kadm5_ad_init_with_password_ctx(krb5_context context,const char * client_name,const char * password,const char * service_name,kadm5_config_params * realm_params,unsigned long struct_version,unsigned long api_version,void ** server_handle)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
kadm5_ad_init_with_password(const char * client_name,const char * password,const char * service_name,kadm5_config_params * realm_params,unsigned long struct_version,unsigned long api_version,void ** server_handle)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