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