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$"); 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_message(context->context, KADM5_NO_SRV, "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_message(context->context, KADM5_NO_SRV, "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 != rk_ns_t_srv) 294 continue; 295 s = realloc(servers, sizeof(*servers) * (num_servers + 1)); 296 if (s == NULL) { 297 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "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_message(context->context, KADM5_NO_SRV, "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_message(context->context, 0, 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_message(context->context, KADM5_RPC_ERROR, 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_message(context->context, KADM5_RPC_ERROR, 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_message(context->context, KADM5_RPC_ERROR, "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_message(context->context, KADM5_RPC_ERROR, "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 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 868 return KADM5_RPC_ERROR; 869 } 870 871 static kadm5_ret_t 872 kadm5_ad_get_principal(void *server_handle, 873 krb5_principal principal, 874 kadm5_principal_ent_t entry, 875 uint32_t mask) 876 { 877 kadm5_ad_context *context = server_handle; 878 #ifdef OPENLDAP 879 LDAPMessage *m, *m0; 880 char **attr = NULL; 881 int attrlen = 0; 882 char *filter, *p, *q, *u; 883 int ret; 884 885 /* 886 * principal 887 * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES 888 */ 889 890 /* 891 * return 0 || KADM5_DUP; 892 */ 893 894 memset(entry, 0, sizeof(*entry)); 895 896 if (mask & KADM5_KVNO) 897 laddattr(&attr, &attrlen, "msDS-KeyVersionNumber"); 898 899 if (mask & KADM5_PRINCIPAL) { 900 laddattr(&attr, &attrlen, "userPrincipalName"); 901 laddattr(&attr, &attrlen, "servicePrincipalName"); 902 } 903 laddattr(&attr, &attrlen, "objectClass"); 904 laddattr(&attr, &attrlen, "lastLogon"); 905 laddattr(&attr, &attrlen, "badPwdCount"); 906 laddattr(&attr, &attrlen, "badPasswordTime"); 907 laddattr(&attr, &attrlen, "pwdLastSet"); 908 laddattr(&attr, &attrlen, "accountExpires"); 909 laddattr(&attr, &attrlen, "userAccountControl"); 910 911 krb5_unparse_name_short(context->context, principal, &p); 912 krb5_unparse_name(context->context, principal, &u); 913 914 /* replace @ in domain part with a / */ 915 q = strrchr(p, '@'); 916 if (q && (p != q && *(q - 1) != '\\')) 917 *q = '/'; 918 919 asprintf(&filter, 920 "(|(userPrincipalName=%s)(servicePrincipalName=%s)(servicePrincipalName=%s))", 921 u, p, u); 922 free(p); 923 free(u); 924 925 ret = ldap_search_s(CTX2LP(context), CTX2BASE(context), 926 LDAP_SCOPE_SUBTREE, 927 filter, attr, 0, &m); 928 free(attr); 929 if (check_ldap(context, ret)) 930 return KADM5_RPC_ERROR; 931 932 if (ldap_count_entries(CTX2LP(context), m) > 0) { 933 char **vals; 934 m0 = ldap_first_entry(CTX2LP(context), m); 935 if (m0 == NULL) { 936 ldap_msgfree(m); 937 goto fail; 938 } 939 #if 0 940 vals = ldap_get_values(CTX2LP(context), m0, "servicePrincipalName"); 941 if (vals) 942 printf("servicePrincipalName %s\n", vals[0]); 943 vals = ldap_get_values(CTX2LP(context), m0, "userPrincipalName"); 944 if (vals) 945 printf("userPrincipalName %s\n", vals[0]); 946 vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl"); 947 if (vals) 948 printf("userAccountControl %s\n", vals[0]); 949 #endif 950 entry->princ_expire_time = 0; 951 if (mask & KADM5_PRINC_EXPIRE_TIME) { 952 vals = ldap_get_values(CTX2LP(context), m0, "accountExpires"); 953 if (vals) 954 entry->princ_expire_time = nt2unixtime(vals[0]); 955 } 956 entry->last_success = 0; 957 if (mask & KADM5_LAST_SUCCESS) { 958 vals = ldap_get_values(CTX2LP(context), m0, "lastLogon"); 959 if (vals) 960 entry->last_success = nt2unixtime(vals[0]); 961 } 962 if (mask & KADM5_LAST_FAILED) { 963 vals = ldap_get_values(CTX2LP(context), m0, "badPasswordTime"); 964 if (vals) 965 entry->last_failed = nt2unixtime(vals[0]); 966 } 967 if (mask & KADM5_LAST_PWD_CHANGE) { 968 vals = ldap_get_values(CTX2LP(context), m0, "pwdLastSet"); 969 if (vals) 970 entry->last_pwd_change = nt2unixtime(vals[0]); 971 } 972 if (mask & KADM5_FAIL_AUTH_COUNT) { 973 vals = ldap_get_values(CTX2LP(context), m0, "badPwdCount"); 974 if (vals) 975 entry->fail_auth_count = atoi(vals[0]); 976 } 977 if (mask & KADM5_ATTRIBUTES) { 978 vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl"); 979 if (vals) { 980 uint32_t i; 981 i = atoi(vals[0]); 982 if (i & (UF_ACCOUNTDISABLE|UF_LOCKOUT)) 983 entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX; 984 if ((i & UF_DONT_REQUIRE_PREAUTH) == 0) 985 entry->attributes |= KRB5_KDB_REQUIRES_PRE_AUTH; 986 if (i & UF_SMARTCARD_REQUIRED) 987 entry->attributes |= KRB5_KDB_REQUIRES_HW_AUTH; 988 if ((i & UF_WORKSTATION_TRUST_ACCOUNT) == 0) 989 entry->attributes |= KRB5_KDB_DISALLOW_SVR; 990 } 991 } 992 if (mask & KADM5_KVNO) { 993 vals = ldap_get_values(CTX2LP(context), m0, 994 "msDS-KeyVersionNumber"); 995 if (vals) 996 entry->kvno = atoi(vals[0]); 997 else 998 entry->kvno = 0; 999 } 1000 ldap_msgfree(m); 1001 } else { 1002 return KADM5_UNK_PRINC; 1003 } 1004 1005 if (mask & KADM5_PRINCIPAL) 1006 krb5_copy_principal(context->context, principal, &entry->principal); 1007 1008 return 0; 1009 fail: 1010 return KADM5_RPC_ERROR; 1011 #else 1012 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 1013 return KADM5_RPC_ERROR; 1014 #endif 1015 } 1016 1017 static kadm5_ret_t 1018 kadm5_ad_get_principals(void *server_handle, 1019 const char *expression, 1020 char ***principals, 1021 int *count) 1022 { 1023 kadm5_ad_context *context = server_handle; 1024 1025 /* 1026 * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES 1027 */ 1028 1029 #ifdef OPENLDAP 1030 kadm5_ret_t ret; 1031 1032 ret = ad_get_cred(context, NULL); 1033 if (ret) 1034 return ret; 1035 1036 ret = _kadm5_ad_connect(server_handle); 1037 if (ret) 1038 return ret; 1039 1040 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 1041 return KADM5_RPC_ERROR; 1042 #else 1043 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 1044 return KADM5_RPC_ERROR; 1045 #endif 1046 } 1047 1048 static kadm5_ret_t 1049 kadm5_ad_get_privs(void *server_handle, uint32_t*privs) 1050 { 1051 kadm5_ad_context *context = server_handle; 1052 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 1053 return KADM5_RPC_ERROR; 1054 } 1055 1056 static kadm5_ret_t 1057 kadm5_ad_modify_principal(void *server_handle, 1058 kadm5_principal_ent_t entry, 1059 uint32_t mask) 1060 { 1061 kadm5_ad_context *context = server_handle; 1062 1063 /* 1064 * KADM5_ATTRIBUTES 1065 * KRB5_KDB_DISALLOW_ALL_TIX (| KADM5_KVNO) 1066 */ 1067 1068 #ifdef OPENLDAP 1069 LDAPMessage *m = NULL, *m0; 1070 kadm5_ret_t ret; 1071 char **attr = NULL; 1072 int attrlen = 0; 1073 char *p = NULL, *s = NULL, *q; 1074 char **vals; 1075 LDAPMod *attrs[4], rattrs[3], *a; 1076 char *uaf[2] = { NULL, NULL }; 1077 char *kvno[2] = { NULL, NULL }; 1078 char *tv[2] = { NULL, NULL }; 1079 char *filter, *dn; 1080 int i; 1081 1082 for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++) 1083 attrs[i] = &rattrs[i]; 1084 attrs[i] = NULL; 1085 a = &rattrs[0]; 1086 1087 ret = _kadm5_ad_connect(server_handle); 1088 if (ret) 1089 return ret; 1090 1091 if (mask & KADM5_KVNO) 1092 laddattr(&attr, &attrlen, "msDS-KeyVersionNumber"); 1093 if (mask & KADM5_PRINC_EXPIRE_TIME) 1094 laddattr(&attr, &attrlen, "accountExpires"); 1095 if (mask & KADM5_ATTRIBUTES) 1096 laddattr(&attr, &attrlen, "userAccountControl"); 1097 laddattr(&attr, &attrlen, "distinguishedName"); 1098 1099 krb5_unparse_name(context->context, entry->principal, &p); 1100 1101 s = strdup(p); 1102 1103 q = strrchr(s, '@'); 1104 if (q && (p != q && *(q - 1) != '\\')) 1105 *q = '\0'; 1106 1107 asprintf(&filter, 1108 "(|(userPrincipalName=%s)(servicePrincipalName=%s))", 1109 s, s); 1110 free(p); 1111 free(s); 1112 1113 ret = ldap_search_s(CTX2LP(context), CTX2BASE(context), 1114 LDAP_SCOPE_SUBTREE, 1115 filter, attr, 0, &m); 1116 free(attr); 1117 free(filter); 1118 if (check_ldap(context, ret)) 1119 return KADM5_RPC_ERROR; 1120 1121 if (ldap_count_entries(CTX2LP(context), m) <= 0) { 1122 ret = KADM5_RPC_ERROR; 1123 goto out; 1124 } 1125 1126 m0 = ldap_first_entry(CTX2LP(context), m); 1127 1128 if (mask & KADM5_ATTRIBUTES) { 1129 int32_t i; 1130 1131 vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl"); 1132 if (vals == NULL) { 1133 ret = KADM5_RPC_ERROR; 1134 goto out; 1135 } 1136 1137 i = atoi(vals[0]); 1138 if (i == 0) 1139 return KADM5_RPC_ERROR; 1140 1141 if (entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX) 1142 i |= (UF_ACCOUNTDISABLE|UF_LOCKOUT); 1143 else 1144 i &= ~(UF_ACCOUNTDISABLE|UF_LOCKOUT); 1145 if (entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) 1146 i &= ~UF_DONT_REQUIRE_PREAUTH; 1147 else 1148 i |= UF_DONT_REQUIRE_PREAUTH; 1149 if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH) 1150 i |= UF_SMARTCARD_REQUIRED; 1151 else 1152 i &= UF_SMARTCARD_REQUIRED; 1153 if (entry->attributes & KRB5_KDB_DISALLOW_SVR) 1154 i &= ~UF_WORKSTATION_TRUST_ACCOUNT; 1155 else 1156 i |= UF_WORKSTATION_TRUST_ACCOUNT; 1157 1158 asprintf(&uaf[0], "%d", i); 1159 1160 a->mod_op = LDAP_MOD_REPLACE; 1161 a->mod_type = "userAccountControl"; 1162 a->mod_values = uaf; 1163 a++; 1164 } 1165 1166 if (mask & KADM5_KVNO) { 1167 vals = ldap_get_values(CTX2LP(context), m0, "msDS-KeyVersionNumber"); 1168 if (vals == NULL) { 1169 entry->kvno = 0; 1170 } else { 1171 asprintf(&kvno[0], "%d", entry->kvno); 1172 1173 a->mod_op = LDAP_MOD_REPLACE; 1174 a->mod_type = "msDS-KeyVersionNumber"; 1175 a->mod_values = kvno; 1176 a++; 1177 } 1178 } 1179 1180 if (mask & KADM5_PRINC_EXPIRE_TIME) { 1181 long long wt; 1182 vals = ldap_get_values(CTX2LP(context), m0, "accountExpires"); 1183 if (vals == NULL) { 1184 ret = KADM5_RPC_ERROR; 1185 goto out; 1186 } 1187 1188 wt = unix2nttime(entry->princ_expire_time); 1189 1190 asprintf(&tv[0], "%llu", wt); 1191 1192 a->mod_op = LDAP_MOD_REPLACE; 1193 a->mod_type = "accountExpires"; 1194 a->mod_values = tv; 1195 a++; 1196 } 1197 1198 vals = ldap_get_values(CTX2LP(context), m0, "distinguishedName"); 1199 if (vals == NULL) { 1200 ret = KADM5_RPC_ERROR; 1201 goto out; 1202 } 1203 dn = vals[0]; 1204 1205 attrs[a - &rattrs[0]] = NULL; 1206 1207 ret = ldap_modify_s(CTX2LP(context), dn, attrs); 1208 if (check_ldap(context, ret)) 1209 return KADM5_RPC_ERROR; 1210 1211 out: 1212 if (m) 1213 ldap_msgfree(m); 1214 if (uaf[0]) 1215 free(uaf[0]); 1216 if (kvno[0]) 1217 free(kvno[0]); 1218 if (tv[0]) 1219 free(tv[0]); 1220 return ret; 1221 #else 1222 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 1223 return KADM5_RPC_ERROR; 1224 #endif 1225 } 1226 1227 static kadm5_ret_t 1228 kadm5_ad_randkey_principal(void *server_handle, 1229 krb5_principal principal, 1230 krb5_keyblock **keys, 1231 int *n_keys) 1232 { 1233 kadm5_ad_context *context = server_handle; 1234 1235 /* 1236 * random key 1237 */ 1238 1239 #ifdef OPENLDAP 1240 krb5_data result_code_string, result_string; 1241 int result_code, plen; 1242 kadm5_ret_t ret; 1243 char *password; 1244 1245 *keys = NULL; 1246 *n_keys = 0; 1247 1248 { 1249 char p[64]; 1250 krb5_generate_random_block(p, sizeof(p)); 1251 plen = base64_encode(p, sizeof(p), &password); 1252 if (plen < 0) 1253 return ENOMEM; 1254 } 1255 1256 ret = ad_get_cred(context, NULL); 1257 if (ret) { 1258 free(password); 1259 return ret; 1260 } 1261 1262 krb5_data_zero (&result_code_string); 1263 krb5_data_zero (&result_string); 1264 1265 ret = krb5_set_password_using_ccache (context->context, 1266 context->ccache, 1267 password, 1268 principal, 1269 &result_code, 1270 &result_code_string, 1271 &result_string); 1272 1273 krb5_data_free (&result_code_string); 1274 krb5_data_free (&result_string); 1275 1276 if (ret == 0) { 1277 1278 *keys = malloc(sizeof(**keys) * 1); 1279 if (*keys == NULL) { 1280 ret = ENOMEM; 1281 goto out; 1282 } 1283 *n_keys = 1; 1284 1285 ret = krb5_string_to_key(context->context, 1286 ENCTYPE_ARCFOUR_HMAC_MD5, 1287 password, 1288 principal, 1289 &(*keys)[0]); 1290 memset(password, 0, sizeof(password)); 1291 if (ret) { 1292 free(*keys); 1293 *keys = NULL; 1294 *n_keys = 0; 1295 goto out; 1296 } 1297 } 1298 memset(password, 0, plen); 1299 free(password); 1300 out: 1301 return ret; 1302 #else 1303 *keys = NULL; 1304 *n_keys = 0; 1305 1306 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 1307 return KADM5_RPC_ERROR; 1308 #endif 1309 } 1310 1311 static kadm5_ret_t 1312 kadm5_ad_rename_principal(void *server_handle, 1313 krb5_principal from, 1314 krb5_principal to) 1315 { 1316 kadm5_ad_context *context = server_handle; 1317 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 1318 return KADM5_RPC_ERROR; 1319 } 1320 1321 static kadm5_ret_t 1322 kadm5_ad_chpass_principal_with_key(void *server_handle, 1323 krb5_principal princ, 1324 int n_key_data, 1325 krb5_key_data *key_data) 1326 { 1327 kadm5_ad_context *context = server_handle; 1328 krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented"); 1329 return KADM5_RPC_ERROR; 1330 } 1331 1332 static void 1333 set_funcs(kadm5_ad_context *c) 1334 { 1335 #define SET(C, F) (C)->funcs.F = kadm5_ad_ ## F 1336 SET(c, chpass_principal); 1337 SET(c, chpass_principal_with_key); 1338 SET(c, create_principal); 1339 SET(c, delete_principal); 1340 SET(c, destroy); 1341 SET(c, flush); 1342 SET(c, get_principal); 1343 SET(c, get_principals); 1344 SET(c, get_privs); 1345 SET(c, modify_principal); 1346 SET(c, randkey_principal); 1347 SET(c, rename_principal); 1348 } 1349 1350 kadm5_ret_t 1351 kadm5_ad_init_with_password_ctx(krb5_context context, 1352 const char *client_name, 1353 const char *password, 1354 const char *service_name, 1355 kadm5_config_params *realm_params, 1356 unsigned long struct_version, 1357 unsigned long api_version, 1358 void **server_handle) 1359 { 1360 kadm5_ret_t ret; 1361 kadm5_ad_context *ctx; 1362 1363 ctx = malloc(sizeof(*ctx)); 1364 if(ctx == NULL) 1365 return ENOMEM; 1366 memset(ctx, 0, sizeof(*ctx)); 1367 set_funcs(ctx); 1368 1369 ctx->context = context; 1370 krb5_add_et_list (context, initialize_kadm5_error_table_r); 1371 1372 ret = krb5_parse_name(ctx->context, client_name, &ctx->caller); 1373 if(ret) { 1374 free(ctx); 1375 return ret; 1376 } 1377 1378 if(realm_params->mask & KADM5_CONFIG_REALM) { 1379 ret = 0; 1380 ctx->realm = strdup(realm_params->realm); 1381 if (ctx->realm == NULL) 1382 ret = ENOMEM; 1383 } else 1384 ret = krb5_get_default_realm(ctx->context, &ctx->realm); 1385 if (ret) { 1386 free(ctx); 1387 return ret; 1388 } 1389 1390 ctx->client_name = strdup(client_name); 1391 1392 if(password != NULL && *password != '\0') 1393 ret = ad_get_cred(ctx, password); 1394 else 1395 ret = ad_get_cred(ctx, NULL); 1396 if(ret) { 1397 kadm5_ad_destroy(ctx); 1398 return ret; 1399 } 1400 1401 #ifdef OPENLDAP 1402 ret = _kadm5_ad_connect(ctx); 1403 if (ret) { 1404 kadm5_ad_destroy(ctx); 1405 return ret; 1406 } 1407 #endif 1408 1409 *server_handle = ctx; 1410 return 0; 1411 } 1412 1413 kadm5_ret_t 1414 kadm5_ad_init_with_password(const char *client_name, 1415 const char *password, 1416 const char *service_name, 1417 kadm5_config_params *realm_params, 1418 unsigned long struct_version, 1419 unsigned long api_version, 1420 void **server_handle) 1421 { 1422 krb5_context context; 1423 kadm5_ret_t ret; 1424 kadm5_ad_context *ctx; 1425 1426 ret = krb5_init_context(&context); 1427 if (ret) 1428 return ret; 1429 ret = kadm5_ad_init_with_password_ctx(context, 1430 client_name, 1431 password, 1432 service_name, 1433 realm_params, 1434 struct_version, 1435 api_version, 1436 server_handle); 1437 if(ret) { 1438 krb5_free_context(context); 1439 return ret; 1440 } 1441 ctx = *server_handle; 1442 ctx->my_context = 1; 1443 return 0; 1444 } 1445