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 */