1258343Sdes/* $OpenBSD: gss-genr.c,v 1.22 2013/11/08 00:39:15 djm Exp $ */ 2124208Sdes 3124208Sdes/* 4181111Sdes * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. 5124208Sdes * 6124208Sdes * Redistribution and use in source and binary forms, with or without 7124208Sdes * modification, are permitted provided that the following conditions 8124208Sdes * are met: 9124208Sdes * 1. Redistributions of source code must retain the above copyright 10124208Sdes * notice, this list of conditions and the following disclaimer. 11124208Sdes * 2. Redistributions in binary form must reproduce the above copyright 12124208Sdes * notice, this list of conditions and the following disclaimer in the 13124208Sdes * documentation and/or other materials provided with the distribution. 14124208Sdes * 15124208Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR 16124208Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17124208Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18124208Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19124208Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20124208Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21124208Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22124208Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23124208Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24124208Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25124208Sdes */ 26124208Sdes 27124208Sdes#include "includes.h" 28124208Sdes 29124208Sdes#ifdef GSSAPI 30124208Sdes 31162852Sdes#include <sys/types.h> 32162852Sdes#include <sys/param.h> 33162852Sdes 34162852Sdes#include <stdarg.h> 35162852Sdes#include <string.h> 36162852Sdes#include <unistd.h> 37162852Sdes 38124208Sdes#include "xmalloc.h" 39162852Sdes#include "buffer.h" 40124208Sdes#include "log.h" 41126274Sdes#include "ssh2.h" 42124208Sdes 43124208Sdes#include "ssh-gss.h" 44124208Sdes 45126274Sdesextern u_char *session_id2; 46126274Sdesextern u_int session_id2_len; 47124208Sdes 48124208Sdes/* Check that the OID in a data stream matches that in the context */ 49124208Sdesint 50124208Sdesssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 51124208Sdes{ 52124208Sdes return (ctx != NULL && ctx->oid != GSS_C_NO_OID && 53124208Sdes ctx->oid->length == len && 54124208Sdes memcmp(ctx->oid->elements, data, len) == 0); 55124208Sdes} 56124208Sdes 57124208Sdes/* Set the contexts OID from a data stream */ 58124208Sdesvoid 59124208Sdesssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) 60124208Sdes{ 61124208Sdes if (ctx->oid != GSS_C_NO_OID) { 62255767Sdes free(ctx->oid->elements); 63255767Sdes free(ctx->oid); 64124208Sdes } 65258343Sdes ctx->oid = xcalloc(1, sizeof(gss_OID_desc)); 66124208Sdes ctx->oid->length = len; 67124208Sdes ctx->oid->elements = xmalloc(len); 68124208Sdes memcpy(ctx->oid->elements, data, len); 69124208Sdes} 70124208Sdes 71124208Sdes/* Set the contexts OID */ 72124208Sdesvoid 73124208Sdesssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) 74124208Sdes{ 75124208Sdes ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length); 76124208Sdes} 77124208Sdes 78124208Sdes/* All this effort to report an error ... */ 79124208Sdesvoid 80124208Sdesssh_gssapi_error(Gssctxt *ctxt) 81124208Sdes{ 82162852Sdes char *s; 83162852Sdes 84162852Sdes s = ssh_gssapi_last_error(ctxt, NULL, NULL); 85162852Sdes debug("%s", s); 86255767Sdes free(s); 87124208Sdes} 88124208Sdes 89124208Sdeschar * 90149749Sdesssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, 91149749Sdes OM_uint32 *minor_status) 92124208Sdes{ 93124208Sdes OM_uint32 lmin; 94124208Sdes gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; 95124208Sdes OM_uint32 ctx; 96124208Sdes Buffer b; 97124208Sdes char *ret; 98124208Sdes 99124208Sdes buffer_init(&b); 100124208Sdes 101124208Sdes if (major_status != NULL) 102124208Sdes *major_status = ctxt->major; 103124208Sdes if (minor_status != NULL) 104124208Sdes *minor_status = ctxt->minor; 105124208Sdes 106124208Sdes ctx = 0; 107124208Sdes /* The GSSAPI error */ 108124208Sdes do { 109124208Sdes gss_display_status(&lmin, ctxt->major, 110181111Sdes GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg); 111124208Sdes 112124208Sdes buffer_append(&b, msg.value, msg.length); 113124208Sdes buffer_put_char(&b, '\n'); 114124208Sdes 115124208Sdes gss_release_buffer(&lmin, &msg); 116124208Sdes } while (ctx != 0); 117124208Sdes 118124208Sdes /* The mechanism specific error */ 119124208Sdes do { 120124208Sdes gss_display_status(&lmin, ctxt->minor, 121181111Sdes GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg); 122124208Sdes 123124208Sdes buffer_append(&b, msg.value, msg.length); 124124208Sdes buffer_put_char(&b, '\n'); 125124208Sdes 126124208Sdes gss_release_buffer(&lmin, &msg); 127124208Sdes } while (ctx != 0); 128124208Sdes 129124208Sdes buffer_put_char(&b, '\0'); 130124208Sdes ret = xmalloc(buffer_len(&b)); 131124208Sdes buffer_get(&b, ret, buffer_len(&b)); 132124208Sdes buffer_free(&b); 133124208Sdes return (ret); 134124208Sdes} 135124208Sdes 136124208Sdes/* 137124208Sdes * Initialise our GSSAPI context. We use this opaque structure to contain all 138124208Sdes * of the data which both the client and server need to persist across 139124208Sdes * {accept,init}_sec_context calls, so that when we do it from the userauth 140124208Sdes * stuff life is a little easier 141124208Sdes */ 142124208Sdesvoid 143124208Sdesssh_gssapi_build_ctx(Gssctxt **ctx) 144124208Sdes{ 145162852Sdes *ctx = xcalloc(1, sizeof (Gssctxt)); 146124208Sdes (*ctx)->context = GSS_C_NO_CONTEXT; 147124208Sdes (*ctx)->name = GSS_C_NO_NAME; 148124208Sdes (*ctx)->oid = GSS_C_NO_OID; 149124208Sdes (*ctx)->creds = GSS_C_NO_CREDENTIAL; 150124208Sdes (*ctx)->client = GSS_C_NO_NAME; 151124208Sdes (*ctx)->client_creds = GSS_C_NO_CREDENTIAL; 152124208Sdes} 153124208Sdes 154124208Sdes/* Delete our context, providing it has been built correctly */ 155124208Sdesvoid 156124208Sdesssh_gssapi_delete_ctx(Gssctxt **ctx) 157124208Sdes{ 158124208Sdes OM_uint32 ms; 159124208Sdes 160124208Sdes if ((*ctx) == NULL) 161124208Sdes return; 162124208Sdes if ((*ctx)->context != GSS_C_NO_CONTEXT) 163124208Sdes gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER); 164124208Sdes if ((*ctx)->name != GSS_C_NO_NAME) 165124208Sdes gss_release_name(&ms, &(*ctx)->name); 166124208Sdes if ((*ctx)->oid != GSS_C_NO_OID) { 167255767Sdes free((*ctx)->oid->elements); 168255767Sdes free((*ctx)->oid); 169124208Sdes (*ctx)->oid = GSS_C_NO_OID; 170124208Sdes } 171124208Sdes if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) 172124208Sdes gss_release_cred(&ms, &(*ctx)->creds); 173124208Sdes if ((*ctx)->client != GSS_C_NO_NAME) 174124208Sdes gss_release_name(&ms, &(*ctx)->client); 175124208Sdes if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL) 176124208Sdes gss_release_cred(&ms, &(*ctx)->client_creds); 177124208Sdes 178255767Sdes free(*ctx); 179124208Sdes *ctx = NULL; 180124208Sdes} 181124208Sdes 182124208Sdes/* 183124208Sdes * Wrapper to init_sec_context 184124208Sdes * Requires that the context contains: 185124208Sdes * oid 186124208Sdes * server name (from ssh_gssapi_import_name) 187124208Sdes */ 188124208SdesOM_uint32 189124208Sdesssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, 190124208Sdes gss_buffer_desc* send_tok, OM_uint32 *flags) 191124208Sdes{ 192124208Sdes int deleg_flag = 0; 193124208Sdes 194124208Sdes if (deleg_creds) { 195124208Sdes deleg_flag = GSS_C_DELEG_FLAG; 196124208Sdes debug("Delegating credentials"); 197124208Sdes } 198124208Sdes 199124208Sdes ctx->major = gss_init_sec_context(&ctx->minor, 200124208Sdes GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 201124208Sdes GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 202124208Sdes 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 203124208Sdes 204124208Sdes if (GSS_ERROR(ctx->major)) 205124208Sdes ssh_gssapi_error(ctx); 206124208Sdes 207124208Sdes return (ctx->major); 208124208Sdes} 209124208Sdes 210124208Sdes/* Create a service name for the given host */ 211124208SdesOM_uint32 212124208Sdesssh_gssapi_import_name(Gssctxt *ctx, const char *host) 213124208Sdes{ 214124208Sdes gss_buffer_desc gssbuf; 215162852Sdes char *val; 216124208Sdes 217162852Sdes xasprintf(&val, "host@%s", host); 218162852Sdes gssbuf.value = val; 219162852Sdes gssbuf.length = strlen(gssbuf.value); 220124208Sdes 221124208Sdes if ((ctx->major = gss_import_name(&ctx->minor, 222124208Sdes &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name))) 223124208Sdes ssh_gssapi_error(ctx); 224124208Sdes 225255767Sdes free(gssbuf.value); 226124208Sdes return (ctx->major); 227124208Sdes} 228124208Sdes 229124208SdesOM_uint32 230126274Sdesssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 231126274Sdes{ 232126274Sdes if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 233126274Sdes GSS_C_QOP_DEFAULT, buffer, hash))) 234126274Sdes ssh_gssapi_error(ctx); 235126274Sdes 236126274Sdes return (ctx->major); 237126274Sdes} 238126274Sdes 239126274Sdesvoid 240126274Sdesssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, 241126274Sdes const char *context) 242126274Sdes{ 243126274Sdes buffer_init(b); 244126274Sdes buffer_put_string(b, session_id2, session_id2_len); 245126274Sdes buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST); 246126274Sdes buffer_put_cstring(b, user); 247126274Sdes buffer_put_cstring(b, service); 248126274Sdes buffer_put_cstring(b, context); 249126274Sdes} 250126274Sdes 251162852Sdesint 252162852Sdesssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 253162852Sdes{ 254162852Sdes gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 255162852Sdes OM_uint32 major, minor; 256162852Sdes gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 257162852Sdes 258162852Sdes /* RFC 4462 says we MUST NOT do SPNEGO */ 259162852Sdes if (oid->length == spnego_oid.length && 260162852Sdes (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) 261162852Sdes return 0; /* false */ 262162852Sdes 263162852Sdes ssh_gssapi_build_ctx(ctx); 264162852Sdes ssh_gssapi_set_oid(*ctx, oid); 265162852Sdes major = ssh_gssapi_import_name(*ctx, host); 266162852Sdes if (!GSS_ERROR(major)) { 267162852Sdes major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 268162852Sdes NULL); 269162852Sdes gss_release_buffer(&minor, &token); 270162852Sdes if ((*ctx)->context != GSS_C_NO_CONTEXT) 271162852Sdes gss_delete_sec_context(&minor, &(*ctx)->context, 272162852Sdes GSS_C_NO_BUFFER); 273162852Sdes } 274162852Sdes 275162852Sdes if (GSS_ERROR(major)) 276162852Sdes ssh_gssapi_delete_ctx(ctx); 277162852Sdes 278162852Sdes return (!GSS_ERROR(major)); 279162852Sdes} 280162852Sdes 281124208Sdes#endif /* GSSAPI */ 282