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