1 /* 2 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions 5 * are met: 6 * 1. Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * 2. Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * 12 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR 13 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 14 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 15 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 16 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 17 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 18 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 19 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 */ 23 /* 24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #include "includes.h" 29 30 #ifdef GSSAPI 31 32 #include "ssh.h" 33 #include "ssh2.h" 34 #include "xmalloc.h" 35 #include "buffer.h" 36 #include "bufaux.h" 37 #include "packet.h" 38 #include "compat.h" 39 #include <openssl/opensslconf.h> 40 #include <openssl/evp.h> 41 #include "cipher.h" 42 #include "kex.h" 43 #include "log.h" 44 #include "compat.h" 45 #include "xlist.h" 46 47 #include <netdb.h> 48 49 #include "ssh-gss.h" 50 51 #ifdef HAVE_GSS_OID_TO_MECH 52 #include <gssapi/gssapi_ext.h> 53 #endif /* HAVE_GSS_OID_TO_MECH */ 54 55 typedef struct { 56 char *encoded; 57 gss_OID oid; 58 } ssh_gss_kex_mapping; 59 60 static ssh_gss_kex_mapping **gss_enc2oid = NULL; 61 62 static void ssh_gssapi_encode_oid_for_kex(const gss_OID oid, char **enc_name); 63 static char *ssh_gssapi_make_kexalgs_list(gss_OID_set mechs, 64 const char *old_kexalgs); 65 66 /* 67 * Populate gss_enc2oid table and return list of kexnames. 68 * 69 * If called with both mechs == GSS_C_NULL_OID_SET and kexname_list == NULL 70 * then cached gss_enc2oid table is cleaned up. 71 */ 72 void 73 ssh_gssapi_mech_oids_to_kexnames(const gss_OID_set mechs, char **kexname_list) 74 { 75 ssh_gss_kex_mapping **new_gss_enc2oid, **p; 76 Buffer buf; 77 char *enc_name; 78 int i; 79 80 if (kexname_list != NULL) 81 *kexname_list = NULL; /* default to failed */ 82 83 if (mechs != GSS_C_NULL_OID_SET || kexname_list == NULL) { 84 /* Cleanup gss_enc2oid table */ 85 for (p = gss_enc2oid; p != NULL && *p != NULL; p++) { 86 if ((*p)->encoded) 87 xfree((*p)->encoded); 88 ssh_gssapi_release_oid(&(*p)->oid); 89 xfree(*p); 90 } 91 if (gss_enc2oid) 92 xfree(gss_enc2oid); 93 } 94 95 if (mechs == GSS_C_NULL_OID_SET && kexname_list == NULL) 96 return; /* nothing left to do */ 97 98 if (mechs) { 99 gss_OID mech; 100 /* Populate gss_enc2oid table */ 101 new_gss_enc2oid = xmalloc(sizeof (ssh_gss_kex_mapping *) * 102 (mechs->count + 1)); 103 memset(new_gss_enc2oid, 0, 104 sizeof (ssh_gss_kex_mapping *) * (mechs->count + 1)); 105 106 for (i = 0; i < mechs->count; i++) { 107 mech = &mechs->elements[i]; 108 ssh_gssapi_encode_oid_for_kex((const gss_OID)mech, 109 &enc_name); 110 111 if (!enc_name) 112 continue; 113 114 new_gss_enc2oid[i] = 115 xmalloc(sizeof (ssh_gss_kex_mapping)); 116 (new_gss_enc2oid[i])->encoded = enc_name; 117 (new_gss_enc2oid[i])->oid = 118 ssh_gssapi_dup_oid(&mechs->elements[i]); 119 } 120 121 /* Do this last to avoid run-ins with fatal_cleanups */ 122 gss_enc2oid = new_gss_enc2oid; 123 } 124 125 if (!kexname_list) 126 return; /* nothing left to do */ 127 128 /* Make kex name list */ 129 buffer_init(&buf); 130 for (p = gss_enc2oid; p && *p; p++) { 131 buffer_put_char(&buf, ','); 132 buffer_append(&buf, (*p)->encoded, strlen((*p)->encoded)); 133 } 134 135 if (buffer_len(&buf) == 0) { 136 buffer_free(&buf); 137 return; 138 } 139 140 buffer_consume(&buf, 1); /* consume leading ',' */ 141 buffer_put_char(&buf, '\0'); 142 143 *kexname_list = xstrdup(buffer_ptr(&buf)); 144 buffer_free(&buf); 145 } 146 147 void 148 ssh_gssapi_mech_oid_to_kexname(const gss_OID mech, char **kexname) 149 { 150 ssh_gss_kex_mapping **p; 151 152 if (mech == GSS_C_NULL_OID || !kexname) 153 return; 154 155 *kexname = NULL; /* default to not found */ 156 if (gss_enc2oid) { 157 for (p = gss_enc2oid; p && *p; p++) { 158 if (mech->length == (*p)->oid->length && 159 memcmp(mech->elements, (*p)->oid->elements, 160 mech->length) == 0) 161 *kexname = xstrdup((*p)->encoded); 162 } 163 } 164 165 if (*kexname) 166 return; /* found */ 167 168 ssh_gssapi_encode_oid_for_kex(mech, kexname); 169 } 170 171 void 172 ssh_gssapi_oid_of_kexname(const char *kexname, gss_OID *mech) 173 { 174 ssh_gss_kex_mapping **p; 175 176 if (!mech || !kexname || !*kexname) 177 return; 178 179 *mech = GSS_C_NULL_OID; /* default to not found */ 180 181 if (!gss_enc2oid) 182 return; 183 184 for (p = gss_enc2oid; p && *p; p++) { 185 if (strcmp(kexname, (*p)->encoded) == 0) { 186 *mech = (*p)->oid; 187 return; 188 } 189 } 190 } 191 192 static 193 void 194 ssh_gssapi_encode_oid_for_kex(const gss_OID oid, char **enc_name) 195 { 196 Buffer buf; 197 OM_uint32 oidlen; 198 uint_t enclen; 199 const EVP_MD *evp_md = EVP_md5(); 200 EVP_MD_CTX md; 201 uchar_t digest[EVP_MAX_MD_SIZE]; 202 char *encoded; 203 204 if (oid == GSS_C_NULL_OID || !enc_name) 205 return; 206 207 *enc_name = NULL; 208 209 oidlen = oid->length; 210 211 /* No GSS mechs have OIDs as long as 128 -- simplify DER encoding */ 212 if (oidlen > 128) 213 return; /* fail gracefully */ 214 215 /* 216 * NOTE: If we need to support SSH_BUG_GSSAPI_BER this is where 217 * we'd do it. 218 * 219 * That means using "Se3H81ismmOC3OE+FwYCiQ==" for the Kerberos 220 * V mech and "N3+k7/4wGxHyuP8Yxi4RhA==" for the GSI mech. Ick. 221 */ 222 223 buffer_init(&buf); 224 225 /* UNIVERSAL class tag for OBJECT IDENTIFIER */ 226 buffer_put_char(&buf, 0x06); 227 buffer_put_char(&buf, oidlen); /* one octet DER length -- see above */ 228 229 /* OID elements */ 230 buffer_append(&buf, oid->elements, oidlen); 231 232 /* Make digest */ 233 EVP_DigestInit(&md, evp_md); 234 EVP_DigestUpdate(&md, buffer_ptr(&buf), buffer_len(&buf)); 235 EVP_DigestFinal(&md, digest, NULL); 236 buffer_free(&buf); 237 238 /* Base 64 encoding */ 239 encoded = xmalloc(EVP_MD_size(evp_md)*2); 240 enclen = __b64_ntop(digest, EVP_MD_size(evp_md), 241 encoded, EVP_MD_size(evp_md) * 2); 242 buffer_init(&buf); 243 buffer_append(&buf, KEX_GSS_SHA1, sizeof (KEX_GSS_SHA1) - 1); 244 buffer_append(&buf, encoded, enclen); 245 buffer_put_char(&buf, '\0'); 246 247 debug2("GSS-API Mechanism encoded as %s", encoded); 248 xfree(encoded); 249 250 *enc_name = xstrdup(buffer_ptr(&buf)); 251 buffer_free(&buf); 252 } 253 254 static char * 255 ssh_gssapi_make_kexalgs_list(gss_OID_set mechs, const char *old_kexalgs) 256 { 257 char *gss_kexalgs, *new_kexalgs; 258 int len; 259 260 if (mechs == GSS_C_NULL_OID_SET) 261 return (xstrdup(old_kexalgs)); /* never null */ 262 263 ssh_gssapi_mech_oids_to_kexnames(mechs, &gss_kexalgs); 264 265 if (gss_kexalgs == NULL || *gss_kexalgs == '\0') 266 return (xstrdup(old_kexalgs)); /* never null */ 267 268 if (old_kexalgs == NULL || *old_kexalgs == '\0') 269 return (gss_kexalgs); 270 271 len = strlen(old_kexalgs) + strlen(gss_kexalgs) + 2; 272 new_kexalgs = xmalloc(len); 273 (void) snprintf(new_kexalgs, len, "%s,%s", gss_kexalgs, old_kexalgs); 274 xfree(gss_kexalgs); 275 276 return (new_kexalgs); 277 } 278 279 void 280 ssh_gssapi_modify_kex(Kex *kex, gss_OID_set mechs, char **proposal) 281 { 282 char *kexalgs, *orig_kexalgs, *p; 283 char **hostalg, *orig_hostalgs, *new_hostalgs; 284 char **hostalgs; 285 gss_OID_set dup_mechs; 286 OM_uint32 maj, min; 287 int i; 288 289 if (kex == NULL || proposal == NULL || 290 proposal[PROPOSAL_KEX_ALGS] == NULL) { 291 fatal("INTERNAL ERROR (%s)", __func__); 292 } 293 294 orig_hostalgs = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; 295 296 if (kex->mechs == GSS_C_NULL_OID_SET && mechs == GSS_C_NULL_OID_SET) 297 return; /* didn't offer GSS last time, not offering now */ 298 299 if (kex->mechs == GSS_C_NULL_OID_SET || mechs == GSS_C_NULL_OID_SET) 300 goto mod_offer; /* didn't offer last time or not offering now */ 301 302 /* Check if mechs is congruent to kex->mechs (last offered) */ 303 if (kex->mechs->count == mechs->count) { 304 int present, matches = 0; 305 306 for (i = 0; i < mechs->count; i++) { 307 maj = gss_test_oid_set_member(&min, 308 &kex->mechs->elements[i], mechs, &present); 309 310 if (GSS_ERROR(maj)) { 311 mechs = GSS_C_NULL_OID_SET; 312 break; 313 } 314 315 matches += (present) ? 1 : 0; 316 } 317 318 if (matches == kex->mechs->count) 319 return; /* no change in offer from last time */ 320 } 321 322 mod_offer: 323 /* 324 * Remove previously offered mechs from PROPOSAL_KEX_ALGS proposal 325 * 326 * ASSUMPTION: GSS-API kex algs always go in front, so removing 327 * them is a matter of skipping them. 328 */ 329 p = kexalgs = orig_kexalgs = proposal[PROPOSAL_KEX_ALGS]; 330 while (p != NULL && *p != '\0' && 331 strncmp(p, KEX_GSS_SHA1, strlen(KEX_GSS_SHA1)) == 0) { 332 333 if ((p = strchr(p, ',')) == NULL) 334 break; 335 p++; 336 kexalgs = p; 337 338 } 339 kexalgs = proposal[PROPOSAL_KEX_ALGS] = xstrdup(kexalgs); 340 xfree(orig_kexalgs); 341 342 (void) gss_release_oid_set(&min, &kex->mechs); /* ok if !kex->mechs */ 343 344 /* Not offering GSS kex algorithms now -> all done */ 345 if (mechs == GSS_C_NULL_OID_SET) 346 return; 347 348 /* Remember mechs we're offering */ 349 maj = gss_create_empty_oid_set(&min, &dup_mechs); 350 if (GSS_ERROR(maj)) 351 return; 352 for (i = 0; i < mechs->count; i++) { 353 maj = gss_add_oid_set_member(&min, &mechs->elements[i], 354 &dup_mechs); 355 356 if (GSS_ERROR(maj)) { 357 (void) gss_release_oid_set(&min, &dup_mechs); 358 return; 359 } 360 } 361 362 /* Add mechs to kex algorithms ... */ 363 proposal[PROPOSAL_KEX_ALGS] = ssh_gssapi_make_kexalgs_list(mechs, 364 kexalgs); 365 xfree(kexalgs); 366 kex->mechs = dup_mechs; /* remember what we offer now */ 367 368 /* 369 * ... and add null host key alg, if it wasn't there before, but 370 * not if we're the server and we have other host key algs to 371 * offer. 372 * 373 * NOTE: Never remove "null" host key alg once added. 374 */ 375 if (orig_hostalgs == NULL || *orig_hostalgs == '\0') { 376 proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = xstrdup("null"); 377 } else if (!kex->server) { 378 hostalgs = xsplit(orig_hostalgs, ','); 379 for (hostalg = hostalgs; *hostalg != NULL; hostalg++) { 380 if (strcmp(*hostalg, "null") == 0) { 381 xfree_split_list(hostalgs); 382 return; 383 } 384 } 385 xfree_split_list(hostalgs); 386 387 if (kex->mechs != GSS_C_NULL_OID_SET) { 388 int len; 389 390 len = strlen(orig_hostalgs) + sizeof (",null"); 391 new_hostalgs = xmalloc(len); 392 (void) snprintf(new_hostalgs, len, "%s,null", 393 orig_hostalgs); 394 proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = new_hostalgs; 395 } 396 397 xfree(orig_hostalgs); 398 } 399 } 400 401 /* 402 * Yes, we harcode OIDs for some things, for now it's all we can do. 403 * 404 * We have to reference particular mechanisms due to lack of generality 405 * in the GSS-API in several areas: authorization, mapping principal 406 * names to usernames, "storing" delegated credentials, and discovering 407 * whether a mechanism is a pseudo-mechanism that negotiates mechanisms. 408 * 409 * Even if they were in some header file or if __gss_mech_to_oid() 410 * and/or __gss_oid_to_mech() were standard we'd still have to hardcode 411 * the mechanism names, and since the mechanisms have no standard names 412 * other than their OIDs it's actually worse [less portable] to hardcode 413 * names than OIDs, so we hardcode OIDs. 414 * 415 * SPNEGO is a difficult problem though -- it MUST NOT be used in SSHv2, 416 * but that's true of all possible pseudo-mechanisms that can perform 417 * mechanism negotiation, and SPNEGO could have new OIDs in the future. 418 * Ideally we could query each mechanism for its feature set and then 419 * ignore any mechanisms that negotiate mechanisms, but, alas, there's 420 * no interface to do that. 421 * 422 * In the future, if the necessary generic GSS interfaces for the issues 423 * listed above are made available (even if they differ by platform, as 424 * we can expect authorization interfaces will), then we can stop 425 * referencing specific mechanism OIDs here. 426 */ 427 int 428 ssh_gssapi_is_spnego(gss_OID oid) 429 { 430 return (oid->length == 6 && 431 memcmp("\053\006\001\005\005\002", oid->elements, 6) == 0); 432 } 433 434 int 435 ssh_gssapi_is_krb5(gss_OID oid) 436 { 437 return (oid->length == 9 && 438 memcmp("\x2A\x86\x48\x86\xF7\x12\x01\x02\x02", 439 oid->elements, 9) == 0); 440 } 441 442 int 443 ssh_gssapi_is_dh(gss_OID oid) 444 { 445 return (oid->length == 9 && 446 memcmp("\053\006\004\001\052\002\032\002\005", 447 oid->elements, 9) == 0); 448 } 449 450 int 451 ssh_gssapi_is_gsi(gss_OID oid) 452 { 453 return (oid->length == 9 && 454 memcmp("\x2B\x06\x01\x04\x01\x9B\x50\x01\x01", 455 oid->elements, 9) == 0); 456 } 457 458 const char * 459 ssh_gssapi_oid_to_name(gss_OID oid) 460 { 461 #ifdef HAVE_GSS_OID_TO_MECH 462 return (__gss_oid_to_mech(oid)); 463 #else 464 if (ssh_gssapi_is_krb5(oid)) 465 return ("Kerberos"); 466 if (ssh_gssapi_is_gsi(oid)) 467 return ("GSI"); 468 return ("(unknown)"); 469 #endif /* HAVE_GSS_OID_TO_MECH */ 470 } 471 472 char * 473 ssh_gssapi_oid_to_str(gss_OID oid) 474 { 475 #ifdef HAVE_GSS_OID_TO_STR 476 gss_buffer_desc str_buf; 477 char *str; 478 OM_uint32 maj, min; 479 480 maj = gss_oid_to_str(&min, oid, &str_buf); 481 482 if (GSS_ERROR(maj)) 483 return (xstrdup("<gss_oid_to_str() failed>")); 484 485 str = xmalloc(str_buf.length + 1); 486 memset(str, 0, str_buf.length + 1); 487 strlcpy(str, str_buf.value, str_buf.length + 1); 488 (void) gss_release_buffer(&min, &str_buf); 489 490 return (str); 491 #else 492 return (xstrdup("<gss_oid_to_str() unsupported>")); 493 #endif /* HAVE_GSS_OID_TO_STR */ 494 } 495 496 /* Check that the OID in a data stream matches that in the context */ 497 int 498 ssh_gssapi_check_mech_oid(Gssctxt *ctx, void *data, size_t len) 499 { 500 501 return (ctx != NULL && ctx->desired_mech != GSS_C_NULL_OID && 502 ctx->desired_mech->length == len && 503 memcmp(ctx->desired_mech->elements, data, len) == 0); 504 } 505 506 /* Set the contexts OID from a data stream */ 507 void 508 ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) 509 { 510 if (ctx->actual_mech != GSS_C_NULL_OID) { 511 xfree(ctx->actual_mech->elements); 512 xfree(ctx->actual_mech); 513 } 514 ctx->actual_mech = xmalloc(sizeof (gss_OID_desc)); 515 ctx->actual_mech->length = len; 516 ctx->actual_mech->elements = xmalloc(len); 517 memcpy(ctx->actual_mech->elements, data, len); 518 } 519 520 /* Set the contexts OID */ 521 void 522 ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) 523 { 524 ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length); 525 } 526 527 /* All this effort to report an error ... */ 528 529 void 530 ssh_gssapi_error(Gssctxt *ctxt, const char *where) 531 { 532 char *errmsg = ssh_gssapi_last_error(ctxt, NULL, NULL); 533 534 if (where != NULL) 535 debug("GSS-API error while %s: %s", where, errmsg); 536 else 537 debug("GSS-API error: %s", errmsg); 538 539 /* ssh_gssapi_last_error() can't return NULL */ 540 xfree(errmsg); 541 } 542 543 char * 544 ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, 545 OM_uint32 *minor_status) 546 { 547 OM_uint32 lmin, more; 548 OM_uint32 maj, min; 549 gss_OID mech = GSS_C_NULL_OID; 550 gss_buffer_desc msg; 551 Buffer b; 552 char *ret; 553 554 buffer_init(&b); 555 556 if (ctxt) { 557 /* Get status codes from the Gssctxt */ 558 maj = ctxt->major; 559 min = ctxt->minor; 560 /* Output them if desired */ 561 if (major_status) 562 *major_status = maj; 563 if (minor_status) 564 *minor_status = min; 565 /* Get mechanism for minor status display */ 566 mech = (ctxt->actual_mech != GSS_C_NULL_OID) ? 567 ctxt->actual_mech : ctxt->desired_mech; 568 } else if (major_status && minor_status) { 569 maj = *major_status; 570 min = *major_status; 571 } else { 572 maj = GSS_S_COMPLETE; 573 min = 0; 574 } 575 576 more = 0; 577 /* The GSSAPI error */ 578 do { 579 gss_display_status(&lmin, maj, GSS_C_GSS_CODE, 580 GSS_C_NULL_OID, &more, &msg); 581 582 buffer_append(&b, msg.value, msg.length); 583 buffer_put_char(&b, '\n'); 584 gss_release_buffer(&lmin, &msg); 585 } while (more != 0); 586 587 /* The mechanism specific error */ 588 do { 589 /* 590 * If mech == GSS_C_NULL_OID we may get the default 591 * mechanism, whatever that is, and that may not be 592 * useful. 593 */ 594 gss_display_status(&lmin, min, GSS_C_MECH_CODE, mech, &more, 595 &msg); 596 597 buffer_append(&b, msg.value, msg.length); 598 buffer_put_char(&b, '\n'); 599 600 gss_release_buffer(&lmin, &msg); 601 } while (more != 0); 602 603 buffer_put_char(&b, '\0'); 604 ret = xstrdup(buffer_ptr(&b)); 605 buffer_free(&b); 606 607 return (ret); 608 } 609 610 /* 611 * Initialise our GSSAPI context. We use this opaque structure to contain all 612 * of the data which both the client and server need to persist across 613 * {accept,init}_sec_context calls, so that when we do it from the userauth 614 * stuff life is a little easier 615 */ 616 void 617 ssh_gssapi_build_ctx(Gssctxt **ctx, int client, gss_OID mech) 618 { 619 Gssctxt *newctx; 620 621 622 newctx = (Gssctxt*)xmalloc(sizeof (Gssctxt)); 623 memset(newctx, 0, sizeof (Gssctxt)); 624 625 626 newctx->local = client; 627 newctx->desired_mech = ssh_gssapi_dup_oid(mech); 628 629 /* This happens to be redundant given the memset() above */ 630 newctx->major = GSS_S_COMPLETE; 631 newctx->context = GSS_C_NO_CONTEXT; 632 newctx->actual_mech = GSS_C_NULL_OID; 633 newctx->desired_name = GSS_C_NO_NAME; 634 newctx->src_name = GSS_C_NO_NAME; 635 newctx->dst_name = GSS_C_NO_NAME; 636 newctx->creds = GSS_C_NO_CREDENTIAL; 637 newctx->deleg_creds = GSS_C_NO_CREDENTIAL; 638 639 newctx->default_creds = (*ctx != NULL) ? (*ctx)->default_creds : 0; 640 641 ssh_gssapi_delete_ctx(ctx); 642 643 *ctx = newctx; 644 } 645 646 gss_OID 647 ssh_gssapi_dup_oid(gss_OID oid) 648 { 649 gss_OID new_oid; 650 651 new_oid = xmalloc(sizeof (gss_OID_desc)); 652 653 new_oid->elements = xmalloc(oid->length); 654 new_oid->length = oid->length; 655 memcpy(new_oid->elements, oid->elements, oid->length); 656 657 return (new_oid); 658 } 659 660 gss_OID 661 ssh_gssapi_make_oid(size_t length, void *elements) 662 { 663 gss_OID_desc oid; 664 665 oid.length = length; 666 oid.elements = elements; 667 668 return (ssh_gssapi_dup_oid(&oid)); 669 } 670 671 void 672 ssh_gssapi_release_oid(gss_OID *oid) 673 { 674 OM_uint32 min; 675 676 if (oid && *oid == GSS_C_NULL_OID) 677 return; 678 (void) gss_release_oid(&min, oid); 679 680 if (*oid == GSS_C_NULL_OID) 681 return; /* libgss did own this gss_OID and released it */ 682 683 xfree((*oid)->elements); 684 xfree(*oid); 685 *oid = GSS_C_NULL_OID; 686 } 687 688 struct gss_name { 689 gss_OID name_type; 690 gss_buffer_t external_name; 691 gss_OID mech_type; 692 void *mech_name; 693 }; 694 695 /* Delete our context, providing it has been built correctly */ 696 void 697 ssh_gssapi_delete_ctx(Gssctxt **ctx) 698 { 699 OM_uint32 ms; 700 701 if ((*ctx) == NULL) 702 return; 703 704 if ((*ctx)->context != GSS_C_NO_CONTEXT) 705 gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER); 706 #if 0 707 /* XXX */ 708 if ((*ctx)->desired_mech != GSS_C_NULL_OID) 709 ssh_gssapi_release_oid(&(*ctx)->desired_mech); 710 #endif 711 if ((*ctx)->actual_mech != GSS_C_NULL_OID) 712 (void) ssh_gssapi_release_oid(&(*ctx)->actual_mech); 713 if ((*ctx)->desired_name != GSS_C_NO_NAME) 714 gss_release_name(&ms, &(*ctx)->desired_name); 715 #if 0 716 if ((*ctx)->src_name != GSS_C_NO_NAME) 717 gss_release_name(&ms, &(*ctx)->src_name); 718 #endif 719 if ((*ctx)->dst_name != GSS_C_NO_NAME) 720 gss_release_name(&ms, &(*ctx)->dst_name); 721 if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) 722 gss_release_cred(&ms, &(*ctx)->creds); 723 if ((*ctx)->deleg_creds != GSS_C_NO_CREDENTIAL) 724 gss_release_cred(&ms, &(*ctx)->deleg_creds); 725 726 xfree(*ctx); 727 *ctx = NULL; 728 } 729 730 /* Create a GSS hostbased service principal name for a given server hostname */ 731 int 732 ssh_gssapi_import_name(Gssctxt *ctx, const char *server_host) 733 { 734 gss_buffer_desc name_buf; 735 int ret; 736 737 /* Build target principal */ 738 name_buf.length = strlen(SSH_GSS_HOSTBASED_SERVICE) + 739 strlen(server_host) + 1; /* +1 for '@' */ 740 name_buf.value = xmalloc(name_buf.length + 1); /* +1 for NUL */ 741 ret = snprintf(name_buf.value, name_buf.length + 1, "%s@%s", 742 SSH_GSS_HOSTBASED_SERVICE, server_host); 743 744 debug3("%s: snprintf() returned %d, expected %d", __func__, ret, 745 name_buf.length); 746 747 ctx->major = gss_import_name(&ctx->minor, &name_buf, 748 GSS_C_NT_HOSTBASED_SERVICE, &ctx->desired_name); 749 750 if (GSS_ERROR(ctx->major)) { 751 ssh_gssapi_error(ctx, "calling GSS_Import_name()"); 752 return (0); 753 } 754 755 xfree(name_buf.value); 756 757 return (1); 758 } 759 760 OM_uint32 761 ssh_gssapi_get_mic(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) 762 { 763 764 ctx->major = gss_get_mic(&ctx->minor, ctx->context, 765 GSS_C_QOP_DEFAULT, buffer, hash); 766 if (GSS_ERROR(ctx->major)) 767 ssh_gssapi_error(ctx, "while getting MIC"); 768 return (ctx->major); 769 } 770 771 OM_uint32 772 ssh_gssapi_verify_mic(Gssctxt *ctx, gss_buffer_desc *buffer, 773 gss_buffer_desc *hash) 774 { 775 gss_qop_t qop; 776 777 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, buffer, 778 hash, &qop); 779 if (GSS_ERROR(ctx->major)) 780 ssh_gssapi_error(ctx, "while verifying MIC"); 781 return (ctx->major); 782 } 783 #endif /* GSSAPI */