1/* 2 Unix SMB/CIFS implementation. 3 simple kerberos5 routines for active directory 4 Copyright (C) Andrew Tridgell 2001 5 Copyright (C) Luke Howard 2002-2003 6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 7 Copyright (C) Guenther Deschner 2005 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2 of the License, or 12 (at your option) any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software 21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22*/ 23 24#define KRB5_PRIVATE 1 /* this file uses PRIVATE interfaces! */ 25#define KRB5_DEPRECATED 1 /* this file uses DEPRECATED interfaces! */ 26 27#include "includes.h" 28 29#ifdef HAVE_KRB5 30 31#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE 32#define KRB5_KEY_TYPE(k) ((k)->keytype) 33#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length) 34#define KRB5_KEY_DATA(k) ((k)->keyvalue.data) 35#else 36#define KRB5_KEY_TYPE(k) ((k)->enctype) 37#define KRB5_KEY_LENGTH(k) ((k)->length) 38#define KRB5_KEY_DATA(k) ((k)->contents) 39#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */ 40 41/************************************************************** 42 Wrappers around kerberos string functions that convert from 43 utf8 -> unix charset and vica versa. 44**************************************************************/ 45 46/************************************************************** 47 krb5_parse_name that takes a UNIX charset. 48**************************************************************/ 49 50 krb5_error_code smb_krb5_parse_name(krb5_context context, 51 const char *name, /* in unix charset */ 52 krb5_principal *principal) 53{ 54 krb5_error_code ret; 55 char *utf8_name; 56 57 if (push_utf8_allocate(&utf8_name, name) == (size_t)-1) { 58 return ENOMEM; 59 } 60 61 ret = krb5_parse_name(context, utf8_name, principal); 62 SAFE_FREE(utf8_name); 63 return ret; 64} 65 66#ifdef HAVE_KRB5_PARSE_NAME_NOREALM 67/************************************************************** 68 krb5_parse_name_norealm that takes a UNIX charset. 69**************************************************************/ 70 71static krb5_error_code smb_krb5_parse_name_norealm_conv(krb5_context context, 72 const char *name, /* in unix charset */ 73 krb5_principal *principal) 74{ 75 krb5_error_code ret; 76 char *utf8_name; 77 78 *principal = NULL; 79 if (push_utf8_allocate(&utf8_name, name) == (size_t)-1) { 80 return ENOMEM; 81 } 82 83 ret = krb5_parse_name_norealm(context, utf8_name, principal); 84 SAFE_FREE(utf8_name); 85 return ret; 86} 87#endif 88 89/************************************************************** 90 krb5_parse_name that returns a UNIX charset name. Must 91 be freed with normal free() call. 92**************************************************************/ 93 94 krb5_error_code smb_krb5_unparse_name(krb5_context context, 95 krb5_const_principal principal, 96 char **unix_name) 97{ 98 krb5_error_code ret; 99 char *utf8_name; 100 101 *unix_name = NULL; 102 ret = krb5_unparse_name(context, principal, &utf8_name); 103 if (ret) { 104 return ret; 105 } 106 107 if (pull_utf8_allocate(unix_name, utf8_name)==-1) { 108 krb5_free_unparsed_name(context, utf8_name); 109 return ENOMEM; 110 } 111 krb5_free_unparsed_name(context, utf8_name); 112 return 0; 113} 114 115#ifndef HAVE_KRB5_SET_REAL_TIME 116/* 117 * This function is not in the Heimdal mainline. 118 */ 119 krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds) 120{ 121 krb5_error_code ret; 122 int32_t sec, usec; 123 124 ret = krb5_us_timeofday(context, &sec, &usec); 125 if (ret) 126 return ret; 127 128 context->kdc_sec_offset = seconds - sec; 129 context->kdc_usec_offset = microseconds - usec; 130 131 return 0; 132} 133#endif 134 135#if !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES) 136 137#if defined(HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES) 138 139/* With MIT kerberos, we should use krb5_set_default_tgs_enctypes in preference 140 * to krb5_set_default_tgs_ktypes. See 141 * http://lists.samba.org/archive/samba-technical/2006-July/048271.html 142 * 143 * If the MIT libraries are not exporting internal symbols, we will end up in 144 * this branch, which is correct. Otherwise we will continue to use the 145 * internal symbol 146 */ 147 krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc) 148{ 149 return krb5_set_default_tgs_enctypes(ctx, enc); 150} 151 152#elif defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) 153 154/* Heimdal */ 155 krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc) 156{ 157 return krb5_set_default_in_tkt_etypes(ctx, enc); 158} 159 160#endif /* HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES */ 161 162#endif /* HAVE_KRB5_SET_DEFAULT_TGS_KTYPES */ 163 164#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) 165/* HEIMDAL */ 166 void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr) 167{ 168 pkaddr->addr_type = KRB5_ADDRESS_INET; 169 pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr); 170 pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr); 171} 172#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) 173/* MIT */ 174 void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr) 175{ 176 pkaddr->addrtype = ADDRTYPE_INET; 177 pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr); 178 pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr); 179} 180#else 181#error UNKNOWN_ADDRTYPE 182#endif 183 184#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK) 185 int create_kerberos_key_from_string_direct(krb5_context context, 186 krb5_principal host_princ, 187 krb5_data *password, 188 krb5_keyblock *key, 189 krb5_enctype enctype) 190{ 191 int ret; 192 krb5_data salt; 193 krb5_encrypt_block eblock; 194 195 ret = krb5_principal2salt(context, host_princ, &salt); 196 if (ret) { 197 DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret))); 198 return ret; 199 } 200 krb5_use_enctype(context, &eblock, enctype); 201 ret = krb5_string_to_key(context, &eblock, key, password, &salt); 202 SAFE_FREE(salt.data); 203 return ret; 204} 205#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT) 206 int create_kerberos_key_from_string_direct(krb5_context context, 207 krb5_principal host_princ, 208 krb5_data *password, 209 krb5_keyblock *key, 210 krb5_enctype enctype) 211{ 212 int ret; 213 krb5_salt salt; 214 215 ret = krb5_get_pw_salt(context, host_princ, &salt); 216 if (ret) { 217 DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret))); 218 return ret; 219 } 220 221 ret = krb5_string_to_key_salt(context, enctype, (const char *)password->data, salt, key); 222 krb5_free_salt(context, salt); 223 return ret; 224} 225#else 226#error UNKNOWN_CREATE_KEY_FUNCTIONS 227#endif 228 229 int create_kerberos_key_from_string(krb5_context context, 230 krb5_principal host_princ, 231 krb5_data *password, 232 krb5_keyblock *key, 233 krb5_enctype enctype) 234{ 235 krb5_principal salt_princ = NULL; 236 int ret; 237 /* 238 * Check if we've determined that the KDC is salting keys for this 239 * principal/enctype in a non-obvious way. If it is, try to match 240 * its behavior. 241 */ 242 salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype); 243 ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype); 244 if (salt_princ) { 245 krb5_free_principal(context, salt_princ); 246 } 247 return ret; 248} 249 250#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES) 251 krb5_error_code get_kerberos_allowed_etypes(krb5_context context, 252 krb5_enctype **enctypes) 253{ 254 return krb5_get_permitted_enctypes(context, enctypes); 255} 256#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES) 257 krb5_error_code get_kerberos_allowed_etypes(krb5_context context, 258 krb5_enctype **enctypes) 259{ 260 return krb5_get_default_in_tkt_etypes(context, enctypes); 261} 262#else 263#error UNKNOWN_GET_ENCTYPES_FUNCTIONS 264#endif 265 266#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) 267 krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, 268 krb5_auth_context auth_context, 269 krb5_keyblock *keyblock) 270{ 271 return krb5_auth_con_setkey(context, auth_context, keyblock); 272} 273#endif 274 275BOOL unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, DATA_BLOB *unwrapped_pac_data) 276{ 277 DATA_BLOB pac_contents; 278 ASN1_DATA data; 279 int data_type; 280 281 if (!auth_data->length) { 282 return False; 283 } 284 285 asn1_load(&data, *auth_data); 286 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 287 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 288 asn1_start_tag(&data, ASN1_CONTEXT(0)); 289 asn1_read_Integer(&data, &data_type); 290 291 if (data_type != KRB5_AUTHDATA_WIN2K_PAC ) { 292 DEBUG(10,("authorization data is not a Windows PAC (type: %d)\n", data_type)); 293 asn1_free(&data); 294 return False; 295 } 296 297 asn1_end_tag(&data); 298 asn1_start_tag(&data, ASN1_CONTEXT(1)); 299 asn1_read_OctetString(&data, &pac_contents); 300 asn1_end_tag(&data); 301 asn1_end_tag(&data); 302 asn1_end_tag(&data); 303 asn1_free(&data); 304 305 *unwrapped_pac_data = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length); 306 307 data_blob_free(&pac_contents); 308 309 return True; 310} 311 312 BOOL get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt) 313{ 314 DATA_BLOB auth_data_wrapped; 315 BOOL got_auth_data_pac = False; 316 int i; 317 318#if defined(HAVE_KRB5_TKT_ENC_PART2) 319 if (tkt->enc_part2 && tkt->enc_part2->authorization_data && 320 tkt->enc_part2->authorization_data[0] && 321 tkt->enc_part2->authorization_data[0]->length) 322 { 323 for (i = 0; tkt->enc_part2->authorization_data[i] != NULL; i++) { 324 325 if (tkt->enc_part2->authorization_data[i]->ad_type != 326 KRB5_AUTHDATA_IF_RELEVANT) { 327 DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n", 328 tkt->enc_part2->authorization_data[i]->ad_type)); 329 continue; 330 } 331 332 auth_data_wrapped = data_blob(tkt->enc_part2->authorization_data[i]->contents, 333 tkt->enc_part2->authorization_data[i]->length); 334 335 /* check if it is a PAC */ 336 got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data); 337 data_blob_free(&auth_data_wrapped); 338 339 if (!got_auth_data_pac) { 340 continue; 341 } 342 } 343 344 return got_auth_data_pac; 345 } 346 347#else 348 if (tkt->ticket.authorization_data && 349 tkt->ticket.authorization_data->len) 350 { 351 for (i = 0; i < tkt->ticket.authorization_data->len; i++) { 352 353 if (tkt->ticket.authorization_data->val[i].ad_type != 354 KRB5_AUTHDATA_IF_RELEVANT) { 355 DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n", 356 tkt->ticket.authorization_data->val[i].ad_type)); 357 continue; 358 } 359 360 auth_data_wrapped = data_blob(tkt->ticket.authorization_data->val[i].ad_data.data, 361 tkt->ticket.authorization_data->val[i].ad_data.length); 362 363 /* check if it is a PAC */ 364 got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data); 365 data_blob_free(&auth_data_wrapped); 366 367 if (!got_auth_data_pac) { 368 continue; 369 } 370 } 371 372 return got_auth_data_pac; 373 } 374#endif 375 return False; 376} 377 378 krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt) 379{ 380#if defined(HAVE_KRB5_TKT_ENC_PART2) 381 return tkt->enc_part2->client; 382#else 383 return tkt->client; 384#endif 385} 386 387#if !defined(HAVE_KRB5_LOCATE_KDC) 388 389/* krb5_locate_kdc is an internal MIT symbol. MIT are not yet willing to commit 390 * to a public interface for this functionality, so we have to be able to live 391 * without it if the MIT libraries are hiding their internal symbols. 392 */ 393 394#if defined(KRB5_KRBHST_INIT) 395/* Heimdal */ 396 krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters) 397{ 398 krb5_krbhst_handle hnd; 399 krb5_krbhst_info *hinfo; 400 krb5_error_code rc; 401 int num_kdcs, i; 402 struct sockaddr *sa; 403 struct addrinfo *ai; 404 405 *addr_pp = NULL; 406 *naddrs = 0; 407 408 rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd); 409 if (rc) { 410 DEBUG(0, ("smb_krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc))); 411 return rc; 412 } 413 414 for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++) 415 ; 416 417 krb5_krbhst_reset(ctx, hnd); 418 419 if (!num_kdcs) { 420 DEBUG(0, ("smb_krb5_locate_kdc: zero kdcs found !\n")); 421 krb5_krbhst_free(ctx, hnd); 422 return -1; 423 } 424 425 sa = SMB_MALLOC_ARRAY( struct sockaddr, num_kdcs ); 426 if (!sa) { 427 DEBUG(0, ("smb_krb5_locate_kdc: malloc failed\n")); 428 krb5_krbhst_free(ctx, hnd); 429 naddrs = 0; 430 return -1; 431 } 432 433 memset(sa, '\0', sizeof(struct sockaddr) * num_kdcs ); 434 435 for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) { 436 437#if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO) 438 rc = krb5_krbhst_get_addrinfo(ctx, hinfo, &ai); 439 if (rc) { 440 DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc))); 441 continue; 442 } 443#endif 444 if (hinfo->ai && hinfo->ai->ai_family == AF_INET) 445 memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr)); 446 } 447 448 krb5_krbhst_free(ctx, hnd); 449 450 *naddrs = num_kdcs; 451 *addr_pp = sa; 452 return 0; 453} 454 455#else /* ! defined(KRB5_KRBHST_INIT) */ 456 457 krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, 458 struct sockaddr **addr_pp, int *naddrs, int get_masters) 459{ 460 DEBUG(0, ("unable to explicitly locate the KDC on this platform\n")); 461 return KRB5_KDC_UNREACH; 462} 463 464#endif /* KRB5_KRBHST_INIT */ 465 466#else /* ! HAVE_KRB5_LOCATE_KDC */ 467 468 krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, 469 struct sockaddr **addr_pp, int *naddrs, int get_masters) 470{ 471 return krb5_locate_kdc(ctx, realm, addr_pp, naddrs, get_masters); 472} 473 474#endif /* HAVE_KRB5_LOCATE_KDC */ 475 476#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME) 477 void krb5_free_unparsed_name(krb5_context context, char *val) 478{ 479 SAFE_FREE(val); 480} 481#endif 482 483 void kerberos_free_data_contents(krb5_context context, krb5_data *pdata) 484{ 485#if defined(HAVE_KRB5_FREE_DATA_CONTENTS) 486 if (pdata->data) { 487 krb5_free_data_contents(context, pdata); 488 } 489#else 490 SAFE_FREE(pdata->data); 491#endif 492} 493 494 void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype) 495{ 496#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS) 497 KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype; 498#elif defined(HAVE_KRB5_SESSION_IN_CREDS) 499 KRB5_KEY_TYPE((&pcreds->session)) = enctype; 500#else 501#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT 502#endif 503} 504 505 BOOL kerberos_compatible_enctypes(krb5_context context, 506 krb5_enctype enctype1, 507 krb5_enctype enctype2) 508{ 509#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE) 510 krb5_boolean similar = 0; 511 512 krb5_c_enctype_compare(context, enctype1, enctype2, &similar); 513 return similar ? True : False; 514#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS) 515 return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False; 516#endif 517} 518 519static BOOL ads_cleanup_expired_creds(krb5_context context, 520 krb5_ccache ccache, 521 krb5_creds *credsp) 522{ 523 krb5_error_code retval; 524 const char *cc_type = krb5_cc_get_type(context, ccache); 525 526 DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n", 527 cc_type, krb5_cc_get_name(context, ccache), 528 http_timestring(credsp->times.endtime))); 529 530 /* we will probably need new tickets if the current ones 531 will expire within 10 seconds. 532 */ 533 if (credsp->times.endtime >= (time(NULL) + 10)) 534 return False; 535 536 /* heimdal won't remove creds from a file ccache, and 537 perhaps we shouldn't anyway, since internally we 538 use memory ccaches, and a FILE one probably means that 539 we're using creds obtained outside of our exectuable 540 */ 541 if (strequal(cc_type, "FILE")) { 542 DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type)); 543 return False; 544 } 545 546 retval = krb5_cc_remove_cred(context, ccache, 0, credsp); 547 if (retval) { 548 DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n", 549 error_message(retval))); 550 /* If we have an error in this, we want to display it, 551 but continue as though we deleted it */ 552 } 553 return True; 554} 555 556/* 557 we can't use krb5_mk_req because w2k wants the service to be in a particular format 558*/ 559static krb5_error_code ads_krb5_mk_req(krb5_context context, 560 krb5_auth_context *auth_context, 561 const krb5_flags ap_req_options, 562 const char *principal, 563 krb5_ccache ccache, 564 krb5_data *outbuf, 565 time_t *expire_time) 566{ 567 krb5_error_code retval; 568 krb5_principal server; 569 krb5_creds * credsp; 570 krb5_creds creds; 571 krb5_data in_data; 572 BOOL creds_ready = False; 573 int i = 0, maxtries = 3; 574 575 retval = smb_krb5_parse_name(context, principal, &server); 576 if (retval) { 577 DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal)); 578 return retval; 579 } 580 581 /* obtain ticket & session key */ 582 ZERO_STRUCT(creds); 583 if ((retval = krb5_copy_principal(context, server, &creds.server))) { 584 DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n", 585 error_message(retval))); 586 goto cleanup_princ; 587 } 588 589 if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) { 590 /* This can commonly fail on smbd startup with no ticket in the cache. 591 * Report at higher level than 1. */ 592 DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n", 593 error_message(retval))); 594 goto cleanup_creds; 595 } 596 597 while (!creds_ready && (i < maxtries)) { 598 599 if ((retval = krb5_get_credentials(context, 0, ccache, 600 &creds, &credsp))) { 601 DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n", 602 principal, error_message(retval))); 603 goto cleanup_creds; 604 } 605 606 /* cope with ticket being in the future due to clock skew */ 607 if ((unsigned)credsp->times.starttime > time(NULL)) { 608 time_t t = time(NULL); 609 int time_offset =(int)((unsigned)credsp->times.starttime-t); 610 DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset)); 611 krb5_set_real_time(context, t + time_offset + 1, 0); 612 } 613 614 if (!ads_cleanup_expired_creds(context, ccache, credsp)) { 615 creds_ready = True; 616 } 617 618 i++; 619 } 620 621 DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n", 622 principal, krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache), 623 http_timestring((unsigned)credsp->times.endtime), 624 (unsigned)credsp->times.endtime)); 625 626 if (expire_time) { 627 *expire_time = (time_t)credsp->times.endtime; 628 } 629 630 in_data.length = 0; 631 retval = krb5_mk_req_extended(context, auth_context, ap_req_options, 632 &in_data, credsp, outbuf); 633 if (retval) { 634 DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", 635 error_message(retval))); 636 } 637 638 krb5_free_creds(context, credsp); 639 640cleanup_creds: 641 krb5_free_cred_contents(context, &creds); 642 643cleanup_princ: 644 krb5_free_principal(context, server); 645 646 return retval; 647} 648 649/* 650 get a kerberos5 ticket for the given service 651*/ 652int cli_krb5_get_ticket(const char *principal, time_t time_offset, 653 DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, 654 uint32 extra_ap_opts, const char *ccname, 655 time_t *tgs_expire) 656 657{ 658 krb5_error_code retval; 659 krb5_data packet; 660 krb5_context context = NULL; 661 krb5_ccache ccdef = NULL; 662 krb5_auth_context auth_context = NULL; 663 krb5_enctype enc_types[] = { 664#ifdef ENCTYPE_ARCFOUR_HMAC 665 ENCTYPE_ARCFOUR_HMAC, 666#endif 667 ENCTYPE_DES_CBC_MD5, 668 ENCTYPE_DES_CBC_CRC, 669 ENCTYPE_NULL}; 670 671 initialize_krb5_error_table(); 672 retval = krb5_init_context(&context); 673 if (retval) { 674 DEBUG(1,("cli_krb5_get_ticket: krb5_init_context failed (%s)\n", 675 error_message(retval))); 676 goto failed; 677 } 678 679 if (time_offset != 0) { 680 krb5_set_real_time(context, time(NULL) + time_offset, 0); 681 } 682 683 if ((retval = krb5_cc_resolve(context, ccname ? 684 ccname : krb5_cc_default_name(context), &ccdef))) { 685 DEBUG(1,("cli_krb5_get_ticket: krb5_cc_default failed (%s)\n", 686 error_message(retval))); 687 goto failed; 688 } 689 690 if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) { 691 DEBUG(1,("cli_krb5_get_ticket: krb5_set_default_tgs_ktypes failed (%s)\n", 692 error_message(retval))); 693 goto failed; 694 } 695 696 if ((retval = ads_krb5_mk_req(context, 697 &auth_context, 698 AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts, 699 principal, 700 ccdef, &packet, 701 tgs_expire))) { 702 goto failed; 703 } 704 705 get_krb5_smb_session_key(context, auth_context, session_key_krb5, False); 706 707 *ticket = data_blob(packet.data, packet.length); 708 709 kerberos_free_data_contents(context, &packet); 710 711failed: 712 713 if ( context ) { 714 if (ccdef) 715 krb5_cc_close(context, ccdef); 716 if (auth_context) 717 krb5_auth_con_free(context, auth_context); 718 krb5_free_context(context); 719 } 720 721 return retval; 722} 723 724 BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote) 725 { 726 krb5_keyblock *skey; 727 krb5_error_code err; 728 BOOL ret = False; 729 730 if (remote) 731 err = krb5_auth_con_getremotesubkey(context, auth_context, &skey); 732 else 733 err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey); 734 if (err == 0 && skey != NULL) { 735 DEBUG(10, ("Got KRB5 session key of length %d\n", (int)KRB5_KEY_LENGTH(skey))); 736 *session_key = data_blob(KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); 737 dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); 738 739 ret = True; 740 741 krb5_free_keyblock(context, skey); 742 } else { 743 DEBUG(10, ("KRB5 error getting session key %d\n", err)); 744 } 745 746 return ret; 747 } 748 749 750#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT) 751 const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i ); 752 753 const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i ) 754{ 755 static krb5_data kdata; 756 757 kdata.data = (char *)krb5_principal_get_comp_string(context, principal, i); 758 kdata.length = strlen((const char *)kdata.data); 759 return &kdata; 760} 761#endif 762 763 krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry) 764{ 765#if defined(HAVE_KRB5_KT_FREE_ENTRY) 766 return krb5_kt_free_entry(context, kt_entry); 767#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS) 768 return krb5_free_keytab_entry_contents(context, kt_entry); 769#else 770#error UNKNOWN_KT_FREE_FUNCTION 771#endif 772} 773 774 void smb_krb5_checksum_from_pac_sig(krb5_checksum *cksum, 775 PAC_SIGNATURE_DATA *sig) 776{ 777#ifdef HAVE_CHECKSUM_IN_KRB5_CHECKSUM 778 cksum->cksumtype = (krb5_cksumtype)sig->type; 779 cksum->checksum.length = sig->signature.buf_len; 780 cksum->checksum.data = sig->signature.buffer; 781#else 782 cksum->checksum_type = (krb5_cksumtype)sig->type; 783 cksum->length = sig->signature.buf_len; 784 cksum->contents = sig->signature.buffer; 785#endif 786} 787 788 krb5_error_code smb_krb5_verify_checksum(krb5_context context, 789 krb5_keyblock *keyblock, 790 krb5_keyusage usage, 791 krb5_checksum *cksum, 792 uint8 *data, 793 size_t length) 794{ 795 krb5_error_code ret; 796 797 /* verify the checksum */ 798 799 /* welcome to the wonderful world of samba's kerberos abstraction layer: 800 * 801 * function heimdal 0.6.1rc3 heimdal 0.7 MIT krb 1.4.2 802 * ----------------------------------------------------------------------------- 803 * krb5_c_verify_checksum - works works 804 * krb5_verify_checksum works (6 args) works (6 args) broken (7 args) 805 */ 806 807#if defined(HAVE_KRB5_C_VERIFY_CHECKSUM) 808 { 809 krb5_boolean checksum_valid = False; 810 krb5_data input; 811 812 input.data = (char *)data; 813 input.length = length; 814 815 ret = krb5_c_verify_checksum(context, 816 keyblock, 817 usage, 818 &input, 819 cksum, 820 &checksum_valid); 821 if (ret) { 822 DEBUG(3,("smb_krb5_verify_checksum: krb5_c_verify_checksum() failed: %s\n", 823 error_message(ret))); 824 return ret; 825 } 826 827 if (!checksum_valid) 828 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; 829 } 830 831#elif KRB5_VERIFY_CHECKSUM_ARGS == 6 && defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CRYPTO) && defined(HAVE_KRB5_CRYPTO_DESTROY) 832 833 /* Warning: MIT's krb5_verify_checksum cannot be used as it will use a key 834 * without enctype and it ignores any key_usage types - Guenther */ 835 836 { 837 838 krb5_crypto crypto; 839 ret = krb5_crypto_init(context, 840 keyblock, 841 0, 842 &crypto); 843 if (ret) { 844 DEBUG(0,("smb_krb5_verify_checksum: krb5_crypto_init() failed: %s\n", 845 error_message(ret))); 846 return ret; 847 } 848 849 ret = krb5_verify_checksum(context, 850 crypto, 851 usage, 852 data, 853 length, 854 cksum); 855 856 krb5_crypto_destroy(context, crypto); 857 } 858 859#else 860#error UNKNOWN_KRB5_VERIFY_CHECKSUM_FUNCTION 861#endif 862 863 return ret; 864} 865 866 time_t get_authtime_from_tkt(krb5_ticket *tkt) 867{ 868#if defined(HAVE_KRB5_TKT_ENC_PART2) 869 return tkt->enc_part2->times.authtime; 870#else 871 return tkt->ticket.authtime; 872#endif 873} 874 875#ifdef HAVE_KRB5_DECODE_AP_REQ /* Heimdal */ 876static int get_kvno_from_ap_req(krb5_ap_req *ap_req) 877{ 878#ifdef HAVE_TICKET_POINTER_IN_KRB5_AP_REQ /* MIT */ 879 if (ap_req->ticket->enc_part.kvno) 880 return ap_req->ticket->enc_part.kvno; 881#else /* Heimdal */ 882 if (ap_req->ticket.enc_part.kvno) 883 return *ap_req->ticket.enc_part.kvno; 884#endif 885 return 0; 886} 887 888static krb5_enctype get_enctype_from_ap_req(krb5_ap_req *ap_req) 889{ 890#ifdef HAVE_ETYPE_IN_ENCRYPTEDDATA /* Heimdal */ 891 return ap_req->ticket.enc_part.etype; 892#else /* MIT */ 893 return ap_req->ticket->enc_part.enctype; 894#endif 895} 896#endif /* HAVE_KRB5_DECODE_AP_REQ */ 897 898static krb5_error_code 899get_key_from_keytab(krb5_context context, 900 krb5_const_principal server, 901 krb5_enctype enctype, 902 krb5_kvno kvno, 903 krb5_keyblock **out_key) 904{ 905 krb5_keytab_entry entry; 906 krb5_error_code ret; 907 krb5_keytab keytab; 908 char *name = NULL; 909 910 /* We have to open a new keytab handle here, as MIT does 911 an implicit open/getnext/close on krb5_kt_get_entry. We 912 may be in the middle of a keytab enumeration when this is 913 called. JRA. */ 914 915 ret = krb5_kt_default(context, &keytab); 916 if (ret) { 917 DEBUG(0,("get_key_from_keytab: failed to open keytab: %s\n", error_message(ret))); 918 return ret; 919 } 920 921 if ( DEBUGLEVEL >= 10 ) { 922 if (smb_krb5_unparse_name(context, server, &name) == 0) { 923 DEBUG(10,("get_key_from_keytab: will look for kvno %d, enctype %d and name: %s\n", 924 kvno, enctype, name)); 925 SAFE_FREE(name); 926 } 927 } 928 929 ret = krb5_kt_get_entry(context, 930 keytab, 931 server, 932 kvno, 933 enctype, 934 &entry); 935 936 if (ret) { 937 DEBUG(0,("get_key_from_keytab: failed to retrieve key: %s\n", error_message(ret))); 938 goto out; 939 } 940 941#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK /* Heimdal */ 942 ret = krb5_copy_keyblock(context, &entry.keyblock, out_key); 943#elif defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) /* MIT */ 944 ret = krb5_copy_keyblock(context, &entry.key, out_key); 945#else 946#error UNKNOWN_KRB5_KEYTAB_ENTRY_FORMAT 947#endif 948 949 if (ret) { 950 DEBUG(0,("get_key_from_keytab: failed to copy key: %s\n", error_message(ret))); 951 goto out; 952 } 953 954 smb_krb5_kt_free_entry(context, &entry); 955 956out: 957 krb5_kt_close(context, keytab); 958 return ret; 959} 960 961/* Prototypes */ 962 963 krb5_error_code smb_krb5_get_keyinfo_from_ap_req(krb5_context context, 964 const krb5_data *inbuf, 965 krb5_kvno *kvno, 966 krb5_enctype *enctype) 967{ 968#ifdef HAVE_KRB5_DECODE_AP_REQ /* Heimdal */ 969 { 970 krb5_error_code ret; 971 krb5_ap_req ap_req; 972 973 ret = krb5_decode_ap_req(context, inbuf, &ap_req); 974 if (ret) 975 return ret; 976 977 *kvno = get_kvno_from_ap_req(&ap_req); 978 *enctype = get_enctype_from_ap_req(&ap_req); 979 980 free_AP_REQ(&ap_req); 981 return 0; 982 } 983#endif 984 985 /* Possibly not an appropriate error code. */ 986 return KRB5KDC_ERR_BADOPTION; 987} 988 989 krb5_error_code krb5_rd_req_return_keyblock_from_keytab(krb5_context context, 990 krb5_auth_context *auth_context, 991 const krb5_data *inbuf, 992 krb5_const_principal server, 993 krb5_keytab keytab, 994 krb5_flags *ap_req_options, 995 krb5_ticket **ticket, 996 krb5_keyblock **keyblock) 997{ 998 krb5_error_code ret; 999 krb5_kvno kvno; 1000 krb5_enctype enctype; 1001 krb5_keyblock *local_keyblock; 1002 1003 ret = krb5_rd_req(context, 1004 auth_context, 1005 inbuf, 1006 server, 1007 keytab, 1008 ap_req_options, 1009 ticket); 1010 if (ret) { 1011 return ret; 1012 } 1013 1014#ifdef KRB5_TICKET_HAS_KEYINFO 1015 enctype = (*ticket)->enc_part.enctype; 1016 kvno = (*ticket)->enc_part.kvno; 1017#else 1018 ret = smb_krb5_get_keyinfo_from_ap_req(context, inbuf, &kvno, &enctype); 1019 if (ret) { 1020 return ret; 1021 } 1022#endif 1023 1024 ret = get_key_from_keytab(context, 1025 server, 1026 enctype, 1027 kvno, 1028 &local_keyblock); 1029 if (ret) { 1030 DEBUG(0,("krb5_rd_req_return_keyblock_from_keytab: failed to call get_key_from_keytab\n")); 1031 goto out; 1032 } 1033 1034out: 1035 if (ret && local_keyblock != NULL) { 1036 krb5_free_keyblock(context, local_keyblock); 1037 } else { 1038 *keyblock = local_keyblock; 1039 } 1040 1041 return ret; 1042} 1043 1044 krb5_error_code smb_krb5_parse_name_norealm(krb5_context context, 1045 const char *name, 1046 krb5_principal *principal) 1047{ 1048#ifdef HAVE_KRB5_PARSE_NAME_NOREALM 1049 return smb_krb5_parse_name_norealm_conv(context, name, principal); 1050#endif 1051 1052 /* we are cheating here because parse_name will in fact set the realm. 1053 * We don't care as the only caller of smb_krb5_parse_name_norealm 1054 * ignores the realm anyway when calling 1055 * smb_krb5_principal_compare_any_realm later - Guenther */ 1056 1057 return smb_krb5_parse_name(context, name, principal); 1058} 1059 1060 BOOL smb_krb5_principal_compare_any_realm(krb5_context context, 1061 krb5_const_principal princ1, 1062 krb5_const_principal princ2) 1063{ 1064#ifdef HAVE_KRB5_PRINCIPAL_COMPARE_ANY_REALM 1065 1066 return krb5_principal_compare_any_realm(context, princ1, princ2); 1067 1068/* krb5_princ_size is a macro in MIT */ 1069#elif defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size) 1070 1071 int i, len1, len2; 1072 const krb5_data *p1, *p2; 1073 1074 len1 = krb5_princ_size(context, princ1); 1075 len2 = krb5_princ_size(context, princ2); 1076 1077 if (len1 != len2) 1078 return False; 1079 1080 for (i = 0; i < len1; i++) { 1081 1082 p1 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ1), i); 1083 p2 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ2), i); 1084 1085 if (p1->length != p2->length || memcmp(p1->data, p2->data, p1->length)) 1086 return False; 1087 } 1088 1089 return True; 1090#else 1091#error NO_SUITABLE_PRINCIPAL_COMPARE_FUNCTION 1092#endif 1093} 1094 1095 krb5_error_code smb_krb5_renew_ticket(const char *ccache_string, /* FILE:/tmp/krb5cc_0 */ 1096 const char *client_string, /* gd@BER.SUSE.DE */ 1097 const char *service_string, /* krbtgt/BER.SUSE.DE@BER.SUSE.DE */ 1098 time_t *expire_time) 1099{ 1100 krb5_error_code ret; 1101 krb5_context context = NULL; 1102 krb5_ccache ccache = NULL; 1103 krb5_principal client = NULL; 1104 1105 initialize_krb5_error_table(); 1106 ret = krb5_init_context(&context); 1107 if (ret) { 1108 goto done; 1109 } 1110 1111 if (!ccache_string) { 1112 ccache_string = krb5_cc_default_name(context); 1113 } 1114 1115 DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string)); 1116 1117 /* FIXME: we should not fall back to defaults */ 1118 ret = krb5_cc_resolve(context, CONST_DISCARD(char *, ccache_string), &ccache); 1119 if (ret) { 1120 goto done; 1121 } 1122 1123#ifdef HAVE_KRB5_GET_RENEWED_CREDS /* MIT */ 1124 { 1125 krb5_creds creds; 1126 1127 if (client_string) { 1128 ret = smb_krb5_parse_name(context, client_string, &client); 1129 if (ret) { 1130 goto done; 1131 } 1132 } else { 1133 ret = krb5_cc_get_principal(context, ccache, &client); 1134 if (ret) { 1135 goto done; 1136 } 1137 } 1138 1139 ret = krb5_get_renewed_creds(context, &creds, client, ccache, CONST_DISCARD(char *, service_string)); 1140 if (ret) { 1141 DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret))); 1142 goto done; 1143 } 1144 1145 /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */ 1146 ret = krb5_cc_initialize(context, ccache, client); 1147 if (ret) { 1148 goto done; 1149 } 1150 1151 ret = krb5_cc_store_cred(context, ccache, &creds); 1152 1153 if (expire_time) { 1154 *expire_time = (time_t) creds.times.endtime; 1155 } 1156 1157 krb5_free_cred_contents(context, &creds); 1158 } 1159#elif defined(HAVE_KRB5_GET_KDC_CRED) /* Heimdal */ 1160 { 1161 krb5_kdc_flags flags; 1162 krb5_creds creds_in; 1163 krb5_realm *client_realm; 1164 krb5_creds *creds; 1165 1166 memset(&creds_in, 0, sizeof(creds_in)); 1167 1168 if (client_string) { 1169 ret = smb_krb5_parse_name(context, client_string, &creds_in.client); 1170 if (ret) { 1171 goto done; 1172 } 1173 } else { 1174 ret = krb5_cc_get_principal(context, ccache, &creds_in.client); 1175 if (ret) { 1176 goto done; 1177 } 1178 } 1179 1180 if (service_string) { 1181 ret = smb_krb5_parse_name(context, service_string, &creds_in.server); 1182 if (ret) { 1183 goto done; 1184 } 1185 } else { 1186 /* build tgt service by default */ 1187 client_realm = krb5_princ_realm(context, creds_in.client); 1188 if (!client_realm) { 1189 ret = ENOMEM; 1190 goto done; 1191 } 1192 ret = krb5_make_principal(context, &creds_in.server, *client_realm, KRB5_TGS_NAME, *client_realm, NULL); 1193 if (ret) { 1194 goto done; 1195 } 1196 } 1197 1198 flags.i = 0; 1199 flags.b.renewable = flags.b.renew = True; 1200 1201 ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &creds_in, &creds); 1202 if (ret) { 1203 DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret))); 1204 goto done; 1205 } 1206 1207 /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */ 1208 ret = krb5_cc_initialize(context, ccache, creds_in.client); 1209 if (ret) { 1210 goto done; 1211 } 1212 1213 ret = krb5_cc_store_cred(context, ccache, creds); 1214 1215 if (expire_time) { 1216 *expire_time = (time_t) creds->times.endtime; 1217 } 1218 1219 krb5_free_cred_contents(context, &creds_in); 1220 krb5_free_creds(context, creds); 1221 } 1222#else 1223#error No suitable krb5 ticket renew function available 1224#endif 1225 1226 1227done: 1228 if (client) { 1229 krb5_free_principal(context, client); 1230 } 1231 if (context) { 1232 krb5_free_context(context); 1233 } 1234 if (ccache) { 1235 krb5_cc_close(context, ccache); 1236 } 1237 1238 return ret; 1239 1240} 1241 1242 krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr) 1243{ 1244 krb5_error_code ret = 0; 1245 if (addr == NULL) { 1246 return ret; 1247 } 1248#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */ 1249 krb5_free_addresses(context, addr->addrs); 1250#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */ 1251 ret = krb5_free_addresses(context, addr->addrs); 1252 SAFE_FREE(addr->addrs); 1253#endif 1254 SAFE_FREE(addr); 1255 addr = NULL; 1256 return ret; 1257} 1258 1259 krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr) 1260{ 1261 krb5_error_code ret = 0; 1262 nstring buf; 1263#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */ 1264 krb5_address **addrs = NULL; 1265#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */ 1266 krb5_addresses *addrs = NULL; 1267#endif 1268 1269 *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses)); 1270 if (*kerb_addr == NULL) { 1271 return ENOMEM; 1272 } 1273 1274 put_name(buf, global_myname(), ' ', 0x20); 1275 1276#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */ 1277 { 1278 int num_addr = 2; 1279 1280 addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr); 1281 if (addrs == NULL) { 1282 SAFE_FREE(kerb_addr); 1283 return ENOMEM; 1284 } 1285 1286 memset(addrs, 0, sizeof(krb5_address *) * num_addr); 1287 1288 addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address)); 1289 if (addrs[0] == NULL) { 1290 SAFE_FREE(addrs); 1291 SAFE_FREE(kerb_addr); 1292 return ENOMEM; 1293 } 1294 1295 addrs[0]->magic = KV5M_ADDRESS; 1296 addrs[0]->addrtype = KRB5_ADDR_NETBIOS; 1297 addrs[0]->length = MAX_NETBIOSNAME_LEN; 1298 addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length); 1299 if (addrs[0]->contents == NULL) { 1300 SAFE_FREE(addrs[0]); 1301 SAFE_FREE(addrs); 1302 SAFE_FREE(kerb_addr); 1303 return ENOMEM; 1304 } 1305 1306 memcpy(addrs[0]->contents, buf, addrs[0]->length); 1307 1308 addrs[1] = NULL; 1309 } 1310#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */ 1311 { 1312 addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses)); 1313 if (addrs == NULL) { 1314 SAFE_FREE(kerb_addr); 1315 return ENOMEM; 1316 } 1317 1318 memset(addrs, 0, sizeof(krb5_addresses)); 1319 1320 addrs->len = 1; 1321 addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address)); 1322 if (addrs->val == NULL) { 1323 SAFE_FREE(addrs); 1324 SAFE_FREE(kerb_addr); 1325 return ENOMEM; 1326 } 1327 1328 addrs->val[0].addr_type = KRB5_ADDR_NETBIOS; 1329 addrs->val[0].address.length = MAX_NETBIOSNAME_LEN; 1330 addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length); 1331 if (addrs->val[0].address.data == NULL) { 1332 SAFE_FREE(addrs->val); 1333 SAFE_FREE(addrs); 1334 SAFE_FREE(kerb_addr); 1335 return ENOMEM; 1336 } 1337 1338 memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length); 1339 } 1340#else 1341#error UNKNOWN_KRB5_ADDRESS_FORMAT 1342#endif 1343 (*kerb_addr)->addrs = addrs; 1344 1345 return ret; 1346} 1347 1348 void smb_krb5_free_error(krb5_context context, krb5_error *krberror) 1349{ 1350#ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */ 1351 krb5_free_error_contents(context, krberror); 1352#else /* MIT */ 1353 krb5_free_error(context, krberror); 1354#endif 1355} 1356 1357 krb5_error_code handle_krberror_packet(krb5_context context, 1358 krb5_data *packet) 1359{ 1360 krb5_error_code ret; 1361 BOOL got_error_code = False; 1362 1363 DEBUG(10,("handle_krberror_packet: got error packet\n")); 1364 1365#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */ 1366 { 1367 krb5_error krberror; 1368 1369 if ((ret = krb5_rd_error(context, packet, &krberror))) { 1370 DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 1371 error_message(ret))); 1372 return ret; 1373 } 1374 1375 if (krberror.e_data == NULL || krberror.e_data->data == NULL) { 1376 ret = (krb5_error_code) krberror.error_code; 1377 got_error_code = True; 1378 } 1379 1380 smb_krb5_free_error(context, &krberror); 1381 } 1382#else /* MIT */ 1383 { 1384 krb5_error *krberror; 1385 1386 if ((ret = krb5_rd_error(context, packet, &krberror))) { 1387 DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 1388 error_message(ret))); 1389 return ret; 1390 } 1391 1392 if (krberror->e_data.data == NULL) { 1393 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error; 1394 got_error_code = True; 1395 } 1396 smb_krb5_free_error(context, krberror); 1397 } 1398#endif 1399 if (got_error_code) { 1400 DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n", 1401 error_message(ret), ret)); 1402 } 1403 return ret; 1404} 1405 1406 krb5_error_code smb_krb5_get_init_creds_opt_alloc(krb5_context context, 1407 krb5_get_init_creds_opt **opt) 1408{ 1409#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC 1410 /* Heimdal or modern MIT version */ 1411 return krb5_get_init_creds_opt_alloc(context, opt); 1412#else 1413 /* Historical MIT version */ 1414 krb5_get_init_creds_opt *my_opt; 1415 1416 *opt = NULL; 1417 1418 if ((my_opt = SMB_MALLOC(sizeof(krb5_get_init_creds_opt))) == NULL) { 1419 return ENOMEM; 1420 } 1421 1422 krb5_get_init_creds_opt_init(my_opt); 1423 1424 *opt = my_opt; 1425 return 0; 1426#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC */ 1427} 1428 1429 void smb_krb5_get_init_creds_opt_free(krb5_context context, 1430 krb5_get_init_creds_opt *opt) 1431{ 1432#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE 1433 1434#ifdef KRB5_CREDS_OPT_FREE_REQUIRES_CONTEXT 1435 /* Modern MIT version */ 1436 krb5_get_init_creds_opt_free(context, opt); 1437#else 1438 /* Heimdal version */ 1439 krb5_get_init_creds_opt_free(opt); 1440#endif 1441 1442#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_FREE */ 1443 /* Historical MIT version */ 1444 SAFE_FREE(opt); 1445 opt = NULL; 1446#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_FREE */ 1447} 1448 1449 krb5_error_code smb_krb5_mk_error(krb5_context context, 1450 krb5_error_code error_code, 1451 const krb5_principal server, 1452 krb5_data *reply) 1453{ 1454#ifdef HAVE_SHORT_KRB5_MK_ERROR_INTERFACE /* MIT */ 1455 /* 1456 * The MIT interface is *terrible*. 1457 * We have to construct this ourselves... 1458 */ 1459 krb5_error e; 1460 1461 memset(&e, 0, sizeof(e)); 1462 krb5_us_timeofday(context, &e.stime, &e.susec); 1463 e.server = server; 1464#if defined(krb5_err_base) 1465 e.error = error_code - krb5_err_base; 1466#elif defined(ERROR_TABLE_BASE_krb5) 1467 e.error = error_code - ERROR_TABLE_BASE_krb5; 1468#else 1469 e.error = error_code; /* Almost certainly wrong, but what can we do... ? */ 1470#endif 1471 1472 return krb5_mk_error(context, &e, reply); 1473#else /* Heimdal. */ 1474 return krb5_mk_error(context, 1475 error_code, 1476 NULL, 1477 NULL, /* e_data */ 1478 NULL, 1479 server, 1480 NULL, 1481 NULL, 1482 reply); 1483#endif 1484} 1485 1486#else /* HAVE_KRB5 */ 1487 /* this saves a few linking headaches */ 1488 int cli_krb5_get_ticket(const char *principal, time_t time_offset, 1489 DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, uint32 extra_ap_opts, 1490 const char *ccname, time_t *tgs_expire) 1491{ 1492 DEBUG(0,("NO KERBEROS SUPPORT\n")); 1493 return 1; 1494} 1495 1496#endif 1497