1/* $NetBSD: ad.c,v 1.1.1.1 2011/04/13 18:15:29 elric 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.1.1.1 2011/04/13 18:15:29 elric 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 101static int 102sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *interact) 103{ 104 return LDAP_SUCCESS; 105} 106#endif 107 108#if 0 109static 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 120static int 121ldap_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 227static int 228check_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 250static void 251laddattr(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 263static 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 413static time_t 414nt2unixtime(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 424static long long 425unix2nttime(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 434static int 435ad_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 483static kadm5_ret_t 484ad_get_cred(kadm5_ad_context *context, const char *password) 485{ 486 kadm5_ret_t ret; 487 krb5_ccache cc; 488 char *service; 489 490 if (context->ccache) 491 return 0; 492 493 asprintf(&service, "%s/%s@%s", KRB5_TGS_NAME, 494 context->realm, context->realm); 495 if (service == NULL) 496 return ENOMEM; 497 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; 508} 509 510static kadm5_ret_t 511kadm5_ad_chpass_principal(void *server_handle, 512 krb5_principal principal, 513 const char *password) 514{ 515 kadm5_ad_context *context = server_handle; 516 krb5_data result_code_string, result_string; 517 int result_code; 518 kadm5_ret_t ret; 519 520 ret = ad_get_cred(context, NULL); 521 if (ret) 522 return ret; 523 524 krb5_data_zero (&result_code_string); 525 krb5_data_zero (&result_string); 526 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); 534 535 krb5_data_free (&result_code_string); 536 krb5_data_free (&result_string); 537 538 /* XXX do mapping here on error codes */ 539 540 return ret; 541} 542 543#ifdef OPENLDAP 544static const char * 545get_fqdn(krb5_context context, const krb5_principal p) 546{ 547 const char *s, *hosttypes[] = { "host", "ldap", "gc", "cifs", "dns" }; 548 int i; 549 550 s = krb5_principal_get_comp_string(context, p, 0); 551 if (p == NULL) 552 return NULL; 553 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); 557 } 558 return 0; 559} 560#endif 561 562 563static kadm5_ret_t 564kadm5_ad_create_principal(void *server_handle, 565 kadm5_principal_ent_t entry, 566 uint32_t mask, 567 const char *password) 568{ 569 kadm5_ad_context *context = server_handle; 570 571 /* 572 * KADM5_PRINC_EXPIRE_TIME 573 * 574 * return 0 || KADM5_DUP; 575 */ 576 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; 588 589 if ((mask & KADM5_PRINCIPAL) == 0) 590 return KADM5_BAD_MASK; 591 592 for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++) 593 attrs[i] = &rattrs[i]; 594 attrs[i] = NULL; 595 596 ret = ad_get_cred(context, NULL); 597 if (ret) 598 return ret; 599 600 ret = _kadm5_ad_connect(server_handle); 601 if (ret) 602 return ret; 603 604 fqdn = get_fqdn(context->context, entry->principal); 605 606 ret = krb5_unparse_name(context->context, entry->principal, &p); 607 if (ret) 608 return ret; 609 610 if (ad_find_entry(context, fqdn, p, NULL) == 0) { 611 free(p); 612 return KADM5_DUP; 613 } 614 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; 622 } 623 624 realmless_p = strdup(p); 625 if (realmless_p == NULL) { 626 ret = ENOMEM; 627 goto out; 628 } 629 s = strrchr(realmless_p, '@'); 630 if (s) 631 *s = '\0'; 632 633 if (fqdn) { 634 /* create computer account */ 635 asprintf(&samname, "%s$", fqdn); 636 if (samname == NULL) { 637 ret = ENOMEM; 638 goto out; 639 } 640 s = strchr(samname, '.'); 641 if (s) { 642 s[0] = '$'; 643 s[1] = '\0'; 644 } 645 646 short_spn = strdup(p); 647 if (short_spn == NULL) { 648 errno = ENOMEM; 649 goto out; 650 } 651 s = strchr(short_spn, '.'); 652 if (s) { 653 *s = '\0'; 654 } else { 655 free(short_spn); 656 short_spn = NULL; 657 } 658 659 p_msrealm = strdup(p); 660 if (p_msrealm == NULL) { 661 errno = ENOMEM; 662 goto out; 663 } 664 s = strrchr(p_msrealm, '@'); 665 if (s) { 666 *s = '/'; 667 } else { 668 free(p_msrealm); 669 p_msrealm = NULL; 670 } 671 672 asprintf(&dn, "cn=%s, cn=Computers, %s", fqdn, CTX2BASE(context)); 673 if (dn == NULL) { 674 ret = ENOMEM; 675 goto out; 676 } 677 678 a = &rattrs[0]; 679 a->mod_op = LDAP_MOD_ADD; 680 a->mod_type = "objectClass"; 681 a->mod_values = ocvals_spn; 682 a++; 683 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++; 693 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++; 700 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++; 707 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++; 721 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++; 728 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++; 735 736 } else { 737 /* create user account */ 738 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++; 748 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++; 755 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++; 762 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++; 769 } 770 771 attrs[a - &rattrs[0]] = NULL; 772 773 ret = ldap_add_s(CTX2LP(context), dn, attrs); 774 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); 787 788 if (check_ldap(context, ret)) 789 return KADM5_RPC_ERROR; 790 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 796} 797 798static kadm5_ret_t 799kadm5_ad_delete_principal(void *server_handle, krb5_principal principal) 800{ 801 kadm5_ad_context *context = server_handle; 802#ifdef OPENLDAP 803 char *p, *dn = NULL; 804 const char *fqdn; 805 int ret; 806 807 ret = ad_get_cred(context, NULL); 808 if (ret) 809 return ret; 810 811 ret = _kadm5_ad_connect(server_handle); 812 if (ret) 813 return ret; 814 815 fqdn = get_fqdn(context->context, principal); 816 817 ret = krb5_unparse_name(context->context, principal, &p); 818 if (ret) 819 return ret; 820 821 if (ad_find_entry(context, fqdn, p, &dn) != 0) { 822 free(p); 823 return KADM5_UNK_PRINC; 824 } 825 826 ret = ldap_delete_s(CTX2LP(context), dn); 827 828 free(dn); 829 free(p); 830 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 838} 839 840static kadm5_ret_t 841kadm5_ad_destroy(void *server_handle) 842{ 843 kadm5_ad_context *context = server_handle; 844 845 if (context->ccache) 846 krb5_cc_destroy(context->context, context->ccache); 847 848#ifdef OPENLDAP 849 { 850 LDAP *lp = CTX2LP(context); 851 if (lp) 852 ldap_unbind(lp); 853 if (context->base_dn) 854 free(context->base_dn); 855 } 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; 863} 864 865static kadm5_ret_t 866kadm5_ad_flush(void *server_handle) 867{ 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; 871} 872 873static kadm5_ret_t 874kadm5_ad_get_principal(void *server_handle, 875 krb5_principal principal, 876 kadm5_principal_ent_t entry, 877 uint32_t mask) 878{ 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; 886 887 /* 888 * principal 889 * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES 890 */ 891 892 /* 893 * return 0 || KADM5_DUP; 894 */ 895 896 memset(entry, 0, sizeof(*entry)); 897 898 if (mask & KADM5_KVNO) 899 laddattr(&attr, &attrlen, "msDS-KeyVersionNumber"); 900 901 if (mask & KADM5_PRINCIPAL) { 902 laddattr(&attr, &attrlen, "userPrincipalName"); 903 laddattr(&attr, &attrlen, "servicePrincipalName"); 904 } 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"); 912 913 krb5_unparse_name_short(context->context, principal, &p); 914 krb5_unparse_name(context->context, principal, &u); 915 916 /* replace @ in domain part with a / */ 917 q = strrchr(p, '@'); 918 if (q && (p != q && *(q - 1) != '\\')) 919 *q = '/'; 920 921 asprintf(&filter, 922 "(|(userPrincipalName=%s)(servicePrincipalName=%s)(servicePrincipalName=%s))", 923 u, p, u); 924 free(p); 925 free(u); 926 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; 933 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; 940 } 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]); 957 } 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]); 963 } 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]); 968 } 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]); 973 } 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]); 978 } 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; 992 } 993 } 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; 1001 } 1002 ldap_msgfree(m); 1003 } else { 1004 return KADM5_UNK_PRINC; 1005 } 1006 1007 if (mask & KADM5_PRINCIPAL) 1008 krb5_copy_principal(context->context, principal, &entry->principal); 1009 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 1017} 1018 1019static kadm5_ret_t 1020kadm5_ad_get_principals(void *server_handle, 1021 const char *expression, 1022 char ***principals, 1023 int *count) 1024{ 1025 kadm5_ad_context *context = server_handle; 1026 1027 /* 1028 * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES 1029 */ 1030 1031#ifdef OPENLDAP 1032 kadm5_ret_t ret; 1033 1034 ret = ad_get_cred(context, NULL); 1035 if (ret) 1036 return ret; 1037 1038 ret = _kadm5_ad_connect(server_handle); 1039 if (ret) 1040 return ret; 1041 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 1048} 1049 1050static kadm5_ret_t 1051kadm5_ad_get_privs(void *server_handle, uint32_t*privs) 1052{ 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; 1056} 1057 1058static kadm5_ret_t 1059kadm5_ad_modify_principal(void *server_handle, 1060 kadm5_principal_ent_t entry, 1061 uint32_t mask) 1062{ 1063 kadm5_ad_context *context = server_handle; 1064 1065 /* 1066 * KADM5_ATTRIBUTES 1067 * KRB5_KDB_DISALLOW_ALL_TIX (| KADM5_KVNO) 1068 */ 1069 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; 1083 1084 for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++) 1085 attrs[i] = &rattrs[i]; 1086 attrs[i] = NULL; 1087 a = &rattrs[0]; 1088 1089 ret = _kadm5_ad_connect(server_handle); 1090 if (ret) 1091 return ret; 1092 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"); 1100 1101 krb5_unparse_name(context->context, entry->principal, &p); 1102 1103 s = strdup(p); 1104 1105 q = strrchr(s, '@'); 1106 if (q && (p != q && *(q - 1) != '\\')) 1107 *q = '\0'; 1108 1109 asprintf(&filter, 1110 "(|(userPrincipalName=%s)(servicePrincipalName=%s))", 1111 s, s); 1112 free(p); 1113 free(s); 1114 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; 1122 1123 if (ldap_count_entries(CTX2LP(context), m) <= 0) { 1124 ret = KADM5_RPC_ERROR; 1125 goto out; 1126 } 1127 1128 m0 = ldap_first_entry(CTX2LP(context), m); 1129 1130 if (mask & KADM5_ATTRIBUTES) { 1131 int32_t i; 1132 1133 vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl"); 1134 if (vals == NULL) { 1135 ret = KADM5_RPC_ERROR; 1136 goto out; 1137 } 1138 1139 i = atoi(vals[0]); 1140 if (i == 0) 1141 return KADM5_RPC_ERROR; 1142 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; 1159 1160 asprintf(&uaf[0], "%d", i); 1161 1162 a->mod_op = LDAP_MOD_REPLACE; 1163 a->mod_type = "userAccountControl"; 1164 a->mod_values = uaf; 1165 a++; 1166 } 1167 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); 1174 1175 a->mod_op = LDAP_MOD_REPLACE; 1176 a->mod_type = "msDS-KeyVersionNumber"; 1177 a->mod_values = kvno; 1178 a++; 1179 } 1180 } 1181 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; 1188 } 1189 1190 wt = unix2nttime(entry->princ_expire_time); 1191 1192 asprintf(&tv[0], "%llu", wt); 1193 1194 a->mod_op = LDAP_MOD_REPLACE; 1195 a->mod_type = "accountExpires"; 1196 a->mod_values = tv; 1197 a++; 1198 } 1199 1200 vals = ldap_get_values(CTX2LP(context), m0, "distinguishedName"); 1201 if (vals == NULL) { 1202 ret = KADM5_RPC_ERROR; 1203 goto out; 1204 } 1205 dn = vals[0]; 1206 1207 attrs[a - &rattrs[0]] = NULL; 1208 1209 ret = ldap_modify_s(CTX2LP(context), dn, attrs); 1210 if (check_ldap(context, ret)) 1211 return KADM5_RPC_ERROR; 1212 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 1227} 1228 1229static kadm5_ret_t 1230kadm5_ad_randkey_principal(void *server_handle, 1231 krb5_principal principal, 1232 krb5_keyblock **keys, 1233 int *n_keys) 1234{ 1235 kadm5_ad_context *context = server_handle; 1236 1237 /* 1238 * random key 1239 */ 1240 1241#ifdef OPENLDAP 1242 krb5_data result_code_string, result_string; 1243 int result_code, plen; 1244 kadm5_ret_t ret; 1245 char *password; 1246 1247 *keys = NULL; 1248 *n_keys = 0; 1249 1250 { 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; 1256 } 1257 1258 ret = ad_get_cred(context, NULL); 1259 if (ret) { 1260 free(password); 1261 return ret; 1262 } 1263 1264 krb5_data_zero (&result_code_string); 1265 krb5_data_zero (&result_string); 1266 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); 1274 1275 krb5_data_free (&result_code_string); 1276 krb5_data_free (&result_string); 1277 1278 if (ret == 0) { 1279 1280 *keys = malloc(sizeof(**keys) * 1); 1281 if (*keys == NULL) { 1282 ret = ENOMEM; 1283 goto out; 1284 } 1285 *n_keys = 1; 1286 1287 ret = krb5_string_to_key(context->context, 1288 ENCTYPE_ARCFOUR_HMAC_MD5, 1289 password, 1290 principal, 1291 &(*keys)[0]); 1292 memset(password, 0, sizeof(password)); 1293 if (ret) { 1294 free(*keys); 1295 *keys = NULL; 1296 *n_keys = 0; 1297 goto out; 1298 } 1299 } 1300 memset(password, 0, plen); 1301 free(password); 1302 out: 1303 return ret; 1304#else 1305 *keys = NULL; 1306 *n_keys = 0; 1307 1308 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 1309 return KADM5_RPC_ERROR; 1310#endif 1311} 1312 1313static kadm5_ret_t 1314kadm5_ad_rename_principal(void *server_handle, 1315 krb5_principal from, 1316 krb5_principal to) 1317{ 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; 1321} 1322 1323static kadm5_ret_t 1324kadm5_ad_chpass_principal_with_key(void *server_handle, 1325 krb5_principal princ, 1326 int n_key_data, 1327 krb5_key_data *key_data) 1328{ 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; 1332} 1333 1334static void 1335set_funcs(kadm5_ad_context *c) 1336{ 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); 1350} 1351 1352kadm5_ret_t 1353kadm5_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) 1361{ 1362 kadm5_ret_t ret; 1363 kadm5_ad_context *ctx; 1364 1365 ctx = malloc(sizeof(*ctx)); 1366 if(ctx == NULL) 1367 return ENOMEM; 1368 memset(ctx, 0, sizeof(*ctx)); 1369 set_funcs(ctx); 1370 1371 ctx->context = context; 1372 krb5_add_et_list (context, initialize_kadm5_error_table_r); 1373 1374 ret = krb5_parse_name(ctx->context, client_name, &ctx->caller); 1375 if(ret) { 1376 free(ctx); 1377 return ret; 1378 } 1379 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; 1390 } 1391 1392 ctx->client_name = strdup(client_name); 1393 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; 1401 } 1402 1403#ifdef OPENLDAP 1404 ret = _kadm5_ad_connect(ctx); 1405 if (ret) { 1406 kadm5_ad_destroy(ctx); 1407 return ret; 1408 } 1409#endif 1410 1411 *server_handle = ctx; 1412 return 0; 1413} 1414 1415kadm5_ret_t 1416kadm5_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) 1423{ 1424 krb5_context context; 1425 kadm5_ret_t ret; 1426 kadm5_ad_context *ctx; 1427 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; 1442 } 1443 ctx = *server_handle; 1444 ctx->my_context = 1; 1445 return 0; 1446} 1447