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