Remove building with NOCRYPTO option
[minix.git] / crypto / external / bsd / heimdal / dist / lib / kadm5 / ad.c
blob05557ee5f633ad5ee2c78b710057e3e6927d9f06
1 /* $NetBSD: ad.c,v 1.3 2014/04/24 13:45:34 pettai Exp $ */
3 /*
4 * Copyright (c) 2004 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 #define HAVE_TSASL 1
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
52 __RCSID("NetBSD");
54 #ifdef OPENLDAP
56 #define CTX2LP(context) ((LDAP *)((context)->ldap_conn))
57 #define CTX2BASE(context) ((context)->base_dn)
60 * userAccountControl
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
100 #ifndef HAVE_TSASL
101 static int
102 sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *interact)
104 return LDAP_SUCCESS;
106 #endif
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 */
117 #endif
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)
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;
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;
143 rc = ldap_search_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, &m0);
144 if (rc != LDAP_SUCCESS)
145 goto out;
147 m = ldap_first_entry(ld, m0);
148 if (m == NULL) {
149 ldap_msgfree(m0);
150 goto out;
153 vals = ldap_get_values(ld, m, "supportedSASLMechanisms");
154 if (vals == NULL) {
155 ldap_msgfree(m0);
156 goto out;
159 ret = tsasl_find_best_mech(peer, vals, &mech);
160 if (ret) {
161 ldap_msgfree(m0);
162 goto out;
165 ldap_msgfree(m0);
167 ret = tsasl_select_mech(peer, mech);
168 if (ret != TSASL_DONE) {
169 rc = LDAP_LOCAL_ERROR;
170 goto out;
173 in.tb_data = NULL;
174 in.tb_size = 0;
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;
183 if (ret != TSASL_DONE && ret != TSASL_CONTINUE) {
184 rc = LDAP_AUTH_UNKNOWN;
185 goto out;
188 ccred.bv_val = out.tb_data;
189 ccred.bv_len = out.tb_size;
191 rc = ldap_sasl_bind_s(ld, dn, mech, &ccred,
192 serverControls, clientControls, &scred);
193 tsasl_buffer_free(&out);
195 if (rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS) {
196 if(scred && scred->bv_len)
197 ber_bvfree(scred);
198 goto out;
201 in.tb_data = malloc(scred->bv_len);
202 if (in.tb_data == NULL) {
203 rc = LDAP_LOCAL_ERROR;
204 goto out;
206 memcpy(in.tb_data, scred->bv_val, scred->bv_len);
207 in.tb_size = scred->bv_len;
208 ber_bvfree(scred);
210 } while (rc == LDAP_SASL_BIND_IN_PROGRESS);
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);
218 #endif
219 } else if (peer != NULL)
220 tsasl_peer_free(peer);
222 return rc;
224 #endif /* HAVE_TSASL */
227 static int
228 check_ldap(kadm5_ad_context *context, int ret)
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;
241 default:
242 return 1;
250 static void
251 laddattr(char ***al, int *attrlen, char *attr)
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;
263 static kadm5_ret_t
264 _kadm5_ad_connect(void *server_handle)
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;
273 if (context->ldap_conn)
274 return 0;
277 struct dns_reply *r;
278 struct resource_record *rr;
279 char *domain;
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;
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;
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;
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);
308 dns_free_data(r);
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;
316 for (i = 0; i < num_servers; i++) {
317 int lret, version = LDAP_VERSION3;
318 LDAP *lp;
320 lp = ldap_init(servers[i].server, servers[i].port);
321 if (lp == NULL)
322 continue;
324 if (ldap_set_option(lp, LDAP_OPT_PROTOCOL_VERSION, &version)) {
325 ldap_unbind(lp);
326 continue;
329 if (ldap_set_option(lp, LDAP_OPT_REFERRALS, LDAP_OPT_OFF)) {
330 ldap_unbind(lp);
331 continue;
334 #ifdef HAVE_TSASL
335 lret = ldap_tsasl_bind_s(lp, NULL, NULL, NULL, servers[i].server);
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;
350 context->ldap_conn = lp;
351 break;
353 if (i >= num_servers) {
354 goto fail;
358 LDAPMessage *m, *m0;
359 char **attr = NULL;
360 int attrlen = 0;
361 char **vals;
362 int ret;
364 laddattr(&attr, &attrlen, "defaultNamingContext");
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;
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;
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;
387 context->base_dn = strdup(vals[0]);
388 } else
389 goto fail;
390 ldap_msgfree(m);
393 for (i = 0; i < num_servers; i++)
394 free(servers[i].server);
395 free(servers);
397 return 0;
399 fail:
400 for (i = 0; i < num_servers; i++)
401 free(servers[i].server);
402 free(servers);
404 if (context->ldap_conn) {
405 ldap_unbind(CTX2LP(context));
406 context->ldap_conn = NULL;
408 return KADM5_RPC_ERROR;
411 #define NTTIME_EPOCH 0x019DB1DED53E8000LL
413 static time_t
414 nt2unixtime(const char *str)
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;
424 static long long
425 unix2nttime(time_t unix_time)
427 long long wt;
428 wt = unix_time * (long long)10000000 + (long long)NTTIME_EPOCH;
429 return wt;
432 /* XXX create filter in a better way */
434 static int
435 ad_find_entry(kadm5_ad_context *context,
436 const char *fqdn,
437 const char *pn,
438 char **name)
440 LDAPMessage *m, *m0;
441 char *attr[] = { "distinguishedName", NULL };
442 char *filter;
443 int ret;
445 if (name)
446 *name = NULL;
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;
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;
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;
472 if (name)
473 *name = strdup(vals[0]);
474 ldap_msgfree(m);
475 } else
476 return KADM5_UNK_PRINC;
478 return 0;
481 #endif /* OPENLDAP */
483 static kadm5_ret_t
484 ad_get_cred(kadm5_ad_context *context, const char *password)
486 kadm5_ret_t ret;
487 krb5_ccache cc;
488 char *service;
490 if (context->ccache)
491 return 0;
493 asprintf(&service, "%s/%s@%s", KRB5_TGS_NAME,
494 context->realm, context->realm);
495 if (service == NULL)
496 return ENOMEM;
498 ret = _kadm5_c_get_cred_cache(context->context,
499 context->client_name,
500 service,
501 password, krb5_prompter_posix,
502 NULL, NULL, &cc);
503 free(service);
504 if(ret)
505 return ret; /* XXX */
506 context->ccache = cc;
507 return 0;
510 static kadm5_ret_t
511 kadm5_ad_chpass_principal(void *server_handle,
512 krb5_principal principal,
513 const char *password)
515 kadm5_ad_context *context = server_handle;
516 krb5_data result_code_string, result_string;
517 int result_code;
518 kadm5_ret_t ret;
520 ret = ad_get_cred(context, NULL);
521 if (ret)
522 return ret;
524 krb5_data_zero (&result_code_string);
525 krb5_data_zero (&result_string);
527 ret = krb5_set_password_using_ccache (context->context,
528 context->ccache,
529 password,
530 principal,
531 &result_code,
532 &result_code_string,
533 &result_string);
535 krb5_data_free (&result_code_string);
536 krb5_data_free (&result_string);
538 /* XXX do mapping here on error codes */
540 return ret;
543 #ifdef OPENLDAP
544 static const char *
545 get_fqdn(krb5_context context, const krb5_principal p)
547 const char *s, *hosttypes[] = { "host", "ldap", "gc", "cifs", "dns" };
548 int i;
550 s = krb5_principal_get_comp_string(context, p, 0);
551 if (p == NULL)
552 return NULL;
554 for (i = 0; i < sizeof(hosttypes)/sizeof(hosttypes[0]); i++) {
555 if (strcasecmp(s, hosttypes[i]) == 0)
556 return krb5_principal_get_comp_string(context, p, 1);
558 return 0;
560 #endif
563 static kadm5_ret_t
564 kadm5_ad_create_principal(void *server_handle,
565 kadm5_principal_ent_t entry,
566 uint32_t mask,
567 const char *password)
569 kadm5_ad_context *context = server_handle;
572 * KADM5_PRINC_EXPIRE_TIME
574 * return 0 || KADM5_DUP;
577 #ifdef OPENLDAP
578 LDAPMod *attrs[8], rattrs[7], *a;
579 char *useraccvals[2] = { NULL, NULL },
580 *samvals[2], *dnsvals[2], *spnvals[5], *upnvals[2], *tv[2];
581 char *ocvals_spn[] = { "top", "person", "organizationalPerson",
582 "user", "computer", NULL};
583 char *p, *realmless_p, *p_msrealm = NULL, *dn = NULL;
584 const char *fqdn;
585 char *s, *samname = NULL, *short_spn = NULL;
586 int ret, i;
587 int32_t uf_flags = 0;
589 if ((mask & KADM5_PRINCIPAL) == 0)
590 return KADM5_BAD_MASK;
592 for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++)
593 attrs[i] = &rattrs[i];
594 attrs[i] = NULL;
596 ret = ad_get_cred(context, NULL);
597 if (ret)
598 return ret;
600 ret = _kadm5_ad_connect(server_handle);
601 if (ret)
602 return ret;
604 fqdn = get_fqdn(context->context, entry->principal);
606 ret = krb5_unparse_name(context->context, entry->principal, &p);
607 if (ret)
608 return ret;
610 if (ad_find_entry(context, fqdn, p, NULL) == 0) {
611 free(p);
612 return KADM5_DUP;
615 if (mask & KADM5_ATTRIBUTES) {
616 if (entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX)
617 uf_flags |= UF_ACCOUNTDISABLE|UF_LOCKOUT;
618 if ((entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) == 0)
619 uf_flags |= UF_DONT_REQUIRE_PREAUTH;
620 if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH)
621 uf_flags |= UF_SMARTCARD_REQUIRED;
624 realmless_p = strdup(p);
625 if (realmless_p == NULL) {
626 ret = ENOMEM;
627 goto out;
629 s = strrchr(realmless_p, '@');
630 if (s)
631 *s = '\0';
633 if (fqdn) {
634 /* create computer account */
635 asprintf(&samname, "%s$", fqdn);
636 if (samname == NULL) {
637 ret = ENOMEM;
638 goto out;
640 s = strchr(samname, '.');
641 if (s) {
642 s[0] = '$';
643 s[1] = '\0';
646 short_spn = strdup(p);
647 if (short_spn == NULL) {
648 errno = ENOMEM;
649 goto out;
651 s = strchr(short_spn, '.');
652 if (s) {
653 *s = '\0';
654 } else {
655 free(short_spn);
656 short_spn = NULL;
659 p_msrealm = strdup(p);
660 if (p_msrealm == NULL) {
661 errno = ENOMEM;
662 goto out;
664 s = strrchr(p_msrealm, '@');
665 if (s) {
666 *s = '/';
667 } else {
668 free(p_msrealm);
669 p_msrealm = NULL;
672 asprintf(&dn, "cn=%s, cn=Computers, %s", fqdn, CTX2BASE(context));
673 if (dn == NULL) {
674 ret = ENOMEM;
675 goto out;
678 a = &rattrs[0];
679 a->mod_op = LDAP_MOD_ADD;
680 a->mod_type = "objectClass";
681 a->mod_values = ocvals_spn;
682 a++;
684 a->mod_op = LDAP_MOD_ADD;
685 a->mod_type = "userAccountControl";
686 a->mod_values = useraccvals;
687 asprintf(&useraccvals[0], "%d",
688 uf_flags |
689 UF_PASSWD_NOT_EXPIRE |
690 UF_WORKSTATION_TRUST_ACCOUNT);
691 useraccvals[1] = NULL;
692 a++;
694 a->mod_op = LDAP_MOD_ADD;
695 a->mod_type = "sAMAccountName";
696 a->mod_values = samvals;
697 samvals[0] = samname;
698 samvals[1] = NULL;
699 a++;
701 a->mod_op = LDAP_MOD_ADD;
702 a->mod_type = "dNSHostName";
703 a->mod_values = dnsvals;
704 dnsvals[0] = (char *)fqdn;
705 dnsvals[1] = NULL;
706 a++;
708 /* XXX add even more spn's */
709 a->mod_op = LDAP_MOD_ADD;
710 a->mod_type = "servicePrincipalName";
711 a->mod_values = spnvals;
712 i = 0;
713 spnvals[i++] = p;
714 spnvals[i++] = realmless_p;
715 if (short_spn)
716 spnvals[i++] = short_spn;
717 if (p_msrealm)
718 spnvals[i++] = p_msrealm;
719 spnvals[i++] = NULL;
720 a++;
722 a->mod_op = LDAP_MOD_ADD;
723 a->mod_type = "userPrincipalName";
724 a->mod_values = upnvals;
725 upnvals[0] = p;
726 upnvals[1] = NULL;
727 a++;
729 a->mod_op = LDAP_MOD_ADD;
730 a->mod_type = "accountExpires";
731 a->mod_values = tv;
732 tv[0] = "9223372036854775807"; /* "never" */
733 tv[1] = NULL;
734 a++;
736 } else {
737 /* create user account */
739 a = &rattrs[0];
740 a->mod_op = LDAP_MOD_ADD;
741 a->mod_type = "userAccountControl";
742 a->mod_values = useraccvals;
743 asprintf(&useraccvals[0], "%d",
744 uf_flags |
745 UF_PASSWD_NOT_EXPIRE);
746 useraccvals[1] = NULL;
747 a++;
749 a->mod_op = LDAP_MOD_ADD;
750 a->mod_type = "sAMAccountName";
751 a->mod_values = samvals;
752 samvals[0] = realmless_p;
753 samvals[1] = NULL;
754 a++;
756 a->mod_op = LDAP_MOD_ADD;
757 a->mod_type = "userPrincipalName";
758 a->mod_values = upnvals;
759 upnvals[0] = p;
760 upnvals[1] = NULL;
761 a++;
763 a->mod_op = LDAP_MOD_ADD;
764 a->mod_type = "accountExpires";
765 a->mod_values = tv;
766 tv[0] = "9223372036854775807"; /* "never" */
767 tv[1] = NULL;
768 a++;
771 attrs[a - &rattrs[0]] = NULL;
773 ret = ldap_add_s(CTX2LP(context), dn, attrs);
775 out:
776 if (useraccvals[0])
777 free(useraccvals[0]);
778 if (realmless_p)
779 free(realmless_p);
780 if (samname)
781 free(samname);
782 if (short_spn)
783 free(short_spn);
784 if (p_msrealm)
785 free(p_msrealm);
786 free(p);
788 if (check_ldap(context, ret))
789 return KADM5_RPC_ERROR;
791 return 0;
792 #else
793 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
794 return KADM5_RPC_ERROR;
795 #endif
798 static kadm5_ret_t
799 kadm5_ad_delete_principal(void *server_handle, krb5_principal principal)
801 kadm5_ad_context *context = server_handle;
802 #ifdef OPENLDAP
803 char *p, *dn = NULL;
804 const char *fqdn;
805 int ret;
807 ret = ad_get_cred(context, NULL);
808 if (ret)
809 return ret;
811 ret = _kadm5_ad_connect(server_handle);
812 if (ret)
813 return ret;
815 fqdn = get_fqdn(context->context, principal);
817 ret = krb5_unparse_name(context->context, principal, &p);
818 if (ret)
819 return ret;
821 if (ad_find_entry(context, fqdn, p, &dn) != 0) {
822 free(p);
823 return KADM5_UNK_PRINC;
826 ret = ldap_delete_s(CTX2LP(context), dn);
828 free(dn);
829 free(p);
831 if (check_ldap(context, ret))
832 return KADM5_RPC_ERROR;
833 return 0;
834 #else
835 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
836 return KADM5_RPC_ERROR;
837 #endif
840 static kadm5_ret_t
841 kadm5_ad_destroy(void *server_handle)
843 kadm5_ad_context *context = server_handle;
845 if (context->ccache)
846 krb5_cc_destroy(context->context, context->ccache);
848 #ifdef OPENLDAP
850 LDAP *lp = CTX2LP(context);
851 if (lp)
852 ldap_unbind(lp);
853 if (context->base_dn)
854 free(context->base_dn);
856 #endif
857 free(context->realm);
858 free(context->client_name);
859 krb5_free_principal(context->context, context->caller);
860 if(context->my_context)
861 krb5_free_context(context->context);
862 return 0;
865 static kadm5_ret_t
866 kadm5_ad_flush(void *server_handle)
868 kadm5_ad_context *context = server_handle;
869 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
870 return KADM5_RPC_ERROR;
873 static kadm5_ret_t
874 kadm5_ad_get_principal(void *server_handle,
875 krb5_principal principal,
876 kadm5_principal_ent_t entry,
877 uint32_t mask)
879 kadm5_ad_context *context = server_handle;
880 #ifdef OPENLDAP
881 LDAPMessage *m, *m0;
882 char **attr = NULL;
883 int attrlen = 0;
884 char *filter, *p, *q, *u;
885 int ret;
888 * principal
889 * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES
893 * return 0 || KADM5_DUP;
896 memset(entry, 0, sizeof(*entry));
898 if (mask & KADM5_KVNO)
899 laddattr(&attr, &attrlen, "msDS-KeyVersionNumber");
901 if (mask & KADM5_PRINCIPAL) {
902 laddattr(&attr, &attrlen, "userPrincipalName");
903 laddattr(&attr, &attrlen, "servicePrincipalName");
905 laddattr(&attr, &attrlen, "objectClass");
906 laddattr(&attr, &attrlen, "lastLogon");
907 laddattr(&attr, &attrlen, "badPwdCount");
908 laddattr(&attr, &attrlen, "badPasswordTime");
909 laddattr(&attr, &attrlen, "pwdLastSet");
910 laddattr(&attr, &attrlen, "accountExpires");
911 laddattr(&attr, &attrlen, "userAccountControl");
913 krb5_unparse_name_short(context->context, principal, &p);
914 krb5_unparse_name(context->context, principal, &u);
916 /* replace @ in domain part with a / */
917 q = strrchr(p, '@');
918 if (q && (p != q && *(q - 1) != '\\'))
919 *q = '/';
921 asprintf(&filter,
922 "(|(userPrincipalName=%s)(servicePrincipalName=%s)(servicePrincipalName=%s))",
923 u, p, u);
924 free(p);
925 free(u);
927 ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
928 LDAP_SCOPE_SUBTREE,
929 filter, attr, 0, &m);
930 free(attr);
931 if (check_ldap(context, ret))
932 return KADM5_RPC_ERROR;
934 if (ldap_count_entries(CTX2LP(context), m) > 0) {
935 char **vals;
936 m0 = ldap_first_entry(CTX2LP(context), m);
937 if (m0 == NULL) {
938 ldap_msgfree(m);
939 goto fail;
941 #if 0
942 vals = ldap_get_values(CTX2LP(context), m0, "servicePrincipalName");
943 if (vals)
944 printf("servicePrincipalName %s\n", vals[0]);
945 vals = ldap_get_values(CTX2LP(context), m0, "userPrincipalName");
946 if (vals)
947 printf("userPrincipalName %s\n", vals[0]);
948 vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
949 if (vals)
950 printf("userAccountControl %s\n", vals[0]);
951 #endif
952 entry->princ_expire_time = 0;
953 if (mask & KADM5_PRINC_EXPIRE_TIME) {
954 vals = ldap_get_values(CTX2LP(context), m0, "accountExpires");
955 if (vals)
956 entry->princ_expire_time = nt2unixtime(vals[0]);
958 entry->last_success = 0;
959 if (mask & KADM5_LAST_SUCCESS) {
960 vals = ldap_get_values(CTX2LP(context), m0, "lastLogon");
961 if (vals)
962 entry->last_success = nt2unixtime(vals[0]);
964 if (mask & KADM5_LAST_FAILED) {
965 vals = ldap_get_values(CTX2LP(context), m0, "badPasswordTime");
966 if (vals)
967 entry->last_failed = nt2unixtime(vals[0]);
969 if (mask & KADM5_LAST_PWD_CHANGE) {
970 vals = ldap_get_values(CTX2LP(context), m0, "pwdLastSet");
971 if (vals)
972 entry->last_pwd_change = nt2unixtime(vals[0]);
974 if (mask & KADM5_FAIL_AUTH_COUNT) {
975 vals = ldap_get_values(CTX2LP(context), m0, "badPwdCount");
976 if (vals)
977 entry->fail_auth_count = atoi(vals[0]);
979 if (mask & KADM5_ATTRIBUTES) {
980 vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
981 if (vals) {
982 uint32_t i;
983 i = atoi(vals[0]);
984 if (i & (UF_ACCOUNTDISABLE|UF_LOCKOUT))
985 entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
986 if ((i & UF_DONT_REQUIRE_PREAUTH) == 0)
987 entry->attributes |= KRB5_KDB_REQUIRES_PRE_AUTH;
988 if (i & UF_SMARTCARD_REQUIRED)
989 entry->attributes |= KRB5_KDB_REQUIRES_HW_AUTH;
990 if ((i & UF_WORKSTATION_TRUST_ACCOUNT) == 0)
991 entry->attributes |= KRB5_KDB_DISALLOW_SVR;
994 if (mask & KADM5_KVNO) {
995 vals = ldap_get_values(CTX2LP(context), m0,
996 "msDS-KeyVersionNumber");
997 if (vals)
998 entry->kvno = atoi(vals[0]);
999 else
1000 entry->kvno = 0;
1002 ldap_msgfree(m);
1003 } else {
1004 return KADM5_UNK_PRINC;
1007 if (mask & KADM5_PRINCIPAL)
1008 krb5_copy_principal(context->context, principal, &entry->principal);
1010 return 0;
1011 fail:
1012 return KADM5_RPC_ERROR;
1013 #else
1014 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1015 return KADM5_RPC_ERROR;
1016 #endif
1019 static kadm5_ret_t
1020 kadm5_ad_get_principals(void *server_handle,
1021 const char *expression,
1022 char ***principals,
1023 int *count)
1025 kadm5_ad_context *context = server_handle;
1028 * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES
1031 #ifdef OPENLDAP
1032 kadm5_ret_t ret;
1034 ret = ad_get_cred(context, NULL);
1035 if (ret)
1036 return ret;
1038 ret = _kadm5_ad_connect(server_handle);
1039 if (ret)
1040 return ret;
1042 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1043 return KADM5_RPC_ERROR;
1044 #else
1045 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1046 return KADM5_RPC_ERROR;
1047 #endif
1050 static kadm5_ret_t
1051 kadm5_ad_get_privs(void *server_handle, uint32_t*privs)
1053 kadm5_ad_context *context = server_handle;
1054 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1055 return KADM5_RPC_ERROR;
1058 static kadm5_ret_t
1059 kadm5_ad_modify_principal(void *server_handle,
1060 kadm5_principal_ent_t entry,
1061 uint32_t mask)
1063 kadm5_ad_context *context = server_handle;
1066 * KADM5_ATTRIBUTES
1067 * KRB5_KDB_DISALLOW_ALL_TIX (| KADM5_KVNO)
1070 #ifdef OPENLDAP
1071 LDAPMessage *m = NULL, *m0;
1072 kadm5_ret_t ret;
1073 char **attr = NULL;
1074 int attrlen = 0;
1075 char *p = NULL, *s = NULL, *q;
1076 char **vals;
1077 LDAPMod *attrs[4], rattrs[3], *a;
1078 char *uaf[2] = { NULL, NULL };
1079 char *kvno[2] = { NULL, NULL };
1080 char *tv[2] = { NULL, NULL };
1081 char *filter, *dn;
1082 int i;
1084 for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++)
1085 attrs[i] = &rattrs[i];
1086 attrs[i] = NULL;
1087 a = &rattrs[0];
1089 ret = _kadm5_ad_connect(server_handle);
1090 if (ret)
1091 return ret;
1093 if (mask & KADM5_KVNO)
1094 laddattr(&attr, &attrlen, "msDS-KeyVersionNumber");
1095 if (mask & KADM5_PRINC_EXPIRE_TIME)
1096 laddattr(&attr, &attrlen, "accountExpires");
1097 if (mask & KADM5_ATTRIBUTES)
1098 laddattr(&attr, &attrlen, "userAccountControl");
1099 laddattr(&attr, &attrlen, "distinguishedName");
1101 krb5_unparse_name(context->context, entry->principal, &p);
1103 s = strdup(p);
1105 q = strrchr(s, '@');
1106 if (q && (p != q && *(q - 1) != '\\'))
1107 *q = '\0';
1109 asprintf(&filter,
1110 "(|(userPrincipalName=%s)(servicePrincipalName=%s))",
1111 s, s);
1112 free(p);
1113 free(s);
1115 ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
1116 LDAP_SCOPE_SUBTREE,
1117 filter, attr, 0, &m);
1118 free(attr);
1119 free(filter);
1120 if (check_ldap(context, ret))
1121 return KADM5_RPC_ERROR;
1123 if (ldap_count_entries(CTX2LP(context), m) <= 0) {
1124 ret = KADM5_RPC_ERROR;
1125 goto out;
1128 m0 = ldap_first_entry(CTX2LP(context), m);
1130 if (mask & KADM5_ATTRIBUTES) {
1131 int32_t i;
1133 vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
1134 if (vals == NULL) {
1135 ret = KADM5_RPC_ERROR;
1136 goto out;
1139 i = atoi(vals[0]);
1140 if (i == 0)
1141 return KADM5_RPC_ERROR;
1143 if (entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX)
1144 i |= (UF_ACCOUNTDISABLE|UF_LOCKOUT);
1145 else
1146 i &= ~(UF_ACCOUNTDISABLE|UF_LOCKOUT);
1147 if (entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH)
1148 i &= ~UF_DONT_REQUIRE_PREAUTH;
1149 else
1150 i |= UF_DONT_REQUIRE_PREAUTH;
1151 if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH)
1152 i |= UF_SMARTCARD_REQUIRED;
1153 else
1154 i &= UF_SMARTCARD_REQUIRED;
1155 if (entry->attributes & KRB5_KDB_DISALLOW_SVR)
1156 i &= ~UF_WORKSTATION_TRUST_ACCOUNT;
1157 else
1158 i |= UF_WORKSTATION_TRUST_ACCOUNT;
1160 asprintf(&uaf[0], "%d", i);
1162 a->mod_op = LDAP_MOD_REPLACE;
1163 a->mod_type = "userAccountControl";
1164 a->mod_values = uaf;
1165 a++;
1168 if (mask & KADM5_KVNO) {
1169 vals = ldap_get_values(CTX2LP(context), m0, "msDS-KeyVersionNumber");
1170 if (vals == NULL) {
1171 entry->kvno = 0;
1172 } else {
1173 asprintf(&kvno[0], "%d", entry->kvno);
1175 a->mod_op = LDAP_MOD_REPLACE;
1176 a->mod_type = "msDS-KeyVersionNumber";
1177 a->mod_values = kvno;
1178 a++;
1182 if (mask & KADM5_PRINC_EXPIRE_TIME) {
1183 long long wt;
1184 vals = ldap_get_values(CTX2LP(context), m0, "accountExpires");
1185 if (vals == NULL) {
1186 ret = KADM5_RPC_ERROR;
1187 goto out;
1190 wt = unix2nttime(entry->princ_expire_time);
1192 asprintf(&tv[0], "%llu", wt);
1194 a->mod_op = LDAP_MOD_REPLACE;
1195 a->mod_type = "accountExpires";
1196 a->mod_values = tv;
1197 a++;
1200 vals = ldap_get_values(CTX2LP(context), m0, "distinguishedName");
1201 if (vals == NULL) {
1202 ret = KADM5_RPC_ERROR;
1203 goto out;
1205 dn = vals[0];
1207 attrs[a - &rattrs[0]] = NULL;
1209 ret = ldap_modify_s(CTX2LP(context), dn, attrs);
1210 if (check_ldap(context, ret))
1211 return KADM5_RPC_ERROR;
1213 out:
1214 if (m)
1215 ldap_msgfree(m);
1216 if (uaf[0])
1217 free(uaf[0]);
1218 if (kvno[0])
1219 free(kvno[0]);
1220 if (tv[0])
1221 free(tv[0]);
1222 return ret;
1223 #else
1224 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1225 return KADM5_RPC_ERROR;
1226 #endif
1229 static kadm5_ret_t
1230 kadm5_ad_randkey_principal(void *server_handle,
1231 krb5_principal principal,
1232 krb5_keyblock **keys,
1233 int *n_keys)
1235 kadm5_ad_context *context = server_handle;
1238 * random key
1241 #ifdef OPENLDAP
1242 krb5_data result_code_string, result_string;
1243 int result_code, plen;
1244 kadm5_ret_t ret;
1245 char *password;
1247 *keys = NULL;
1248 *n_keys = 0;
1251 char p[64];
1252 krb5_generate_random_block(p, sizeof(p));
1253 plen = base64_encode(p, sizeof(p), &password);
1254 if (plen < 0)
1255 return ENOMEM;
1258 ret = ad_get_cred(context, NULL);
1259 if (ret) {
1260 free(password);
1261 return ret;
1264 krb5_data_zero (&result_code_string);
1265 krb5_data_zero (&result_string);
1267 ret = krb5_set_password_using_ccache (context->context,
1268 context->ccache,
1269 password,
1270 principal,
1271 &result_code,
1272 &result_code_string,
1273 &result_string);
1275 krb5_data_free (&result_code_string);
1276 krb5_data_free (&result_string);
1278 if (ret == 0) {
1280 *keys = malloc(sizeof(**keys) * 1);
1281 if (*keys == NULL) {
1282 ret = ENOMEM;
1283 goto out;
1285 *n_keys = 1;
1287 ret = krb5_string_to_key(context->context,
1288 ENCTYPE_ARCFOUR_HMAC_MD5,
1289 password,
1290 principal,
1291 &(*keys)[0]);
1292 memset(password, 0, plen);
1293 if (ret) {
1294 free(*keys);
1295 *keys = NULL;
1296 *n_keys = 0;
1297 goto out;
1300 memset(password, 0, plen);
1301 free(password);
1302 out:
1303 return ret;
1304 #else
1305 *keys = NULL;
1306 *n_keys = 0;
1308 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1309 return KADM5_RPC_ERROR;
1310 #endif
1313 static kadm5_ret_t
1314 kadm5_ad_rename_principal(void *server_handle,
1315 krb5_principal from,
1316 krb5_principal to)
1318 kadm5_ad_context *context = server_handle;
1319 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1320 return KADM5_RPC_ERROR;
1323 static kadm5_ret_t
1324 kadm5_ad_chpass_principal_with_key(void *server_handle,
1325 krb5_principal princ,
1326 int n_key_data,
1327 krb5_key_data *key_data)
1329 kadm5_ad_context *context = server_handle;
1330 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1331 return KADM5_RPC_ERROR;
1334 static void
1335 set_funcs(kadm5_ad_context *c)
1337 #define SET(C, F) (C)->funcs.F = kadm5_ad_ ## F
1338 SET(c, chpass_principal);
1339 SET(c, chpass_principal_with_key);
1340 SET(c, create_principal);
1341 SET(c, delete_principal);
1342 SET(c, destroy);
1343 SET(c, flush);
1344 SET(c, get_principal);
1345 SET(c, get_principals);
1346 SET(c, get_privs);
1347 SET(c, modify_principal);
1348 SET(c, randkey_principal);
1349 SET(c, rename_principal);
1352 kadm5_ret_t
1353 kadm5_ad_init_with_password_ctx(krb5_context context,
1354 const char *client_name,
1355 const char *password,
1356 const char *service_name,
1357 kadm5_config_params *realm_params,
1358 unsigned long struct_version,
1359 unsigned long api_version,
1360 void **server_handle)
1362 kadm5_ret_t ret;
1363 kadm5_ad_context *ctx;
1365 ctx = malloc(sizeof(*ctx));
1366 if(ctx == NULL)
1367 return ENOMEM;
1368 memset(ctx, 0, sizeof(*ctx));
1369 set_funcs(ctx);
1371 ctx->context = context;
1372 krb5_add_et_list (context, initialize_kadm5_error_table_r);
1374 ret = krb5_parse_name(ctx->context, client_name, &ctx->caller);
1375 if(ret) {
1376 free(ctx);
1377 return ret;
1380 if(realm_params->mask & KADM5_CONFIG_REALM) {
1381 ret = 0;
1382 ctx->realm = strdup(realm_params->realm);
1383 if (ctx->realm == NULL)
1384 ret = ENOMEM;
1385 } else
1386 ret = krb5_get_default_realm(ctx->context, &ctx->realm);
1387 if (ret) {
1388 free(ctx);
1389 return ret;
1392 ctx->client_name = strdup(client_name);
1394 if(password != NULL && *password != '\0')
1395 ret = ad_get_cred(ctx, password);
1396 else
1397 ret = ad_get_cred(ctx, NULL);
1398 if(ret) {
1399 kadm5_ad_destroy(ctx);
1400 return ret;
1403 #ifdef OPENLDAP
1404 ret = _kadm5_ad_connect(ctx);
1405 if (ret) {
1406 kadm5_ad_destroy(ctx);
1407 return ret;
1409 #endif
1411 *server_handle = ctx;
1412 return 0;
1415 kadm5_ret_t
1416 kadm5_ad_init_with_password(const char *client_name,
1417 const char *password,
1418 const char *service_name,
1419 kadm5_config_params *realm_params,
1420 unsigned long struct_version,
1421 unsigned long api_version,
1422 void **server_handle)
1424 krb5_context context;
1425 kadm5_ret_t ret;
1426 kadm5_ad_context *ctx;
1428 ret = krb5_init_context(&context);
1429 if (ret)
1430 return ret;
1431 ret = kadm5_ad_init_with_password_ctx(context,
1432 client_name,
1433 password,
1434 service_name,
1435 realm_params,
1436 struct_version,
1437 api_version,
1438 server_handle);
1439 if(ret) {
1440 krb5_free_context(context);
1441 return ret;
1443 ctx = *server_handle;
1444 ctx->my_context = 1;
1445 return 0;