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