1/* 2 Unix SMB/CIFS implementation. 3 kerberos utility library 4 Copyright (C) Andrew Tridgell 2001 5 Copyright (C) Remus Koos 2001 6 Copyright (C) Nalin Dahyabhai <nalin@redhat.com> 2004. 7 Copyright (C) Jeremy Allison 2004. 8 Copyright (C) Gerald Carter 2006. 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 3 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program. If not, see <http://www.gnu.org/licenses/>. 22*/ 23 24#include "includes.h" 25#include "smb_krb5.h" 26 27#ifdef HAVE_KRB5 28 29#define DEFAULT_KRB5_PORT 88 30 31#define LIBADS_CCACHE_NAME "MEMORY:libads" 32 33/* 34 we use a prompter to avoid a crash bug in the kerberos libs when 35 dealing with empty passwords 36 this prompter is just a string copy ... 37*/ 38static krb5_error_code 39kerb_prompter(krb5_context ctx, void *data, 40 const char *name, 41 const char *banner, 42 int num_prompts, 43 krb5_prompt prompts[]) 44{ 45 if (num_prompts == 0) return 0; 46 47 memset(prompts[0].reply->data, '\0', prompts[0].reply->length); 48 if (prompts[0].reply->length > 0) { 49 if (data) { 50 strncpy((char *)prompts[0].reply->data, (const char *)data, 51 prompts[0].reply->length-1); 52 prompts[0].reply->length = strlen((const char *)prompts[0].reply->data); 53 } else { 54 prompts[0].reply->length = 0; 55 } 56 } 57 return 0; 58} 59 60 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error, 61 NTSTATUS *nt_status) 62{ 63 DATA_BLOB edata; 64 DATA_BLOB unwrapped_edata; 65 TALLOC_CTX *mem_ctx; 66 struct KRB5_EDATA_NTSTATUS parsed_edata; 67 enum ndr_err_code ndr_err; 68 69#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR 70 edata = data_blob(error->e_data->data, error->e_data->length); 71#else 72 edata = data_blob(error->e_data.data, error->e_data.length); 73#endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */ 74 75#ifdef DEVELOPER 76 dump_data(10, edata.data, edata.length); 77#endif /* DEVELOPER */ 78 79 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error"); 80 if (mem_ctx == NULL) { 81 data_blob_free(&edata); 82 return False; 83 } 84 85 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) { 86 data_blob_free(&edata); 87 TALLOC_FREE(mem_ctx); 88 return False; 89 } 90 91 data_blob_free(&edata); 92 93 ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx, NULL, 94 &parsed_edata, 95 (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS); 96 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 97 data_blob_free(&unwrapped_edata); 98 TALLOC_FREE(mem_ctx); 99 return False; 100 } 101 102 data_blob_free(&unwrapped_edata); 103 104 if (nt_status) { 105 *nt_status = parsed_edata.ntstatus; 106 } 107 108 TALLOC_FREE(mem_ctx); 109 110 return True; 111} 112 113 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx, 114 krb5_get_init_creds_opt *opt, 115 NTSTATUS *nt_status) 116{ 117 bool ret = False; 118 krb5_error *error = NULL; 119 120#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR 121 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error); 122 if (ret) { 123 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n", 124 error_message(ret))); 125 return False; 126 } 127#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */ 128 129 if (!error) { 130 DEBUG(1,("no krb5_error\n")); 131 return False; 132 } 133 134#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR 135 if (!error->e_data) { 136#else 137 if (error->e_data.data == NULL) { 138#endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */ 139 DEBUG(1,("no edata in krb5_error\n")); 140 krb5_free_error(ctx, error); 141 return False; 142 } 143 144 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status); 145 146 krb5_free_error(ctx, error); 147 148 return ret; 149} 150 151/* 152 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL 153 place in default cache location. 154 remus@snapserver.com 155*/ 156int kerberos_kinit_password_ext(const char *principal, 157 const char *password, 158 int time_offset, 159 time_t *expire_time, 160 time_t *renew_till_time, 161 const char *cache_name, 162 bool request_pac, 163 bool add_netbios_addr, 164 time_t renewable_time, 165 NTSTATUS *ntstatus) 166{ 167 krb5_context ctx = NULL; 168 krb5_error_code code = 0; 169 krb5_ccache cc = NULL; 170 krb5_principal me = NULL; 171 krb5_creds my_creds; 172 krb5_get_init_creds_opt *opt = NULL; 173 smb_krb5_addresses *addr = NULL; 174 175 ZERO_STRUCT(my_creds); 176 177 initialize_krb5_error_table(); 178 if ((code = krb5_init_context(&ctx))) 179 goto out; 180 181 if (time_offset != 0) { 182 krb5_set_real_time(ctx, time(NULL) + time_offset, 0); 183 } 184 185 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n", 186 principal, 187 cache_name ? cache_name: krb5_cc_default_name(ctx), 188 getenv("KRB5_CONFIG"))); 189 190 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) { 191 goto out; 192 } 193 194 if ((code = smb_krb5_parse_name(ctx, principal, &me))) { 195 goto out; 196 } 197 198 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) { 199 goto out; 200 } 201 202 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time); 203 krb5_get_init_creds_opt_set_forwardable(opt, True); 204#if 0 205 /* insane testing */ 206 krb5_get_init_creds_opt_set_tkt_life(opt, 60); 207#endif 208 209#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST 210 if (request_pac) { 211 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) { 212 goto out; 213 } 214 } 215#endif 216 if (add_netbios_addr) { 217 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) { 218 goto out; 219 } 220 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs); 221 } 222 223 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password), 224 kerb_prompter, CONST_DISCARD(char *,password), 225 0, NULL, opt))) { 226 goto out; 227 } 228 229 if ((code = krb5_cc_initialize(ctx, cc, me))) { 230 goto out; 231 } 232 233 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) { 234 goto out; 235 } 236 237 if (expire_time) { 238 *expire_time = (time_t) my_creds.times.endtime; 239 } 240 241 if (renew_till_time) { 242 *renew_till_time = (time_t) my_creds.times.renew_till; 243 } 244 out: 245 if (ntstatus) { 246 247 NTSTATUS status; 248 249 /* fast path */ 250 if (code == 0) { 251 *ntstatus = NT_STATUS_OK; 252 goto cleanup; 253 } 254 255 /* try to get ntstatus code out of krb5_error when we have it 256 * inside the krb5_get_init_creds_opt - gd */ 257 258 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) { 259 *ntstatus = status; 260 goto cleanup; 261 } 262 263 /* fall back to self-made-mapping */ 264 *ntstatus = krb5_to_nt_status(code); 265 } 266 267 cleanup: 268 krb5_free_cred_contents(ctx, &my_creds); 269 if (me) { 270 krb5_free_principal(ctx, me); 271 } 272 if (addr) { 273 smb_krb5_free_addresses(ctx, addr); 274 } 275 if (opt) { 276 smb_krb5_get_init_creds_opt_free(ctx, opt); 277 } 278 if (cc) { 279 krb5_cc_close(ctx, cc); 280 } 281 if (ctx) { 282 krb5_free_context(ctx); 283 } 284 return code; 285} 286 287 288 289/* run kinit to setup our ccache */ 290int ads_kinit_password(ADS_STRUCT *ads) 291{ 292 char *s; 293 int ret; 294 const char *account_name; 295 fstring acct_name; 296 297 if (ads->auth.flags & ADS_AUTH_USER_CREDS) { 298 account_name = ads->auth.user_name; 299 goto got_accountname; 300 } 301 302 if ( IS_DC ) { 303 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */ 304 account_name = lp_workgroup(); 305 } else { 306 /* always use the sAMAccountName for security = domain */ 307 /* global_myname()$@REA.LM */ 308 if ( lp_security() == SEC_DOMAIN ) { 309 fstr_sprintf( acct_name, "%s$", global_myname() ); 310 account_name = acct_name; 311 } 312 else 313 /* This looks like host/global_myname()@REA.LM */ 314 account_name = ads->auth.user_name; 315 } 316 317 got_accountname: 318 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) { 319 return KRB5_CC_NOMEM; 320 } 321 322 if (!ads->auth.password) { 323 SAFE_FREE(s); 324 return KRB5_LIBOS_CANTREADPWD; 325 } 326 327 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset, 328 &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable, 329 NULL); 330 331 if (ret) { 332 DEBUG(0,("kerberos_kinit_password %s failed: %s\n", 333 s, error_message(ret))); 334 } 335 SAFE_FREE(s); 336 return ret; 337} 338 339int ads_kdestroy(const char *cc_name) 340{ 341 krb5_error_code code; 342 krb5_context ctx = NULL; 343 krb5_ccache cc = NULL; 344 345 initialize_krb5_error_table(); 346 if ((code = krb5_init_context (&ctx))) { 347 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", 348 error_message(code))); 349 return code; 350 } 351 352 if (!cc_name) { 353 if ((code = krb5_cc_default(ctx, &cc))) { 354 krb5_free_context(ctx); 355 return code; 356 } 357 } else { 358 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) { 359 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n", 360 error_message(code))); 361 krb5_free_context(ctx); 362 return code; 363 } 364 } 365 366 if ((code = krb5_cc_destroy (ctx, cc))) { 367 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", 368 error_message(code))); 369 } 370 371 krb5_free_context (ctx); 372 return code; 373} 374 375/************************************************************************ 376 Routine to fetch the salting principal for a service. Active 377 Directory may use a non-obvious principal name to generate the salt 378 when it determines the key to use for encrypting tickets for a service, 379 and hopefully we detected that when we joined the domain. 380 ************************************************************************/ 381 382static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype) 383{ 384 char *key = NULL; 385 char *ret = NULL; 386 387 if (asprintf(&key, "%s/%s/enctype=%d", 388 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) { 389 return NULL; 390 } 391 ret = (char *)secrets_fetch(key, NULL); 392 SAFE_FREE(key); 393 return ret; 394} 395 396/************************************************************************ 397 Return the standard DES salt key 398************************************************************************/ 399 400char* kerberos_standard_des_salt( void ) 401{ 402 fstring salt; 403 404 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() ); 405 strlower_m( salt ); 406 fstrcat( salt, lp_realm() ); 407 408 return SMB_STRDUP( salt ); 409} 410 411/************************************************************************ 412************************************************************************/ 413 414static char* des_salt_key( void ) 415{ 416 char *key; 417 418 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, 419 lp_realm()) == -1) { 420 return NULL; 421 } 422 423 return key; 424} 425 426/************************************************************************ 427************************************************************************/ 428 429bool kerberos_secrets_store_des_salt( const char* salt ) 430{ 431 char* key; 432 bool ret; 433 434 if ( (key = des_salt_key()) == NULL ) { 435 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n")); 436 return False; 437 } 438 439 if ( !salt ) { 440 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n")); 441 secrets_delete( key ); 442 return True; 443 } 444 445 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt)); 446 447 ret = secrets_store( key, salt, strlen(salt)+1 ); 448 449 SAFE_FREE( key ); 450 451 return ret; 452} 453 454/************************************************************************ 455************************************************************************/ 456 457char* kerberos_secrets_fetch_des_salt( void ) 458{ 459 char *salt, *key; 460 461 if ( (key = des_salt_key()) == NULL ) { 462 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n")); 463 return False; 464 } 465 466 salt = (char*)secrets_fetch( key, NULL ); 467 468 SAFE_FREE( key ); 469 470 return salt; 471} 472 473/************************************************************************ 474 Routine to get the default realm from the kerberos credentials cache. 475 Caller must free if the return value is not NULL. 476************************************************************************/ 477 478char *kerberos_get_default_realm_from_ccache( void ) 479{ 480 char *realm = NULL; 481 krb5_context ctx = NULL; 482 krb5_ccache cc = NULL; 483 krb5_principal princ = NULL; 484 485 initialize_krb5_error_table(); 486 if (krb5_init_context(&ctx)) { 487 return NULL; 488 } 489 490 DEBUG(5,("kerberos_get_default_realm_from_ccache: " 491 "Trying to read krb5 cache: %s\n", 492 krb5_cc_default_name(ctx))); 493 if (krb5_cc_default(ctx, &cc)) { 494 DEBUG(0,("kerberos_get_default_realm_from_ccache: " 495 "failed to read default cache\n")); 496 goto out; 497 } 498 if (krb5_cc_get_principal(ctx, cc, &princ)) { 499 DEBUG(0,("kerberos_get_default_realm_from_ccache: " 500 "failed to get default principal\n")); 501 goto out; 502 } 503 504#if defined(HAVE_KRB5_PRINCIPAL_GET_REALM) 505 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ)); 506#elif defined(HAVE_KRB5_PRINC_REALM) 507 { 508 krb5_data *realm_data = krb5_princ_realm(ctx, princ); 509 realm = SMB_STRNDUP(realm_data->data, realm_data->length); 510 } 511#endif 512 513 out: 514 515 if (ctx) { 516 if (princ) { 517 krb5_free_principal(ctx, princ); 518 } 519 if (cc) { 520 krb5_cc_close(ctx, cc); 521 } 522 krb5_free_context(ctx); 523 } 524 525 return realm; 526} 527 528/************************************************************************ 529 Routine to get the realm from a given DNS name. Returns malloc'ed memory. 530 Caller must free() if the return value is not NULL. 531************************************************************************/ 532 533char *kerberos_get_realm_from_hostname(const char *hostname) 534{ 535#if defined(HAVE_KRB5_GET_HOST_REALM) && defined(HAVE_KRB5_FREE_HOST_REALM) 536#if defined(HAVE_KRB5_REALM_TYPE) 537 /* Heimdal. */ 538 krb5_realm *realm_list = NULL; 539#else 540 /* MIT */ 541 char **realm_list = NULL; 542#endif 543 char *realm = NULL; 544 krb5_error_code kerr; 545 krb5_context ctx = NULL; 546 547 initialize_krb5_error_table(); 548 if (krb5_init_context(&ctx)) { 549 return NULL; 550 } 551 552 kerr = krb5_get_host_realm(ctx, hostname, &realm_list); 553 if (kerr != 0) { 554 DEBUG(3,("kerberos_get_realm_from_hostname %s: " 555 "failed %s\n", 556 hostname ? hostname : "(NULL)", 557 error_message(kerr) )); 558 goto out; 559 } 560 561 if (realm_list && realm_list[0]) { 562 realm = SMB_STRDUP(realm_list[0]); 563 } 564 565 out: 566 567 if (ctx) { 568 if (realm_list) { 569 krb5_free_host_realm(ctx, realm_list); 570 realm_list = NULL; 571 } 572 krb5_free_context(ctx); 573 ctx = NULL; 574 } 575 return realm; 576#else 577 return NULL; 578#endif 579} 580 581/************************************************************************ 582 Routine to get the salting principal for this service. This is 583 maintained for backwards compatibilty with releases prior to 3.0.24. 584 Since we store the salting principal string only at join, we may have 585 to look for the older tdb keys. Caller must free if return is not null. 586 ************************************************************************/ 587 588krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, 589 krb5_principal host_princ, 590 int enctype) 591{ 592 char *unparsed_name = NULL, *salt_princ_s = NULL; 593 krb5_principal ret_princ = NULL; 594 595 /* lookup new key first */ 596 597 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) { 598 599 /* look under the old key. If this fails, just use the standard key */ 600 601 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) { 602 return (krb5_principal)NULL; 603 } 604 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) { 605 /* fall back to host/machine.realm@REALM */ 606 salt_princ_s = kerberos_standard_des_salt(); 607 } 608 } 609 610 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) { 611 ret_princ = NULL; 612 } 613 614 TALLOC_FREE(unparsed_name); 615 SAFE_FREE(salt_princ_s); 616 617 return ret_princ; 618} 619 620/************************************************************************ 621 Routine to set the salting principal for this service. Active 622 Directory may use a non-obvious principal name to generate the salt 623 when it determines the key to use for encrypting tickets for a service, 624 and hopefully we detected that when we joined the domain. 625 Setting principal to NULL deletes this entry. 626 ************************************************************************/ 627 628bool kerberos_secrets_store_salting_principal(const char *service, 629 int enctype, 630 const char *principal) 631{ 632 char *key = NULL; 633 bool ret = False; 634 krb5_context context = NULL; 635 krb5_principal princ = NULL; 636 char *princ_s = NULL; 637 char *unparsed_name = NULL; 638 krb5_error_code code; 639 640 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) { 641 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n", 642 error_message(code))); 643 return False; 644 } 645 if (strchr_m(service, '@')) { 646 if (asprintf(&princ_s, "%s", service) == -1) { 647 goto out; 648 } 649 } else { 650 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) { 651 goto out; 652 } 653 } 654 655 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) { 656 goto out; 657 658 } 659 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) { 660 goto out; 661 } 662 663 if (asprintf(&key, "%s/%s/enctype=%d", 664 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype) 665 == -1) { 666 goto out; 667 } 668 669 if ((principal != NULL) && (strlen(principal) > 0)) { 670 ret = secrets_store(key, principal, strlen(principal) + 1); 671 } else { 672 ret = secrets_delete(key); 673 } 674 675 out: 676 677 SAFE_FREE(key); 678 SAFE_FREE(princ_s); 679 TALLOC_FREE(unparsed_name); 680 681 if (princ) { 682 krb5_free_principal(context, princ); 683 } 684 685 if (context) { 686 krb5_free_context(context); 687 } 688 689 return ret; 690} 691 692 693/************************************************************************ 694************************************************************************/ 695 696int kerberos_kinit_password(const char *principal, 697 const char *password, 698 int time_offset, 699 const char *cache_name) 700{ 701 return kerberos_kinit_password_ext(principal, 702 password, 703 time_offset, 704 0, 705 0, 706 cache_name, 707 False, 708 False, 709 0, 710 NULL); 711} 712 713/************************************************************************ 714************************************************************************/ 715 716static char *print_kdc_line(char *mem_ctx, 717 const char *prev_line, 718 const struct sockaddr_storage *pss, 719 const char *kdc_name) 720{ 721 char *kdc_str = NULL; 722 723 if (pss->ss_family == AF_INET) { 724 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n", 725 prev_line, 726 print_canonical_sockaddr(mem_ctx, pss)); 727 } else { 728 char addr[INET6_ADDRSTRLEN]; 729 uint16_t port = get_sockaddr_port(pss); 730 731 DEBUG(10,("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n", 732 kdc_name, port)); 733 734 if (port != 0 && port != DEFAULT_KRB5_PORT) { 735 /* Currently for IPv6 we can't specify a non-default 736 krb5 port with an address, as this requires a ':'. 737 Resolve to a name. */ 738 char hostname[MAX_DNS_NAME_LENGTH]; 739 int ret = sys_getnameinfo((const struct sockaddr *)pss, 740 sizeof(*pss), 741 hostname, sizeof(hostname), 742 NULL, 0, 743 NI_NAMEREQD); 744 if (ret) { 745 DEBUG(0,("print_kdc_line: can't resolve name " 746 "for kdc with non-default port %s. " 747 "Error %s\n.", 748 print_canonical_sockaddr(mem_ctx, pss), 749 gai_strerror(ret))); 750 return NULL; 751 } 752 /* Success, use host:port */ 753 kdc_str = talloc_asprintf(mem_ctx, 754 "%s\tkdc = %s:%u\n", 755 prev_line, 756 hostname, 757 (unsigned int)port); 758 } else { 759 760 /* no krb5 lib currently supports "kdc = ipv6 address" 761 * at all, so just fill in just the kdc_name if we have 762 * it and let the krb5 lib figure out the appropriate 763 * ipv6 address - gd */ 764 765 if (kdc_name) { 766 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n", 767 prev_line, kdc_name); 768 } else { 769 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n", 770 prev_line, 771 print_sockaddr(addr, 772 sizeof(addr), 773 pss)); 774 } 775 } 776 } 777 return kdc_str; 778} 779 780/************************************************************************ 781 Create a string list of available kdc's, possibly searching by sitename. 782 Does DNS queries. 783 784 If "sitename" is given, the DC's in that site are listed first. 785 786************************************************************************/ 787 788static char *get_kdc_ip_string(char *mem_ctx, 789 const char *realm, 790 const char *sitename, 791 struct sockaddr_storage *pss, 792 const char *kdc_name) 793{ 794 int i; 795 struct ip_service *ip_srv_site = NULL; 796 struct ip_service *ip_srv_nonsite = NULL; 797 int count_site = 0; 798 int count_nonsite; 799 char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name); 800 801 if (kdc_str == NULL) { 802 return NULL; 803 } 804 805 /* 806 * First get the KDC's only in this site, the rest will be 807 * appended later 808 */ 809 810 if (sitename) { 811 812 get_kdc_list(realm, sitename, &ip_srv_site, &count_site); 813 814 for (i = 0; i < count_site; i++) { 815 if (sockaddr_equal((struct sockaddr *)&ip_srv_site[i].ss, 816 (struct sockaddr *)pss)) { 817 continue; 818 } 819 /* Append to the string - inefficient 820 * but not done often. */ 821 kdc_str = print_kdc_line(mem_ctx, 822 kdc_str, 823 &ip_srv_site[i].ss, 824 NULL); 825 if (!kdc_str) { 826 SAFE_FREE(ip_srv_site); 827 return NULL; 828 } 829 } 830 } 831 832 /* Get all KDC's. */ 833 834 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite); 835 836 for (i = 0; i < count_nonsite; i++) { 837 int j; 838 839 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, (struct sockaddr *)pss)) { 840 continue; 841 } 842 843 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */ 844 for (j = 0; j < count_site; j++) { 845 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, 846 (struct sockaddr *)&ip_srv_site[j].ss)) { 847 break; 848 } 849 /* As the lists are sorted we can break early if nonsite > site. */ 850 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) { 851 break; 852 } 853 } 854 if (j != i) { 855 continue; 856 } 857 858 /* Append to the string - inefficient but not done often. */ 859 kdc_str = print_kdc_line(mem_ctx, 860 kdc_str, 861 &ip_srv_nonsite[i].ss, 862 NULL); 863 if (!kdc_str) { 864 SAFE_FREE(ip_srv_site); 865 SAFE_FREE(ip_srv_nonsite); 866 return NULL; 867 } 868 } 869 870 871 SAFE_FREE(ip_srv_site); 872 SAFE_FREE(ip_srv_nonsite); 873 874 DEBUG(10,("get_kdc_ip_string: Returning %s\n", 875 kdc_str )); 876 877 return kdc_str; 878} 879 880/************************************************************************ 881 Create a specific krb5.conf file in the private directory pointing 882 at a specific kdc for a realm. Keyed off domain name. Sets 883 KRB5_CONFIG environment variable to point to this file. Must be 884 run as root or will fail (which is a good thing :-). 885************************************************************************/ 886 887bool create_local_private_krb5_conf_for_domain(const char *realm, 888 const char *domain, 889 const char *sitename, 890 struct sockaddr_storage *pss, 891 const char *kdc_name) 892{ 893 char *dname; 894 char *tmpname = NULL; 895 char *fname = NULL; 896 char *file_contents = NULL; 897 char *kdc_ip_string = NULL; 898 size_t flen = 0; 899 ssize_t ret; 900 int fd; 901 char *realm_upper = NULL; 902 bool result = false; 903 904 if (!lp_create_krb5_conf()) { 905 return false; 906 } 907 908 dname = lock_path("smb_krb5"); 909 if (!dname) { 910 return false; 911 } 912 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) { 913 DEBUG(0,("create_local_private_krb5_conf_for_domain: " 914 "failed to create directory %s. Error was %s\n", 915 dname, strerror(errno) )); 916 goto done; 917 } 918 919 tmpname = lock_path("smb_tmp_krb5.XXXXXX"); 920 if (!tmpname) { 921 goto done; 922 } 923 924 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain); 925 if (!fname) { 926 goto done; 927 } 928 929 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n", 930 fname, realm, domain )); 931 932 realm_upper = talloc_strdup(fname, realm); 933 strupper_m(realm_upper); 934 935 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name); 936 if (!kdc_ip_string) { 937 goto done; 938 } 939 940 file_contents = talloc_asprintf(fname, 941 "[libdefaults]\n\tdefault_realm = %s\n" 942 "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n" 943 "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n" 944 "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n" 945 "[realms]\n\t%s = {\n" 946 "\t%s\t}\n", 947 realm_upper, realm_upper, kdc_ip_string); 948 949 if (!file_contents) { 950 goto done; 951 } 952 953 flen = strlen(file_contents); 954 955 fd = mkstemp(tmpname); 956 if (fd == -1) { 957 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed," 958 " for file %s. Errno %s\n", 959 tmpname, strerror(errno) )); 960 goto done; 961 } 962 963 if (fchmod(fd, 0644)==-1) { 964 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s." 965 " Errno %s\n", 966 tmpname, strerror(errno) )); 967 unlink(tmpname); 968 close(fd); 969 goto done; 970 } 971 972 ret = write(fd, file_contents, flen); 973 if (flen != ret) { 974 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed," 975 " returned %d (should be %u). Errno %s\n", 976 (int)ret, (unsigned int)flen, strerror(errno) )); 977 unlink(tmpname); 978 close(fd); 979 goto done; 980 } 981 if (close(fd)==-1) { 982 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed." 983 " Errno %s\n", strerror(errno) )); 984 unlink(tmpname); 985 goto done; 986 } 987 988 if (rename(tmpname, fname) == -1) { 989 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename " 990 "of %s to %s failed. Errno %s\n", 991 tmpname, fname, strerror(errno) )); 992 unlink(tmpname); 993 goto done; 994 } 995 996 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote " 997 "file %s with realm %s KDC list = %s\n", 998 fname, realm_upper, kdc_ip_string)); 999 1000 /* Set the environment variable to this file. */ 1001 setenv("KRB5_CONFIG", fname, 1); 1002 1003 result = true; 1004 1005#if defined(OVERWRITE_SYSTEM_KRB5_CONF) 1006 1007#define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf" 1008 /* Insanity, sheer insanity..... */ 1009 1010 if (strequal(realm, lp_realm())) { 1011 char linkpath[PATH_MAX+1]; 1012 int lret; 1013 1014 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1); 1015 if (lret != -1) { 1016 linkpath[lret] = '\0'; 1017 } 1018 1019 if (lret != -1 || strcmp(linkpath, fname) == 0) { 1020 /* Symlink already exists. */ 1021 goto done; 1022 } 1023 1024 /* Try and replace with a symlink. */ 1025 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) { 1026 const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved"; 1027 if (errno != EEXIST) { 1028 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink " 1029 "of %s to %s failed. Errno %s\n", 1030 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) )); 1031 goto done; /* Not a fatal error. */ 1032 } 1033 1034 /* Yes, this is a race conditon... too bad. */ 1035 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) { 1036 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename " 1037 "of %s to %s failed. Errno %s\n", 1038 SYSTEM_KRB5_CONF_PATH, newpath, 1039 strerror(errno) )); 1040 goto done; /* Not a fatal error. */ 1041 } 1042 1043 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) { 1044 DEBUG(0,("create_local_private_krb5_conf_for_domain: " 1045 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n", 1046 fname, strerror(errno) )); 1047 goto done; /* Not a fatal error. */ 1048 } 1049 } 1050 } 1051#endif 1052 1053done: 1054 TALLOC_FREE(tmpname); 1055 TALLOC_FREE(dname); 1056 1057 return result; 1058} 1059#endif 1060