gssapictx.c revision 224092
1/* 2 * Copyright (C) 2004-2011 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: gssapictx.c,v 1.26 2011-01-10 03:49:49 marka Exp $ */ 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 isc_buffer_putuint8(buffer, 0); 139 isc_buffer_usedregion(buffer, &r); 140 REGION_TO_GBUFFER(r, *gbuffer); 141} 142 143static void 144log_cred(const gss_cred_id_t cred) { 145 OM_uint32 gret, minor, lifetime; 146 gss_name_t gname; 147 gss_buffer_desc gbuffer; 148 gss_cred_usage_t usage; 149 const char *usage_text; 150 char buf[1024]; 151 152 gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL); 153 if (gret != GSS_S_COMPLETE) { 154 gss_log(3, "failed gss_inquire_cred: %s", 155 gss_error_tostring(gret, minor, buf, sizeof(buf))); 156 return; 157 } 158 159 gret = gss_display_name(&minor, gname, &gbuffer, NULL); 160 if (gret != GSS_S_COMPLETE) 161 gss_log(3, "failed gss_display_name: %s", 162 gss_error_tostring(gret, minor, buf, sizeof(buf))); 163 else { 164 switch (usage) { 165 case GSS_C_BOTH: 166 usage_text = "GSS_C_BOTH"; 167 break; 168 case GSS_C_INITIATE: 169 usage_text = "GSS_C_INITIATE"; 170 break; 171 case GSS_C_ACCEPT: 172 usage_text = "GSS_C_ACCEPT"; 173 break; 174 default: 175 usage_text = "???"; 176 } 177 gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value, 178 usage_text, (unsigned long)lifetime); 179 } 180 181 if (gret == GSS_S_COMPLETE) { 182 if (gbuffer.length != 0) { 183 gret = gss_release_buffer(&minor, &gbuffer); 184 if (gret != GSS_S_COMPLETE) 185 gss_log(3, "failed gss_release_buffer: %s", 186 gss_error_tostring(gret, minor, buf, 187 sizeof(buf))); 188 } 189 } 190 191 gret = gss_release_name(&minor, &gname); 192 if (gret != GSS_S_COMPLETE) 193 gss_log(3, "failed gss_release_name: %s", 194 gss_error_tostring(gret, minor, buf, sizeof(buf))); 195} 196#endif 197 198#ifdef GSSAPI 199/* 200 * check for the most common configuration errors. 201 * 202 * The errors checked for are: 203 * - tkey-gssapi-credential doesn't start with DNS/ 204 * - the default realm in /etc/krb5.conf and the 205 * tkey-gssapi-credential bind config option don't match 206 * 207 * Note that if tkey-gssapi-keytab is set then these configure checks 208 * are not performed, and runtime errors from gssapi are used instead 209 */ 210static void 211check_config(const char *gss_name) { 212 const char *p; 213 krb5_context krb5_ctx; 214 char *krb5_realm = NULL; 215 216 if (strncasecmp(gss_name, "DNS/", 4) != 0) { 217 gss_log(ISC_LOG_ERROR, "tkey-gssapi-credential (%s) " 218 "should start with 'DNS/'", gss_name); 219 return; 220 } 221 222 if (krb5_init_context(&krb5_ctx) != 0) { 223 gss_log(ISC_LOG_ERROR, "Unable to initialise krb5 context"); 224 return; 225 } 226 if (krb5_get_default_realm(krb5_ctx, &krb5_realm) != 0) { 227 gss_log(ISC_LOG_ERROR, "Unable to get krb5 default realm"); 228 krb5_free_context(krb5_ctx); 229 return; 230 } 231 p = strchr(gss_name, '/'); 232 if (p == NULL) { 233 gss_log(ISC_LOG_ERROR, "badly formatted " 234 "tkey-gssapi-credentials (%s)", gss_name); 235 krb5_free_context(krb5_ctx); 236 return; 237 } 238 if (strcasecmp(p + 1, krb5_realm) != 0) { 239 gss_log(ISC_LOG_ERROR, "default realm from krb5.conf (%s) " 240 "does not match tkey-gssapi-credential (%s)", 241 krb5_realm, gss_name); 242 krb5_free_context(krb5_ctx); 243 return; 244 } 245 krb5_free_context(krb5_ctx); 246} 247#endif 248 249isc_result_t 250dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, 251 gss_cred_id_t *cred) 252{ 253#ifdef GSSAPI 254 isc_buffer_t namebuf; 255 gss_name_t gname; 256 gss_buffer_desc gnamebuf; 257 unsigned char array[DNS_NAME_MAXTEXT + 1]; 258 OM_uint32 gret, minor; 259 gss_OID_set mechs; 260 OM_uint32 lifetime; 261 gss_cred_usage_t usage; 262 char buf[1024]; 263 264 REQUIRE(cred != NULL && *cred == NULL); 265 266 /* 267 * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE 268 * here when we're in the acceptor role, which would let us 269 * default the hostname and use a compiled in default service 270 * name of "DNS", giving one less thing to configure in 271 * named.conf. Unfortunately, this creates a circular 272 * dependency due to DNS-based realm lookup in at least one 273 * GSSAPI implementation (Heimdal). Oh well. 274 */ 275 if (name != NULL) { 276 isc_buffer_init(&namebuf, array, sizeof(array)); 277 name_to_gbuffer(name, &namebuf, &gnamebuf); 278 gret = gss_import_name(&minor, &gnamebuf, 279 GSS_C_NO_OID, &gname); 280 if (gret != GSS_S_COMPLETE) { 281 check_config((char *)array); 282 283 gss_log(3, "failed gss_import_name: %s", 284 gss_error_tostring(gret, minor, buf, 285 sizeof(buf))); 286 return (ISC_R_FAILURE); 287 } 288 } else 289 gname = NULL; 290 291 /* Get the credentials. */ 292 if (gname != NULL) 293 gss_log(3, "acquiring credentials for %s", 294 (char *)gnamebuf.value); 295 else { 296 /* XXXDCL does this even make any sense? */ 297 gss_log(3, "acquiring credentials for ?"); 298 } 299 300 if (initiate) 301 usage = GSS_C_INITIATE; 302 else 303 usage = GSS_C_ACCEPT; 304 305 gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE, 306 &mech_oid_set, 307 usage, cred, &mechs, &lifetime); 308 309 if (gret != GSS_S_COMPLETE) { 310 gss_log(3, "failed to acquire %s credentials for %s: %s", 311 initiate ? "initiate" : "accept", 312 (char *)gnamebuf.value, 313 gss_error_tostring(gret, minor, buf, sizeof(buf))); 314 check_config((char *)array); 315 return (ISC_R_FAILURE); 316 } 317 318 gss_log(4, "acquired %s credentials for %s", 319 initiate ? "initiate" : "accept", 320 (char *)gnamebuf.value); 321 322 log_cred(*cred); 323 324 return (ISC_R_SUCCESS); 325#else 326 UNUSED(name); 327 UNUSED(initiate); 328 UNUSED(cred); 329 330 return (ISC_R_NOTIMPLEMENTED); 331#endif 332} 333 334isc_boolean_t 335dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name, 336 dns_name_t *realm) 337{ 338#ifdef GSSAPI 339 char sbuf[DNS_NAME_FORMATSIZE]; 340 char nbuf[DNS_NAME_FORMATSIZE]; 341 char rbuf[DNS_NAME_FORMATSIZE]; 342 char *sname; 343 char *rname; 344 isc_buffer_t buffer; 345 346 /* 347 * It is far, far easier to write the names we are looking at into 348 * a string, and do string operations on them. 349 */ 350 isc_buffer_init(&buffer, sbuf, sizeof(sbuf)); 351 dns_name_toprincipal(signer, &buffer); 352 isc_buffer_putuint8(&buffer, 0); 353 if (name != NULL) 354 dns_name_format(name, nbuf, sizeof(nbuf)); 355 dns_name_format(realm, rbuf, sizeof(rbuf)); 356 357 /* 358 * Find the realm portion. This is the part after the @. If it 359 * does not exist, we don't have something we like, so we fail our 360 * compare. 361 */ 362 rname = strchr(sbuf, '@'); 363 if (rname == NULL) 364 return (isc_boolean_false); 365 *rname = '\0'; 366 rname++; 367 368 /* 369 * Find the host portion of the signer's name. We do this by 370 * searching for the first / character. We then check to make 371 * certain the instance name is "host" 372 * 373 * This will work for 374 * host/example.com@EXAMPLE.COM 375 */ 376 sname = strchr(sbuf, '/'); 377 if (sname == NULL) 378 return (isc_boolean_false); 379 *sname = '\0'; 380 sname++; 381 if (strcmp(sbuf, "host") != 0) 382 return (isc_boolean_false); 383 384 /* 385 * Now, we do a simple comparison between the name and the realm. 386 */ 387 if (name != NULL) { 388 if ((strcasecmp(sname, nbuf) == 0) 389 && (strcmp(rname, rbuf) == 0)) 390 return (isc_boolean_true); 391 } else { 392 if (strcmp(rname, rbuf) == 0) 393 return (isc_boolean_true); 394 } 395 396 return (isc_boolean_false); 397#else 398 UNUSED(signer); 399 UNUSED(name); 400 UNUSED(realm); 401 return (isc_boolean_false); 402#endif 403} 404 405isc_boolean_t 406dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name, 407 dns_name_t *realm) 408{ 409#ifdef GSSAPI 410 char sbuf[DNS_NAME_FORMATSIZE]; 411 char nbuf[DNS_NAME_FORMATSIZE]; 412 char rbuf[DNS_NAME_FORMATSIZE]; 413 char *sname; 414 char *nname; 415 char *rname; 416 isc_buffer_t buffer; 417 418 /* 419 * It is far, far easier to write the names we are looking at into 420 * a string, and do string operations on them. 421 */ 422 isc_buffer_init(&buffer, sbuf, sizeof(sbuf)); 423 dns_name_toprincipal(signer, &buffer); 424 isc_buffer_putuint8(&buffer, 0); 425 if (name != NULL) 426 dns_name_format(name, nbuf, sizeof(nbuf)); 427 dns_name_format(realm, rbuf, sizeof(rbuf)); 428 429 /* 430 * Find the realm portion. This is the part after the @. If it 431 * does not exist, we don't have something we like, so we fail our 432 * compare. 433 */ 434 rname = strchr(sbuf, '@'); 435 if (rname == NULL) 436 return (isc_boolean_false); 437 sname = strchr(sbuf, '$'); 438 if (sname == NULL) 439 return (isc_boolean_false); 440 441 /* 442 * Verify that the $ and @ follow one another. 443 */ 444 if (rname - sname != 1) 445 return (isc_boolean_false); 446 447 /* 448 * Find the host portion of the signer's name. Zero out the $ so 449 * it terminates the signer's name, and skip past the @ for 450 * the realm. 451 * 452 * All service principals in Microsoft format seem to be in 453 * machinename$@EXAMPLE.COM 454 * format. 455 */ 456 rname++; 457 *sname = '\0'; 458 sname = sbuf; 459 460 /* 461 * Find the first . in the target name, and make it the end of 462 * the string. The rest of the name has to match the realm. 463 */ 464 if (name != NULL) { 465 nname = strchr(nbuf, '.'); 466 if (nname == NULL) 467 return (isc_boolean_false); 468 *nname++ = '\0'; 469 } 470 471 /* 472 * Now, we do a simple comparison between the name and the realm. 473 */ 474 if (name != NULL) { 475 if ((strcasecmp(sname, nbuf) == 0) 476 && (strcmp(rname, rbuf) == 0) 477 && (strcasecmp(nname, rbuf) == 0)) 478 return (isc_boolean_true); 479 } else { 480 if (strcmp(rname, rbuf) == 0) 481 return (isc_boolean_true); 482 } 483 484 485 return (isc_boolean_false); 486#else 487 UNUSED(signer); 488 UNUSED(name); 489 UNUSED(realm); 490 return (isc_boolean_false); 491#endif 492} 493 494isc_result_t 495dst_gssapi_releasecred(gss_cred_id_t *cred) { 496#ifdef GSSAPI 497 OM_uint32 gret, minor; 498 char buf[1024]; 499 500 REQUIRE(cred != NULL && *cred != NULL); 501 502 gret = gss_release_cred(&minor, cred); 503 if (gret != GSS_S_COMPLETE) { 504 /* Log the error, but still free the credential's memory */ 505 gss_log(3, "failed releasing credential: %s", 506 gss_error_tostring(gret, minor, buf, sizeof(buf))); 507 } 508 *cred = NULL; 509 510 return(ISC_R_SUCCESS); 511#else 512 UNUSED(cred); 513 514 return (ISC_R_NOTIMPLEMENTED); 515#endif 516} 517 518#ifdef GSSAPI 519/* 520 * Format a gssapi error message info into a char ** on the given memory 521 * context. This is used to return gssapi error messages back up the 522 * call chain for reporting to the user. 523 */ 524static void 525gss_err_message(isc_mem_t *mctx, isc_uint32_t major, isc_uint32_t minor, 526 char **err_message) 527{ 528 char buf[1024]; 529 char *estr; 530 531 if (err_message == NULL || mctx == NULL) { 532 /* the caller doesn't want any error messages */ 533 return; 534 } 535 536 estr = gss_error_tostring(major, minor, buf, sizeof(buf)); 537 if (estr) 538 (*err_message) = isc_mem_strdup(mctx, estr); 539} 540#endif 541 542isc_result_t 543dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, 544 isc_buffer_t *outtoken, gss_ctx_id_t *gssctx, 545 isc_mem_t *mctx, char **err_message) 546{ 547#ifdef GSSAPI 548 isc_region_t r; 549 isc_buffer_t namebuf; 550 gss_name_t gname; 551 OM_uint32 gret, minor, ret_flags, flags; 552 gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER; 553 isc_result_t result; 554 gss_buffer_desc gnamebuf; 555 unsigned char array[DNS_NAME_MAXTEXT + 1]; 556 557 /* Client must pass us a valid gss_ctx_id_t here */ 558 REQUIRE(gssctx != NULL); 559 REQUIRE(mctx != NULL); 560 561 isc_buffer_init(&namebuf, array, sizeof(array)); 562 name_to_gbuffer(name, &namebuf, &gnamebuf); 563 564 /* Get the name as a GSS name */ 565 gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname); 566 if (gret != GSS_S_COMPLETE) { 567 gss_err_message(mctx, gret, minor, err_message); 568 result = ISC_R_FAILURE; 569 goto out; 570 } 571 572 if (intoken != NULL) { 573 /* Don't call gss_release_buffer for gintoken! */ 574 REGION_TO_GBUFFER(*intoken, gintoken); 575 gintokenp = &gintoken; 576 } else { 577 gintokenp = NULL; 578 } 579 580 /* 581 * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS 582 * servers don't like it. 583 */ 584 flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG; 585 586 gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx, 587 gname, GSS_SPNEGO_MECHANISM, flags, 588 0, NULL, gintokenp, 589 NULL, &gouttoken, &ret_flags, NULL); 590 591 if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) { 592 gss_err_message(mctx, gret, minor, err_message); 593 gss_log(3, "Failure initiating security context: %s", 594 *err_message); 595 result = ISC_R_FAILURE; 596 goto out; 597 } 598 599 /* 600 * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags 601 * MUTUAL and INTEG flags, fail if either not set. 602 */ 603 604 /* 605 * RFC 2744 states the a valid output token has a non-zero length. 606 */ 607 if (gouttoken.length != 0) { 608 GBUFFER_TO_REGION(gouttoken, r); 609 RETERR(isc_buffer_copyregion(outtoken, &r)); 610 (void)gss_release_buffer(&minor, &gouttoken); 611 } 612 (void)gss_release_name(&minor, &gname); 613 614 if (gret == GSS_S_COMPLETE) 615 result = ISC_R_SUCCESS; 616 else 617 result = DNS_R_CONTINUE; 618 619 out: 620 return (result); 621#else 622 UNUSED(name); 623 UNUSED(intoken); 624 UNUSED(outtoken); 625 UNUSED(gssctx); 626 UNUSED(mctx); 627 UNUSED(err_message); 628 629 return (ISC_R_NOTIMPLEMENTED); 630#endif 631} 632 633isc_result_t 634dst_gssapi_acceptctx(gss_cred_id_t cred, 635 const char *gssapi_keytab, 636 isc_region_t *intoken, isc_buffer_t **outtoken, 637 gss_ctx_id_t *ctxout, dns_name_t *principal, 638 isc_mem_t *mctx) 639{ 640#ifdef GSSAPI 641 isc_region_t r; 642 isc_buffer_t namebuf; 643 gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken, 644 gouttoken = GSS_C_EMPTY_BUFFER; 645 OM_uint32 gret, minor; 646 gss_ctx_id_t context = GSS_C_NO_CONTEXT; 647 gss_name_t gname = NULL; 648 isc_result_t result; 649 char buf[1024]; 650 651 REQUIRE(outtoken != NULL && *outtoken == NULL); 652 653 log_cred(cred); 654 655 REGION_TO_GBUFFER(*intoken, gintoken); 656 657 if (*ctxout == NULL) 658 context = GSS_C_NO_CONTEXT; 659 else 660 context = *ctxout; 661 662 if (gssapi_keytab != NULL) { 663#ifdef ISC_PLATFORM_GSSAPI_KRB5_HEADER 664 gret = gsskrb5_register_acceptor_identity(gssapi_keytab); 665 if (gret != GSS_S_COMPLETE) { 666 gss_log(3, "failed " 667 "gsskrb5_register_acceptor_identity(%s): %s", 668 gssapi_keytab, 669 gss_error_tostring(gret, minor, 670 buf, sizeof(buf))); 671 return (DNS_R_INVALIDTKEY); 672 } 673#else 674 /* 675 * Minimize memory leakage by only setting KRB5_KTNAME 676 * if it needs to change. 677 */ 678 const char *old = getenv("KRB5_KTNAME"); 679 if (old == NULL || strcmp(old, gssapi_keytab) != 0) { 680 char *kt = malloc(strlen(gssapi_keytab) + 13); 681 if (kt == NULL) 682 return (ISC_R_NOMEMORY); 683 sprintf(kt, "KRB5_KTNAME=%s", gssapi_keytab); 684 if (putenv(kt) != 0) 685 return (ISC_R_NOMEMORY); 686 } 687#endif 688 } 689 690 gret = gss_accept_sec_context(&minor, &context, cred, &gintoken, 691 GSS_C_NO_CHANNEL_BINDINGS, &gname, 692 NULL, &gouttoken, NULL, NULL, NULL); 693 694 result = ISC_R_FAILURE; 695 696 switch (gret) { 697 case GSS_S_COMPLETE: 698 result = ISC_R_SUCCESS; 699 break; 700 case GSS_S_CONTINUE_NEEDED: 701 result = DNS_R_CONTINUE; 702 break; 703 case GSS_S_DEFECTIVE_TOKEN: 704 case GSS_S_DEFECTIVE_CREDENTIAL: 705 case GSS_S_BAD_SIG: 706 case GSS_S_DUPLICATE_TOKEN: 707 case GSS_S_OLD_TOKEN: 708 case GSS_S_NO_CRED: 709 case GSS_S_CREDENTIALS_EXPIRED: 710 case GSS_S_BAD_BINDINGS: 711 case GSS_S_NO_CONTEXT: 712 case GSS_S_BAD_MECH: 713 case GSS_S_FAILURE: 714 result = DNS_R_INVALIDTKEY; 715 /* fall through */ 716 default: 717 gss_log(3, "failed gss_accept_sec_context: %s", 718 gss_error_tostring(gret, minor, buf, sizeof(buf))); 719 return (result); 720 } 721 722 if (gouttoken.length > 0) { 723 RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length)); 724 GBUFFER_TO_REGION(gouttoken, r); 725 RETERR(isc_buffer_copyregion(*outtoken, &r)); 726 (void)gss_release_buffer(&minor, &gouttoken); 727 } 728 729 if (gret == GSS_S_COMPLETE) { 730 gret = gss_display_name(&minor, gname, &gnamebuf, NULL); 731 if (gret != GSS_S_COMPLETE) { 732 gss_log(3, "failed gss_display_name: %s", 733 gss_error_tostring(gret, minor, 734 buf, sizeof(buf))); 735 RETERR(ISC_R_FAILURE); 736 } 737 738 /* 739 * Compensate for a bug in Solaris8's implementation 740 * of gss_display_name(). Should be harmless in any 741 * case, since principal names really should not 742 * contain null characters. 743 */ 744 if (gnamebuf.length > 0 && 745 ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0') 746 gnamebuf.length--; 747 748 gss_log(3, "gss-api source name (accept) is %.*s", 749 (int)gnamebuf.length, (char *)gnamebuf.value); 750 751 GBUFFER_TO_REGION(gnamebuf, r); 752 isc_buffer_init(&namebuf, r.base, r.length); 753 isc_buffer_add(&namebuf, r.length); 754 755 RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname, 756 0, NULL)); 757 758 if (gnamebuf.length != 0) { 759 gret = gss_release_buffer(&minor, &gnamebuf); 760 if (gret != GSS_S_COMPLETE) 761 gss_log(3, "failed gss_release_buffer: %s", 762 gss_error_tostring(gret, minor, buf, 763 sizeof(buf))); 764 } 765 } 766 767 *ctxout = context; 768 769 out: 770 if (gname != NULL) { 771 gret = gss_release_name(&minor, &gname); 772 if (gret != GSS_S_COMPLETE) 773 gss_log(3, "failed gss_release_name: %s", 774 gss_error_tostring(gret, minor, buf, 775 sizeof(buf))); 776 } 777 778 return (result); 779#else 780 UNUSED(cred); 781 UNUSED(gssapi_keytab); 782 UNUSED(intoken); 783 UNUSED(outtoken); 784 UNUSED(ctxout); 785 UNUSED(principal); 786 UNUSED(mctx); 787 788 return (ISC_R_NOTIMPLEMENTED); 789#endif 790} 791 792isc_result_t 793dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx) 794{ 795#ifdef GSSAPI 796 OM_uint32 gret, minor; 797 char buf[1024]; 798 799 UNUSED(mctx); 800 801 REQUIRE(gssctx != NULL && *gssctx != NULL); 802 803 /* Delete the context from the GSS provider */ 804 gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER); 805 if (gret != GSS_S_COMPLETE) { 806 /* Log the error, but still free the context's memory */ 807 gss_log(3, "Failure deleting security context %s", 808 gss_error_tostring(gret, minor, buf, sizeof(buf))); 809 } 810 return(ISC_R_SUCCESS); 811#else 812 UNUSED(mctx); 813 UNUSED(gssctx); 814 return (ISC_R_NOTIMPLEMENTED); 815#endif 816} 817 818char * 819gss_error_tostring(isc_uint32_t major, isc_uint32_t minor, 820 char *buf, size_t buflen) { 821#ifdef GSSAPI 822 gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER, 823 msg_major = GSS_C_EMPTY_BUFFER; 824 OM_uint32 msg_ctx, minor_stat; 825 826 /* Handle major status */ 827 msg_ctx = 0; 828 (void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE, 829 GSS_C_NULL_OID, &msg_ctx, &msg_major); 830 831 /* Handle minor status */ 832 msg_ctx = 0; 833 (void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE, 834 GSS_C_NULL_OID, &msg_ctx, &msg_minor); 835 836 snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.", 837 (char *)msg_major.value, (char *)msg_minor.value); 838 839 if (msg_major.length != 0) 840 (void)gss_release_buffer(&minor_stat, &msg_major); 841 if (msg_minor.length != 0) 842 (void)gss_release_buffer(&minor_stat, &msg_minor); 843 return(buf); 844#else 845 snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.", 846 major, minor); 847 848 return (buf); 849#endif 850} 851 852void 853gss_log(int level, const char *fmt, ...) { 854 va_list ap; 855 856 va_start(ap, fmt); 857 isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, 858 DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap); 859 va_end(ap); 860} 861 862/*! \file */ 863