gssapictx.c revision 193149
1/* 2 * Copyright (C) 2004-2008 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.12 2008/04/03 06:09:04 tbox Exp $ */ 19 20#include <config.h> 21 22#include <stdlib.h> 23#include <string.h> 24 25#include <isc/buffer.h> 26#include <isc/dir.h> 27#include <isc/entropy.h> 28#include <isc/lex.h> 29#include <isc/mem.h> 30#include <isc/once.h> 31#include <isc/print.h> 32#include <isc/random.h> 33#include <isc/string.h> 34#include <isc/time.h> 35#include <isc/util.h> 36 37#include <dns/fixedname.h> 38#include <dns/name.h> 39#include <dns/rdata.h> 40#include <dns/rdataclass.h> 41#include <dns/result.h> 42#include <dns/types.h> 43#include <dns/keyvalues.h> 44#include <dns/log.h> 45 46#include <dst/gssapi.h> 47#include <dst/result.h> 48 49#include "dst_internal.h" 50 51/* 52 * If we're using our own SPNEGO implementation (see configure.in), 53 * pull it in now. Otherwise, we just use whatever GSSAPI supplies. 54 */ 55#if defined(GSSAPI) && defined(USE_ISC_SPNEGO) 56#include "spnego.h" 57#define gss_accept_sec_context gss_accept_sec_context_spnego 58#define gss_init_sec_context gss_init_sec_context_spnego 59#endif 60 61/* 62 * Solaris8 apparently needs an explicit OID set, and Solaris10 needs 63 * one for anything but Kerberos. Supplying an explicit OID set 64 * doesn't appear to hurt anything in other implementations, so we 65 * always use one. If we're not using our own SPNEGO implementation, 66 * we include SPNEGO's OID. 67 */ 68#if defined(GSSAPI) 69 70static unsigned char krb5_mech_oid_bytes[] = { 71 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 72}; 73 74#ifndef USE_ISC_SPNEGO 75static unsigned char spnego_mech_oid_bytes[] = { 76 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02 77}; 78#endif 79 80static gss_OID_desc mech_oid_set_array[] = { 81 { sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes }, 82#ifndef USE_ISC_SPNEGO 83 { sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes }, 84#endif 85}; 86 87static gss_OID_set_desc mech_oid_set = { 88 sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array), 89 mech_oid_set_array 90}; 91 92#endif 93 94#define REGION_TO_GBUFFER(r, gb) \ 95 do { \ 96 (gb).length = (r).length; \ 97 (gb).value = (r).base; \ 98 } while (0) 99 100#define GBUFFER_TO_REGION(gb, r) \ 101 do { \ 102 (r).length = (gb).length; \ 103 (r).base = (gb).value; \ 104 } while (0) 105 106 107#define RETERR(x) do { \ 108 result = (x); \ 109 if (result != ISC_R_SUCCESS) \ 110 goto out; \ 111 } while (0) 112 113#ifdef GSSAPI 114static inline void 115name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer, 116 gss_buffer_desc *gbuffer) 117{ 118 dns_name_t tname, *namep; 119 isc_region_t r; 120 isc_result_t result; 121 122 if (!dns_name_isabsolute(name)) 123 namep = name; 124 else 125 { 126 unsigned int labels; 127 dns_name_init(&tname, NULL); 128 labels = dns_name_countlabels(name); 129 dns_name_getlabelsequence(name, 0, labels - 1, &tname); 130 namep = &tname; 131 } 132 133 result = dns_name_totext(namep, ISC_FALSE, buffer); 134 isc_buffer_putuint8(buffer, 0); 135 isc_buffer_usedregion(buffer, &r); 136 REGION_TO_GBUFFER(r, *gbuffer); 137} 138 139static void 140log_cred(const gss_cred_id_t cred) { 141 OM_uint32 gret, minor, lifetime; 142 gss_name_t gname; 143 gss_buffer_desc gbuffer; 144 gss_cred_usage_t usage; 145 const char *usage_text; 146 char buf[1024]; 147 148 gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL); 149 if (gret != GSS_S_COMPLETE) { 150 gss_log(3, "failed gss_inquire_cred: %s", 151 gss_error_tostring(gret, minor, buf, sizeof(buf))); 152 return; 153 } 154 155 gret = gss_display_name(&minor, gname, &gbuffer, NULL); 156 if (gret != GSS_S_COMPLETE) 157 gss_log(3, "failed gss_display_name: %s", 158 gss_error_tostring(gret, minor, buf, sizeof(buf))); 159 else { 160 switch (usage) { 161 case GSS_C_BOTH: 162 usage_text = "GSS_C_BOTH"; 163 break; 164 case GSS_C_INITIATE: 165 usage_text = "GSS_C_INITIATE"; 166 break; 167 case GSS_C_ACCEPT: 168 usage_text = "GSS_C_ACCEPT"; 169 break; 170 default: 171 usage_text = "???"; 172 } 173 gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value, 174 usage_text, (unsigned long)lifetime); 175 } 176 177 if (gret == GSS_S_COMPLETE) { 178 if (gbuffer.length != 0) { 179 gret = gss_release_buffer(&minor, &gbuffer); 180 if (gret != GSS_S_COMPLETE) 181 gss_log(3, "failed gss_release_buffer: %s", 182 gss_error_tostring(gret, minor, buf, 183 sizeof(buf))); 184 } 185 } 186 187 gret = gss_release_name(&minor, &gname); 188 if (gret != GSS_S_COMPLETE) 189 gss_log(3, "failed gss_release_name: %s", 190 gss_error_tostring(gret, minor, buf, sizeof(buf))); 191} 192#endif 193 194isc_result_t 195dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, 196 gss_cred_id_t *cred) 197{ 198#ifdef GSSAPI 199 isc_buffer_t namebuf; 200 gss_name_t gname; 201 gss_buffer_desc gnamebuf; 202 unsigned char array[DNS_NAME_MAXTEXT + 1]; 203 OM_uint32 gret, minor; 204 gss_OID_set mechs; 205 OM_uint32 lifetime; 206 gss_cred_usage_t usage; 207 char buf[1024]; 208 209 REQUIRE(cred != NULL && *cred == NULL); 210 211 /* 212 * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE 213 * here when we're in the acceptor role, which would let us 214 * default the hostname and use a compiled in default service 215 * name of "DNS", giving one less thing to configure in 216 * named.conf. Unfortunately, this creates a circular 217 * dependency due to DNS-based realm lookup in at least one 218 * GSSAPI implementation (Heimdal). Oh well. 219 */ 220 if (name != NULL) { 221 isc_buffer_init(&namebuf, array, sizeof(array)); 222 name_to_gbuffer(name, &namebuf, &gnamebuf); 223 gret = gss_import_name(&minor, &gnamebuf, 224 GSS_C_NO_OID, &gname); 225 if (gret != GSS_S_COMPLETE) { 226 gss_log(3, "failed gss_import_name: %s", 227 gss_error_tostring(gret, minor, buf, 228 sizeof(buf))); 229 return (ISC_R_FAILURE); 230 } 231 } else 232 gname = NULL; 233 234 /* Get the credentials. */ 235 if (gname != NULL) 236 gss_log(3, "acquiring credentials for %s", 237 (char *)gnamebuf.value); 238 else { 239 /* XXXDCL does this even make any sense? */ 240 gss_log(3, "acquiring credentials for ?"); 241 } 242 243 if (initiate) 244 usage = GSS_C_INITIATE; 245 else 246 usage = GSS_C_ACCEPT; 247 248 gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE, 249 &mech_oid_set, 250 usage, cred, &mechs, &lifetime); 251 252 if (gret != GSS_S_COMPLETE) { 253 gss_log(3, "failed to acquire %s credentials for %s: %s", 254 initiate ? "initiate" : "accept", 255 (char *)gnamebuf.value, 256 gss_error_tostring(gret, minor, buf, sizeof(buf))); 257 return (ISC_R_FAILURE); 258 } 259 260 gss_log(4, "acquired %s credentials for %s", 261 initiate ? "initiate" : "accept", 262 (char *)gnamebuf.value); 263 264 log_cred(*cred); 265 266 return (ISC_R_SUCCESS); 267#else 268 UNUSED(name); 269 UNUSED(initiate); 270 UNUSED(cred); 271 272 return (ISC_R_NOTIMPLEMENTED); 273#endif 274} 275 276isc_boolean_t 277dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name, 278 dns_name_t *realm) 279{ 280#ifdef GSSAPI 281 char sbuf[DNS_NAME_FORMATSIZE]; 282 char nbuf[DNS_NAME_FORMATSIZE]; 283 char rbuf[DNS_NAME_FORMATSIZE]; 284 char *sname; 285 char *rname; 286 287 /* 288 * It is far, far easier to write the names we are looking at into 289 * a string, and do string operations on them. 290 */ 291 dns_name_format(signer, sbuf, sizeof(sbuf)); 292 if (name != NULL) 293 dns_name_format(name, nbuf, sizeof(nbuf)); 294 dns_name_format(realm, rbuf, sizeof(rbuf)); 295 296 /* 297 * Find the realm portion. This is the part after the @. If it 298 * does not exist, we don't have something we like, so we fail our 299 * compare. 300 */ 301 rname = strstr(sbuf, "\\@"); 302 if (rname == NULL) 303 return (isc_boolean_false); 304 *rname = '\0'; 305 rname += 2; 306 307 /* 308 * Find the host portion of the signer's name. We do this by 309 * searching for the first / character. We then check to make 310 * certain the instance name is "host" 311 * 312 * This will work for 313 * host/example.com@EXAMPLE.COM 314 */ 315 sname = strchr(sbuf, '/'); 316 if (sname == NULL) 317 return (isc_boolean_false); 318 *sname = '\0'; 319 sname++; 320 if (strcmp(sbuf, "host") != 0) 321 return (isc_boolean_false); 322 323 /* 324 * Now, we do a simple comparison between the name and the realm. 325 */ 326 if (name != NULL) { 327 if ((strcasecmp(sname, nbuf) == 0) 328 && (strcmp(rname, rbuf) == 0)) 329 return (isc_boolean_true); 330 } else { 331 if (strcmp(rname, rbuf) == 0) 332 return (isc_boolean_true); 333 } 334 335 return (isc_boolean_false); 336#else 337 UNUSED(signer); 338 UNUSED(name); 339 UNUSED(realm); 340 return (isc_boolean_false); 341#endif 342} 343 344isc_boolean_t 345dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name, 346 dns_name_t *realm) 347{ 348#ifdef GSSAPI 349 char sbuf[DNS_NAME_FORMATSIZE]; 350 char nbuf[DNS_NAME_FORMATSIZE]; 351 char rbuf[DNS_NAME_FORMATSIZE]; 352 char *sname; 353 char *nname; 354 char *rname; 355 356 /* 357 * It is far, far easier to write the names we are looking at into 358 * a string, and do string operations on them. 359 */ 360 dns_name_format(signer, sbuf, sizeof(sbuf)); 361 if (name != NULL) 362 dns_name_format(name, nbuf, sizeof(nbuf)); 363 dns_name_format(realm, rbuf, sizeof(rbuf)); 364 365 /* 366 * Find the realm portion. This is the part after the @. If it 367 * does not exist, we don't have something we like, so we fail our 368 * compare. 369 */ 370 rname = strstr(sbuf, "\\@"); 371 if (rname == NULL) 372 return (isc_boolean_false); 373 sname = strstr(sbuf, "\\$"); 374 if (sname == NULL) 375 return (isc_boolean_false); 376 377 /* 378 * Verify that the $ and @ follow one another. 379 */ 380 if (rname - sname != 2) 381 return (isc_boolean_false); 382 383 /* 384 * Find the host portion of the signer's name. Zero out the $ so 385 * it terminates the signer's name, and skip past the @ for 386 * the realm. 387 * 388 * All service principals in Microsoft format seem to be in 389 * machinename$@EXAMPLE.COM 390 * format. 391 */ 392 *rname = '\0'; 393 rname += 2; 394 *sname = '\0'; 395 sname = sbuf; 396 397 /* 398 * Find the first . in the target name, and make it the end of 399 * the string. The rest of the name has to match the realm. 400 */ 401 if (name != NULL) { 402 nname = strchr(nbuf, '.'); 403 if (nname == NULL) 404 return (isc_boolean_false); 405 *nname++ = '\0'; 406 } 407 408 /* 409 * Now, we do a simple comparison between the name and the realm. 410 */ 411 if (name != NULL) { 412 if ((strcasecmp(sname, nbuf) == 0) 413 && (strcmp(rname, rbuf) == 0) 414 && (strcasecmp(nname, rbuf) == 0)) 415 return (isc_boolean_true); 416 } else { 417 if (strcmp(rname, rbuf) == 0) 418 return (isc_boolean_true); 419 } 420 421 422 return (isc_boolean_false); 423#else 424 UNUSED(signer); 425 UNUSED(name); 426 UNUSED(realm); 427 return (isc_boolean_false); 428#endif 429} 430 431isc_result_t 432dst_gssapi_releasecred(gss_cred_id_t *cred) { 433#ifdef GSSAPI 434 OM_uint32 gret, minor; 435 char buf[1024]; 436 437 REQUIRE(cred != NULL && *cred != NULL); 438 439 gret = gss_release_cred(&minor, cred); 440 if (gret != GSS_S_COMPLETE) { 441 /* Log the error, but still free the credential's memory */ 442 gss_log(3, "failed releasing credential: %s", 443 gss_error_tostring(gret, minor, buf, sizeof(buf))); 444 } 445 *cred = NULL; 446 447 return(ISC_R_SUCCESS); 448#else 449 UNUSED(cred); 450 451 return (ISC_R_NOTIMPLEMENTED); 452#endif 453} 454 455isc_result_t 456dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, 457 isc_buffer_t *outtoken, gss_ctx_id_t *gssctx) 458{ 459#ifdef GSSAPI 460 isc_region_t r; 461 isc_buffer_t namebuf; 462 gss_name_t gname; 463 OM_uint32 gret, minor, ret_flags, flags; 464 gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER; 465 isc_result_t result; 466 gss_buffer_desc gnamebuf; 467 unsigned char array[DNS_NAME_MAXTEXT + 1]; 468 char buf[1024]; 469 470 /* Client must pass us a valid gss_ctx_id_t here */ 471 REQUIRE(gssctx != NULL); 472 473 isc_buffer_init(&namebuf, array, sizeof(array)); 474 name_to_gbuffer(name, &namebuf, &gnamebuf); 475 476 /* Get the name as a GSS name */ 477 gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname); 478 if (gret != GSS_S_COMPLETE) { 479 result = ISC_R_FAILURE; 480 goto out; 481 } 482 483 if (intoken != NULL) { 484 /* Don't call gss_release_buffer for gintoken! */ 485 REGION_TO_GBUFFER(*intoken, gintoken); 486 gintokenp = &gintoken; 487 } else { 488 gintokenp = NULL; 489 } 490 491 flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | 492 GSS_C_SEQUENCE_FLAG | GSS_C_INTEG_FLAG; 493 494 gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx, 495 gname, GSS_SPNEGO_MECHANISM, flags, 496 0, NULL, gintokenp, 497 NULL, &gouttoken, &ret_flags, NULL); 498 499 if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) { 500 gss_log(3, "Failure initiating security context"); 501 gss_log(3, "%s", gss_error_tostring(gret, minor, 502 buf, sizeof(buf))); 503 result = ISC_R_FAILURE; 504 goto out; 505 } 506 507 /* 508 * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags 509 * MUTUAL and INTEG flags, fail if either not set. 510 */ 511 512 /* 513 * RFC 2744 states the a valid output token has a non-zero length. 514 */ 515 if (gouttoken.length != 0) { 516 GBUFFER_TO_REGION(gouttoken, r); 517 RETERR(isc_buffer_copyregion(outtoken, &r)); 518 (void)gss_release_buffer(&minor, &gouttoken); 519 } 520 (void)gss_release_name(&minor, &gname); 521 522 if (gret == GSS_S_COMPLETE) 523 result = ISC_R_SUCCESS; 524 else 525 result = DNS_R_CONTINUE; 526 527 out: 528 return (result); 529#else 530 UNUSED(name); 531 UNUSED(intoken); 532 UNUSED(outtoken); 533 UNUSED(gssctx); 534 535 return (ISC_R_NOTIMPLEMENTED); 536#endif 537} 538 539isc_result_t 540dst_gssapi_acceptctx(gss_cred_id_t cred, 541 isc_region_t *intoken, isc_buffer_t **outtoken, 542 gss_ctx_id_t *ctxout, dns_name_t *principal, 543 isc_mem_t *mctx) 544{ 545#ifdef GSSAPI 546 isc_region_t r; 547 isc_buffer_t namebuf; 548 gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken, 549 gouttoken = GSS_C_EMPTY_BUFFER; 550 OM_uint32 gret, minor; 551 gss_ctx_id_t context = GSS_C_NO_CONTEXT; 552 gss_name_t gname = NULL; 553 isc_result_t result; 554 char buf[1024]; 555 556 REQUIRE(outtoken != NULL && *outtoken == NULL); 557 558 log_cred(cred); 559 560 REGION_TO_GBUFFER(*intoken, gintoken); 561 562 if (*ctxout == NULL) 563 context = GSS_C_NO_CONTEXT; 564 else 565 context = *ctxout; 566 567 gret = gss_accept_sec_context(&minor, &context, cred, &gintoken, 568 GSS_C_NO_CHANNEL_BINDINGS, &gname, 569 NULL, &gouttoken, NULL, NULL, NULL); 570 571 result = ISC_R_FAILURE; 572 573 switch (gret) { 574 case GSS_S_COMPLETE: 575 result = ISC_R_SUCCESS; 576 break; 577 case GSS_S_CONTINUE_NEEDED: 578 result = DNS_R_CONTINUE; 579 break; 580 case GSS_S_DEFECTIVE_TOKEN: 581 case GSS_S_DEFECTIVE_CREDENTIAL: 582 case GSS_S_BAD_SIG: 583 case GSS_S_DUPLICATE_TOKEN: 584 case GSS_S_OLD_TOKEN: 585 case GSS_S_NO_CRED: 586 case GSS_S_CREDENTIALS_EXPIRED: 587 case GSS_S_BAD_BINDINGS: 588 case GSS_S_NO_CONTEXT: 589 case GSS_S_BAD_MECH: 590 case GSS_S_FAILURE: 591 result = DNS_R_INVALIDTKEY; 592 /* fall through */ 593 default: 594 gss_log(3, "failed gss_accept_sec_context: %s", 595 gss_error_tostring(gret, minor, buf, sizeof(buf))); 596 return (result); 597 } 598 599 if (gouttoken.length > 0) { 600 RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length)); 601 GBUFFER_TO_REGION(gouttoken, r); 602 RETERR(isc_buffer_copyregion(*outtoken, &r)); 603 (void)gss_release_buffer(&minor, &gouttoken); 604 } 605 606 if (gret == GSS_S_COMPLETE) { 607 gret = gss_display_name(&minor, gname, &gnamebuf, NULL); 608 if (gret != GSS_S_COMPLETE) { 609 gss_log(3, "failed gss_display_name: %s", 610 gss_error_tostring(gret, minor, 611 buf, sizeof(buf))); 612 RETERR(ISC_R_FAILURE); 613 } 614 615 /* 616 * Compensate for a bug in Solaris8's implementation 617 * of gss_display_name(). Should be harmless in any 618 * case, since principal names really should not 619 * contain null characters. 620 */ 621 if (gnamebuf.length > 0 && 622 ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0') 623 gnamebuf.length--; 624 625 gss_log(3, "gss-api source name (accept) is %.*s", 626 (int)gnamebuf.length, (char *)gnamebuf.value); 627 628 GBUFFER_TO_REGION(gnamebuf, r); 629 isc_buffer_init(&namebuf, r.base, r.length); 630 isc_buffer_add(&namebuf, r.length); 631 632 RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname, 633 ISC_FALSE, NULL)); 634 635 if (gnamebuf.length != 0) { 636 gret = gss_release_buffer(&minor, &gnamebuf); 637 if (gret != GSS_S_COMPLETE) 638 gss_log(3, "failed gss_release_buffer: %s", 639 gss_error_tostring(gret, minor, buf, 640 sizeof(buf))); 641 } 642 } 643 644 *ctxout = context; 645 646 out: 647 if (gname != NULL) { 648 gret = gss_release_name(&minor, &gname); 649 if (gret != GSS_S_COMPLETE) 650 gss_log(3, "failed gss_release_name: %s", 651 gss_error_tostring(gret, minor, buf, 652 sizeof(buf))); 653 } 654 655 return (result); 656#else 657 UNUSED(cred); 658 UNUSED(intoken); 659 UNUSED(outtoken); 660 UNUSED(ctxout); 661 UNUSED(principal); 662 UNUSED(mctx); 663 664 return (ISC_R_NOTIMPLEMENTED); 665#endif 666} 667 668isc_result_t 669dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx) 670{ 671#ifdef GSSAPI 672 OM_uint32 gret, minor; 673 char buf[1024]; 674 675 UNUSED(mctx); 676 677 REQUIRE(gssctx != NULL && *gssctx != NULL); 678 679 /* Delete the context from the GSS provider */ 680 gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER); 681 if (gret != GSS_S_COMPLETE) { 682 /* Log the error, but still free the context's memory */ 683 gss_log(3, "Failure deleting security context %s", 684 gss_error_tostring(gret, minor, buf, sizeof(buf))); 685 } 686 return(ISC_R_SUCCESS); 687#else 688 UNUSED(mctx); 689 UNUSED(gssctx); 690 return (ISC_R_NOTIMPLEMENTED); 691#endif 692} 693 694char * 695gss_error_tostring(isc_uint32_t major, isc_uint32_t minor, 696 char *buf, size_t buflen) { 697#ifdef GSSAPI 698 gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER, 699 msg_major = GSS_C_EMPTY_BUFFER; 700 OM_uint32 msg_ctx, minor_stat; 701 702 /* Handle major status */ 703 msg_ctx = 0; 704 (void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE, 705 GSS_C_NULL_OID, &msg_ctx, &msg_major); 706 707 /* Handle minor status */ 708 msg_ctx = 0; 709 (void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE, 710 GSS_C_NULL_OID, &msg_ctx, &msg_minor); 711 712 snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.", 713 (char *)msg_major.value, (char *)msg_minor.value); 714 715 if (msg_major.length != 0) 716 (void)gss_release_buffer(&minor_stat, &msg_major); 717 if (msg_minor.length != 0) 718 (void)gss_release_buffer(&minor_stat, &msg_minor); 719 return(buf); 720#else 721 snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.", 722 major, minor); 723 724 return (buf); 725#endif 726} 727 728void 729gss_log(int level, const char *fmt, ...) { 730 va_list ap; 731 732 va_start(ap, fmt); 733 isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, 734 DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap); 735 va_end(ap); 736} 737 738/*! \file */ 739