1/* 2 * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2000, 2001 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id$ */ 19 20#include <config.h> 21 22#include <ctype.h> 23#include <stdlib.h> 24#include <string.h> 25 26#include <isc/buffer.h> 27#include <isc/dir.h> 28#include <isc/entropy.h> 29#include <isc/file.h> 30#include <isc/lex.h> 31#include <isc/mem.h> 32#include <isc/once.h> 33#include <isc/print.h> 34#include <isc/platform.h> 35#include <isc/random.h> 36#include <isc/string.h> 37#include <isc/time.h> 38#include <isc/util.h> 39 40#include <dns/fixedname.h> 41#include <dns/name.h> 42#include <dns/rdata.h> 43#include <dns/rdataclass.h> 44#include <dns/result.h> 45#include <dns/types.h> 46#include <dns/keyvalues.h> 47#include <dns/log.h> 48 49#include <dst/gssapi.h> 50#include <dst/result.h> 51 52#include "dst_internal.h" 53 54/* 55 * If we're using our own SPNEGO implementation (see configure.in), 56 * pull it in now. Otherwise, we just use whatever GSSAPI supplies. 57 */ 58#if defined(GSSAPI) && defined(USE_ISC_SPNEGO) 59#include "spnego.h" 60#define gss_accept_sec_context gss_accept_sec_context_spnego 61#define gss_init_sec_context gss_init_sec_context_spnego 62#endif 63 64/* 65 * Solaris8 apparently needs an explicit OID set, and Solaris10 needs 66 * one for anything but Kerberos. Supplying an explicit OID set 67 * doesn't appear to hurt anything in other implementations, so we 68 * always use one. If we're not using our own SPNEGO implementation, 69 * we include SPNEGO's OID. 70 */ 71#if defined(GSSAPI) 72#include ISC_PLATFORM_KRB5HEADER 73 74static unsigned char krb5_mech_oid_bytes[] = { 75 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 76}; 77 78#ifndef USE_ISC_SPNEGO 79static unsigned char spnego_mech_oid_bytes[] = { 80 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02 81}; 82#endif 83 84static gss_OID_desc mech_oid_set_array[] = { 85 { sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes }, 86#ifndef USE_ISC_SPNEGO 87 { sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes }, 88#endif 89}; 90 91static gss_OID_set_desc mech_oid_set = { 92 sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array), 93 mech_oid_set_array 94}; 95 96#endif 97 98#define REGION_TO_GBUFFER(r, gb) \ 99 do { \ 100 (gb).length = (r).length; \ 101 (gb).value = (r).base; \ 102 } while (0) 103 104#define GBUFFER_TO_REGION(gb, r) \ 105 do { \ 106 (r).length = (gb).length; \ 107 (r).base = (gb).value; \ 108 } while (0) 109 110 111#define RETERR(x) do { \ 112 result = (x); \ 113 if (result != ISC_R_SUCCESS) \ 114 goto out; \ 115 } while (0) 116 117#ifdef GSSAPI 118static inline void 119name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer, 120 gss_buffer_desc *gbuffer) 121{ 122 dns_name_t tname, *namep; 123 isc_region_t r; 124 isc_result_t result; 125 126 if (!dns_name_isabsolute(name)) 127 namep = name; 128 else 129 { 130 unsigned int labels; 131 dns_name_init(&tname, NULL); 132 labels = dns_name_countlabels(name); 133 dns_name_getlabelsequence(name, 0, labels - 1, &tname); 134 namep = &tname; 135 } 136 137 result = dns_name_toprincipal(namep, buffer); 138 RUNTIME_CHECK(result == ISC_R_SUCCESS); 139 isc_buffer_putuint8(buffer, 0); 140 isc_buffer_usedregion(buffer, &r); 141 REGION_TO_GBUFFER(r, *gbuffer); 142} 143 144static void 145log_cred(const gss_cred_id_t cred) { 146 OM_uint32 gret, minor, lifetime; 147 gss_name_t gname; 148 gss_buffer_desc gbuffer; 149 gss_cred_usage_t usage; 150 const char *usage_text; 151 char buf[1024]; 152 153 gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL); 154 if (gret != GSS_S_COMPLETE) { 155 gss_log(3, "failed gss_inquire_cred: %s", 156 gss_error_tostring(gret, minor, buf, sizeof(buf))); 157 return; 158 } 159 160 gret = gss_display_name(&minor, gname, &gbuffer, NULL); 161 if (gret != GSS_S_COMPLETE) 162 gss_log(3, "failed gss_display_name: %s", 163 gss_error_tostring(gret, minor, buf, sizeof(buf))); 164 else { 165 switch (usage) { 166 case GSS_C_BOTH: 167 usage_text = "GSS_C_BOTH"; 168 break; 169 case GSS_C_INITIATE: 170 usage_text = "GSS_C_INITIATE"; 171 break; 172 case GSS_C_ACCEPT: 173 usage_text = "GSS_C_ACCEPT"; 174 break; 175 default: 176 usage_text = "???"; 177 } 178 gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value, 179 usage_text, (unsigned long)lifetime); 180 } 181 182 if (gret == GSS_S_COMPLETE) { 183 if (gbuffer.length != 0U) { 184 gret = gss_release_buffer(&minor, &gbuffer); 185 if (gret != GSS_S_COMPLETE) 186 gss_log(3, "failed gss_release_buffer: %s", 187 gss_error_tostring(gret, minor, buf, 188 sizeof(buf))); 189 } 190 } 191 192 gret = gss_release_name(&minor, &gname); 193 if (gret != GSS_S_COMPLETE) 194 gss_log(3, "failed gss_release_name: %s", 195 gss_error_tostring(gret, minor, buf, sizeof(buf))); 196} 197#endif 198 199#ifdef GSSAPI 200/* 201 * check for the most common configuration errors. 202 * 203 * The errors checked for are: 204 * - tkey-gssapi-credential doesn't start with DNS/ 205 * - the default realm in /etc/krb5.conf and the 206 * tkey-gssapi-credential bind config option don't match 207 * 208 * Note that if tkey-gssapi-keytab is set then these configure checks 209 * are not performed, and runtime errors from gssapi are used instead 210 */ 211static void 212check_config(const char *gss_name) { 213 const char *p; 214 krb5_context krb5_ctx; 215 char *krb5_realm = NULL; 216 217 if (strncasecmp(gss_name, "DNS/", 4) != 0) { 218 gss_log(ISC_LOG_ERROR, "tkey-gssapi-credential (%s) " 219 "should start with 'DNS/'", gss_name); 220 return; 221 } 222 223 if (krb5_init_context(&krb5_ctx) != 0) { 224 gss_log(ISC_LOG_ERROR, "Unable to initialise krb5 context"); 225 return; 226 } 227 if (krb5_get_default_realm(krb5_ctx, &krb5_realm) != 0) { 228 gss_log(ISC_LOG_ERROR, "Unable to get krb5 default realm"); 229 krb5_free_context(krb5_ctx); 230 return; 231 } 232 p = strchr(gss_name, '/'); 233 if (p == NULL) { 234 gss_log(ISC_LOG_ERROR, "badly formatted " 235 "tkey-gssapi-credentials (%s)", gss_name); 236 krb5_free_context(krb5_ctx); 237 return; 238 } 239 if (strcasecmp(p + 1, krb5_realm) != 0) { 240 gss_log(ISC_LOG_ERROR, "default realm from krb5.conf (%s) " 241 "does not match tkey-gssapi-credential (%s)", 242 krb5_realm, gss_name); 243 krb5_free_context(krb5_ctx); 244 return; 245 } 246 krb5_free_context(krb5_ctx); 247} 248#endif 249 250isc_result_t 251dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, 252 gss_cred_id_t *cred) 253{ 254#ifdef GSSAPI 255 isc_buffer_t namebuf; 256 gss_name_t gname; 257 gss_buffer_desc gnamebuf; 258 unsigned char array[DNS_NAME_MAXTEXT + 1]; 259 OM_uint32 gret, minor; 260 gss_OID_set mechs; 261 OM_uint32 lifetime; 262 gss_cred_usage_t usage; 263 char buf[1024]; 264 265 REQUIRE(cred != NULL && *cred == NULL); 266 267 /* 268 * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE 269 * here when we're in the acceptor role, which would let us 270 * default the hostname and use a compiled in default service 271 * name of "DNS", giving one less thing to configure in 272 * named.conf. Unfortunately, this creates a circular 273 * dependency due to DNS-based realm lookup in at least one 274 * GSSAPI implementation (Heimdal). Oh well. 275 */ 276 if (name != NULL) { 277 isc_buffer_init(&namebuf, array, sizeof(array)); 278 name_to_gbuffer(name, &namebuf, &gnamebuf); 279 gret = gss_import_name(&minor, &gnamebuf, 280 GSS_C_NO_OID, &gname); 281 if (gret != GSS_S_COMPLETE) { 282 check_config((char *)array); 283 284 gss_log(3, "failed gss_import_name: %s", 285 gss_error_tostring(gret, minor, buf, 286 sizeof(buf))); 287 return (ISC_R_FAILURE); 288 } 289 } else 290 gname = NULL; 291 292 /* Get the credentials. */ 293 if (gname != NULL) 294 gss_log(3, "acquiring credentials for %s", 295 (char *)gnamebuf.value); 296 else { 297 /* XXXDCL does this even make any sense? */ 298 gss_log(3, "acquiring credentials for ?"); 299 } 300 301 if (initiate) 302 usage = GSS_C_INITIATE; 303 else 304 usage = GSS_C_ACCEPT; 305 306 gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE, 307 &mech_oid_set, 308 usage, cred, &mechs, &lifetime); 309 310 if (gret != GSS_S_COMPLETE) { 311 gss_log(3, "failed to acquire %s credentials for %s: %s", 312 initiate ? "initiate" : "accept", 313 (gname != NULL) ? (char *)gnamebuf.value : "?", 314 gss_error_tostring(gret, minor, buf, sizeof(buf))); 315 check_config((char *)array); 316 return (ISC_R_FAILURE); 317 } 318 319 gss_log(4, "acquired %s credentials for %s", 320 initiate ? "initiate" : "accept", 321 (gname != NULL) ? (char *)gnamebuf.value : "?"); 322 323 log_cred(*cred); 324 325 return (ISC_R_SUCCESS); 326#else 327 REQUIRE(cred != NULL && *cred == NULL); 328 329 UNUSED(name); 330 UNUSED(initiate); 331 UNUSED(cred); 332 333 return (ISC_R_NOTIMPLEMENTED); 334#endif 335} 336 337isc_boolean_t 338dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name, 339 dns_name_t *realm) 340{ 341#ifdef GSSAPI 342 char sbuf[DNS_NAME_FORMATSIZE]; 343 char nbuf[DNS_NAME_FORMATSIZE]; 344 char rbuf[DNS_NAME_FORMATSIZE]; 345 char *sname; 346 char *rname; 347 isc_buffer_t buffer; 348 isc_result_t result; 349 350 /* 351 * It is far, far easier to write the names we are looking at into 352 * a string, and do string operations on them. 353 */ 354 isc_buffer_init(&buffer, sbuf, sizeof(sbuf)); 355 result = dns_name_toprincipal(signer, &buffer); 356 RUNTIME_CHECK(result == ISC_R_SUCCESS); 357 isc_buffer_putuint8(&buffer, 0); 358 if (name != NULL) 359 dns_name_format(name, nbuf, sizeof(nbuf)); 360 dns_name_format(realm, rbuf, sizeof(rbuf)); 361 362 /* 363 * Find the realm portion. This is the part after the @. If it 364 * does not exist, we don't have something we like, so we fail our 365 * compare. 366 */ 367 rname = strchr(sbuf, '@'); 368 if (rname == NULL) 369 return (isc_boolean_false); 370 *rname = '\0'; 371 rname++; 372 373 /* 374 * Find the host portion of the signer's name. We do this by 375 * searching for the first / character. We then check to make 376 * certain the instance name is "host" 377 * 378 * This will work for 379 * host/example.com@EXAMPLE.COM 380 */ 381 sname = strchr(sbuf, '/'); 382 if (sname == NULL) 383 return (isc_boolean_false); 384 *sname = '\0'; 385 sname++; 386 if (strcmp(sbuf, "host") != 0) 387 return (isc_boolean_false); 388 389 /* 390 * Now, we do a simple comparison between the name and the realm. 391 */ 392 if (name != NULL) { 393 if ((strcasecmp(sname, nbuf) == 0) 394 && (strcmp(rname, rbuf) == 0)) 395 return (isc_boolean_true); 396 } else { 397 if (strcmp(rname, rbuf) == 0) 398 return (isc_boolean_true); 399 } 400 401 return (isc_boolean_false); 402#else 403 UNUSED(signer); 404 UNUSED(name); 405 UNUSED(realm); 406 return (isc_boolean_false); 407#endif 408} 409 410isc_boolean_t 411dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name, 412 dns_name_t *realm) 413{ 414#ifdef GSSAPI 415 char sbuf[DNS_NAME_FORMATSIZE]; 416 char nbuf[DNS_NAME_FORMATSIZE]; 417 char rbuf[DNS_NAME_FORMATSIZE]; 418 char *sname; 419 char *nname; 420 char *rname; 421 isc_buffer_t buffer; 422 isc_result_t result; 423 424 /* 425 * It is far, far easier to write the names we are looking at into 426 * a string, and do string operations on them. 427 */ 428 isc_buffer_init(&buffer, sbuf, sizeof(sbuf)); 429 result = dns_name_toprincipal(signer, &buffer); 430 RUNTIME_CHECK(result == ISC_R_SUCCESS); 431 isc_buffer_putuint8(&buffer, 0); 432 if (name != NULL) 433 dns_name_format(name, nbuf, sizeof(nbuf)); 434 dns_name_format(realm, rbuf, sizeof(rbuf)); 435 436 /* 437 * Find the realm portion. This is the part after the @. If it 438 * does not exist, we don't have something we like, so we fail our 439 * compare. 440 */ 441 rname = strchr(sbuf, '@'); 442 if (rname == NULL) 443 return (isc_boolean_false); 444 sname = strchr(sbuf, '$'); 445 if (sname == NULL) 446 return (isc_boolean_false); 447 448 /* 449 * Verify that the $ and @ follow one another. 450 */ 451 if (rname - sname != 1) 452 return (isc_boolean_false); 453 454 /* 455 * Find the host portion of the signer's name. Zero out the $ so 456 * it terminates the signer's name, and skip past the @ for 457 * the realm. 458 * 459 * All service principals in Microsoft format seem to be in 460 * machinename$@EXAMPLE.COM 461 * format. 462 */ 463 rname++; 464 *sname = '\0'; 465 sname = sbuf; 466 467 /* 468 * Find the first . in the target name, and make it the end of 469 * the string. The rest of the name has to match the realm. 470 */ 471 if (name != NULL) { 472 nname = strchr(nbuf, '.'); 473 if (nname == NULL) 474 return (isc_boolean_false); 475 *nname++ = '\0'; 476 } 477 478 /* 479 * Now, we do a simple comparison between the name and the realm. 480 */ 481 if (name != NULL) { 482 if ((strcasecmp(sname, nbuf) == 0) 483 && (strcmp(rname, rbuf) == 0) 484 && (strcasecmp(nname, rbuf) == 0)) 485 return (isc_boolean_true); 486 } else { 487 if (strcmp(rname, rbuf) == 0) 488 return (isc_boolean_true); 489 } 490 491 492 return (isc_boolean_false); 493#else 494 UNUSED(signer); 495 UNUSED(name); 496 UNUSED(realm); 497 return (isc_boolean_false); 498#endif 499} 500 501isc_result_t 502dst_gssapi_releasecred(gss_cred_id_t *cred) { 503#ifdef GSSAPI 504 OM_uint32 gret, minor; 505 char buf[1024]; 506 507 REQUIRE(cred != NULL && *cred != NULL); 508 509 gret = gss_release_cred(&minor, cred); 510 if (gret != GSS_S_COMPLETE) { 511 /* Log the error, but still free the credential's memory */ 512 gss_log(3, "failed releasing credential: %s", 513 gss_error_tostring(gret, minor, buf, sizeof(buf))); 514 } 515 *cred = NULL; 516 517 return(ISC_R_SUCCESS); 518#else 519 UNUSED(cred); 520 521 return (ISC_R_NOTIMPLEMENTED); 522#endif 523} 524 525#ifdef GSSAPI 526/* 527 * Format a gssapi error message info into a char ** on the given memory 528 * context. This is used to return gssapi error messages back up the 529 * call chain for reporting to the user. 530 */ 531static void 532gss_err_message(isc_mem_t *mctx, isc_uint32_t major, isc_uint32_t minor, 533 char **err_message) 534{ 535 char buf[1024]; 536 char *estr; 537 538 if (err_message == NULL || mctx == NULL) { 539 /* the caller doesn't want any error messages */ 540 return; 541 } 542 543 estr = gss_error_tostring(major, minor, buf, sizeof(buf)); 544 if (estr) 545 (*err_message) = isc_mem_strdup(mctx, estr); 546} 547#endif 548 549isc_result_t 550dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, 551 isc_buffer_t *outtoken, gss_ctx_id_t *gssctx, 552 isc_mem_t *mctx, char **err_message) 553{ 554#ifdef GSSAPI 555 isc_region_t r; 556 isc_buffer_t namebuf; 557 gss_name_t gname; 558 OM_uint32 gret, minor, ret_flags, flags; 559 gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER; 560 isc_result_t result; 561 gss_buffer_desc gnamebuf; 562 unsigned char array[DNS_NAME_MAXTEXT + 1]; 563 564 /* Client must pass us a valid gss_ctx_id_t here */ 565 REQUIRE(gssctx != NULL); 566 REQUIRE(mctx != NULL); 567 568 isc_buffer_init(&namebuf, array, sizeof(array)); 569 name_to_gbuffer(name, &namebuf, &gnamebuf); 570 571 /* Get the name as a GSS name */ 572 gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname); 573 if (gret != GSS_S_COMPLETE) { 574 gss_err_message(mctx, gret, minor, err_message); 575 result = ISC_R_FAILURE; 576 goto out; 577 } 578 579 if (intoken != NULL) { 580 /* Don't call gss_release_buffer for gintoken! */ 581 REGION_TO_GBUFFER(*intoken, gintoken); 582 gintokenp = &gintoken; 583 } else { 584 gintokenp = NULL; 585 } 586 587 /* 588 * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS 589 * servers don't like it. 590 */ 591 flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG; 592 593 gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx, 594 gname, GSS_SPNEGO_MECHANISM, flags, 595 0, NULL, gintokenp, 596 NULL, &gouttoken, &ret_flags, NULL); 597 598 if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) { 599 gss_err_message(mctx, gret, minor, err_message); 600 gss_log(3, "Failure initiating security context: %s", 601 *err_message); 602 result = ISC_R_FAILURE; 603 goto out; 604 } 605 606 /* 607 * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags 608 * MUTUAL and INTEG flags, fail if either not set. 609 */ 610 611 /* 612 * RFC 2744 states the a valid output token has a non-zero length. 613 */ 614 if (gouttoken.length != 0U) { 615 GBUFFER_TO_REGION(gouttoken, r); 616 RETERR(isc_buffer_copyregion(outtoken, &r)); 617 (void)gss_release_buffer(&minor, &gouttoken); 618 } 619 (void)gss_release_name(&minor, &gname); 620 621 if (gret == GSS_S_COMPLETE) 622 result = ISC_R_SUCCESS; 623 else 624 result = DNS_R_CONTINUE; 625 626 out: 627 return (result); 628#else 629 UNUSED(name); 630 UNUSED(intoken); 631 UNUSED(outtoken); 632 UNUSED(gssctx); 633 UNUSED(mctx); 634 UNUSED(err_message); 635 636 return (ISC_R_NOTIMPLEMENTED); 637#endif 638} 639 640isc_result_t 641dst_gssapi_acceptctx(gss_cred_id_t cred, 642 const char *gssapi_keytab, 643 isc_region_t *intoken, isc_buffer_t **outtoken, 644 gss_ctx_id_t *ctxout, dns_name_t *principal, 645 isc_mem_t *mctx) 646{ 647#ifdef GSSAPI 648 isc_region_t r; 649 isc_buffer_t namebuf; 650 gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken, 651 gouttoken = GSS_C_EMPTY_BUFFER; 652 OM_uint32 gret, minor; 653 gss_ctx_id_t context = GSS_C_NO_CONTEXT; 654 gss_name_t gname = NULL; 655 isc_result_t result; 656 char buf[1024]; 657 658 REQUIRE(outtoken != NULL && *outtoken == NULL); 659 660 REGION_TO_GBUFFER(*intoken, gintoken); 661 662 if (*ctxout == NULL) 663 context = GSS_C_NO_CONTEXT; 664 else 665 context = *ctxout; 666 667 if (gssapi_keytab != NULL) { 668#ifdef ISC_PLATFORM_GSSAPI_KRB5_HEADER 669 gret = gsskrb5_register_acceptor_identity(gssapi_keytab); 670 if (gret != GSS_S_COMPLETE) { 671 gss_log(3, "failed " 672 "gsskrb5_register_acceptor_identity(%s): %s", 673 gssapi_keytab, 674 gss_error_tostring(gret, 0, buf, sizeof(buf))); 675 return (DNS_R_INVALIDTKEY); 676 } 677#else 678 /* 679 * Minimize memory leakage by only setting KRB5_KTNAME 680 * if it needs to change. 681 */ 682 const char *old = getenv("KRB5_KTNAME"); 683 if (old == NULL || strcmp(old, gssapi_keytab) != 0) { 684 char *kt = malloc(strlen(gssapi_keytab) + 13); 685 if (kt == NULL) 686 return (ISC_R_NOMEMORY); 687 sprintf(kt, "KRB5_KTNAME=%s", gssapi_keytab); 688 if (putenv(kt) != 0) 689 return (ISC_R_NOMEMORY); 690 } 691#endif 692 } 693 694 log_cred(cred); 695 696 gret = gss_accept_sec_context(&minor, &context, cred, &gintoken, 697 GSS_C_NO_CHANNEL_BINDINGS, &gname, 698 NULL, &gouttoken, NULL, NULL, NULL); 699 700 result = ISC_R_FAILURE; 701 702 switch (gret) { 703 case GSS_S_COMPLETE: 704 result = ISC_R_SUCCESS; 705 break; 706 case GSS_S_CONTINUE_NEEDED: 707 result = DNS_R_CONTINUE; 708 break; 709 case GSS_S_DEFECTIVE_TOKEN: 710 case GSS_S_DEFECTIVE_CREDENTIAL: 711 case GSS_S_BAD_SIG: 712 case GSS_S_DUPLICATE_TOKEN: 713 case GSS_S_OLD_TOKEN: 714 case GSS_S_NO_CRED: 715 case GSS_S_CREDENTIALS_EXPIRED: 716 case GSS_S_BAD_BINDINGS: 717 case GSS_S_NO_CONTEXT: 718 case GSS_S_BAD_MECH: 719 case GSS_S_FAILURE: 720 result = DNS_R_INVALIDTKEY; 721 /* fall through */ 722 default: 723 gss_log(3, "failed gss_accept_sec_context: %s", 724 gss_error_tostring(gret, minor, buf, sizeof(buf))); 725 return (result); 726 } 727 728 if (gouttoken.length > 0U) { 729 RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length)); 730 GBUFFER_TO_REGION(gouttoken, r); 731 RETERR(isc_buffer_copyregion(*outtoken, &r)); 732 (void)gss_release_buffer(&minor, &gouttoken); 733 } 734 735 if (gret == GSS_S_COMPLETE) { 736 gret = gss_display_name(&minor, gname, &gnamebuf, NULL); 737 if (gret != GSS_S_COMPLETE) { 738 gss_log(3, "failed gss_display_name: %s", 739 gss_error_tostring(gret, minor, 740 buf, sizeof(buf))); 741 RETERR(ISC_R_FAILURE); 742 } 743 744 /* 745 * Compensate for a bug in Solaris8's implementation 746 * of gss_display_name(). Should be harmless in any 747 * case, since principal names really should not 748 * contain null characters. 749 */ 750 if (gnamebuf.length > 0U && 751 ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0') 752 gnamebuf.length--; 753 754 gss_log(3, "gss-api source name (accept) is %.*s", 755 (int)gnamebuf.length, (char *)gnamebuf.value); 756 757 GBUFFER_TO_REGION(gnamebuf, r); 758 isc_buffer_init(&namebuf, r.base, r.length); 759 isc_buffer_add(&namebuf, r.length); 760 761 RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname, 762 0, NULL)); 763 764 if (gnamebuf.length != 0U) { 765 gret = gss_release_buffer(&minor, &gnamebuf); 766 if (gret != GSS_S_COMPLETE) 767 gss_log(3, "failed gss_release_buffer: %s", 768 gss_error_tostring(gret, minor, buf, 769 sizeof(buf))); 770 } 771 } 772 773 *ctxout = context; 774 775 out: 776 if (gname != NULL) { 777 gret = gss_release_name(&minor, &gname); 778 if (gret != GSS_S_COMPLETE) 779 gss_log(3, "failed gss_release_name: %s", 780 gss_error_tostring(gret, minor, buf, 781 sizeof(buf))); 782 } 783 784 return (result); 785#else 786 UNUSED(cred); 787 UNUSED(gssapi_keytab); 788 UNUSED(intoken); 789 UNUSED(outtoken); 790 UNUSED(ctxout); 791 UNUSED(principal); 792 UNUSED(mctx); 793 794 return (ISC_R_NOTIMPLEMENTED); 795#endif 796} 797 798isc_result_t 799dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx) 800{ 801#ifdef GSSAPI 802 OM_uint32 gret, minor; 803 char buf[1024]; 804 805 UNUSED(mctx); 806 807 REQUIRE(gssctx != NULL && *gssctx != NULL); 808 809 /* Delete the context from the GSS provider */ 810 gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER); 811 if (gret != GSS_S_COMPLETE) { 812 /* Log the error, but still free the context's memory */ 813 gss_log(3, "Failure deleting security context %s", 814 gss_error_tostring(gret, minor, buf, sizeof(buf))); 815 } 816 return(ISC_R_SUCCESS); 817#else 818 UNUSED(mctx); 819 UNUSED(gssctx); 820 return (ISC_R_NOTIMPLEMENTED); 821#endif 822} 823 824char * 825gss_error_tostring(isc_uint32_t major, isc_uint32_t minor, 826 char *buf, size_t buflen) { 827#ifdef GSSAPI 828 gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER, 829 msg_major = GSS_C_EMPTY_BUFFER; 830 OM_uint32 msg_ctx, minor_stat; 831 832 /* Handle major status */ 833 msg_ctx = 0; 834 (void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE, 835 GSS_C_NULL_OID, &msg_ctx, &msg_major); 836 837 /* Handle minor status */ 838 msg_ctx = 0; 839 (void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE, 840 GSS_C_NULL_OID, &msg_ctx, &msg_minor); 841 842 snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.", 843 (char *)msg_major.value, (char *)msg_minor.value); 844 845 if (msg_major.length != 0U) 846 (void)gss_release_buffer(&minor_stat, &msg_major); 847 if (msg_minor.length != 0U) 848 (void)gss_release_buffer(&minor_stat, &msg_minor); 849 return(buf); 850#else 851 snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.", 852 major, minor); 853 854 return (buf); 855#endif 856} 857 858void 859gss_log(int level, const char *fmt, ...) { 860 va_list ap; 861 862 va_start(ap, fmt); 863 isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, 864 DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap); 865 va_end(ap); 866} 867 868/*! \file */ 869