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