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