1295367Sdes/* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt 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 34295367Sdes#include <limits.h> 35162852Sdes#include <stdarg.h> 36162852Sdes#include <string.h> 37295367Sdes#include <signal.h> 38162852Sdes#include <unistd.h> 39162852Sdes 40124208Sdes#include "xmalloc.h" 41162852Sdes#include "buffer.h" 42124208Sdes#include "log.h" 43126274Sdes#include "ssh2.h" 44124208Sdes 45124208Sdes#include "ssh-gss.h" 46124208Sdes 47126274Sdesextern u_char *session_id2; 48126274Sdesextern u_int session_id2_len; 49124208Sdes 50124208Sdes/* Check that the OID in a data stream matches that in the context */ 51124208Sdesint 52124208Sdesssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 53124208Sdes{ 54124208Sdes return (ctx != NULL && ctx->oid != GSS_C_NO_OID && 55124208Sdes ctx->oid->length == len && 56124208Sdes memcmp(ctx->oid->elements, data, len) == 0); 57124208Sdes} 58124208Sdes 59124208Sdes/* Set the contexts OID from a data stream */ 60124208Sdesvoid 61124208Sdesssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) 62124208Sdes{ 63124208Sdes if (ctx->oid != GSS_C_NO_OID) { 64255767Sdes free(ctx->oid->elements); 65255767Sdes free(ctx->oid); 66124208Sdes } 67258343Sdes ctx->oid = xcalloc(1, sizeof(gss_OID_desc)); 68124208Sdes ctx->oid->length = len; 69124208Sdes ctx->oid->elements = xmalloc(len); 70124208Sdes memcpy(ctx->oid->elements, data, len); 71124208Sdes} 72124208Sdes 73124208Sdes/* Set the contexts OID */ 74124208Sdesvoid 75124208Sdesssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) 76124208Sdes{ 77124208Sdes ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length); 78124208Sdes} 79124208Sdes 80124208Sdes/* All this effort to report an error ... */ 81124208Sdesvoid 82124208Sdesssh_gssapi_error(Gssctxt *ctxt) 83124208Sdes{ 84162852Sdes char *s; 85162852Sdes 86162852Sdes s = ssh_gssapi_last_error(ctxt, NULL, NULL); 87162852Sdes debug("%s", s); 88255767Sdes free(s); 89124208Sdes} 90124208Sdes 91124208Sdeschar * 92149749Sdesssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, 93149749Sdes OM_uint32 *minor_status) 94124208Sdes{ 95124208Sdes OM_uint32 lmin; 96124208Sdes gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; 97124208Sdes OM_uint32 ctx; 98124208Sdes Buffer b; 99124208Sdes char *ret; 100124208Sdes 101124208Sdes buffer_init(&b); 102124208Sdes 103124208Sdes if (major_status != NULL) 104124208Sdes *major_status = ctxt->major; 105124208Sdes if (minor_status != NULL) 106124208Sdes *minor_status = ctxt->minor; 107124208Sdes 108124208Sdes ctx = 0; 109124208Sdes /* The GSSAPI error */ 110124208Sdes do { 111124208Sdes gss_display_status(&lmin, ctxt->major, 112181111Sdes GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg); 113124208Sdes 114124208Sdes buffer_append(&b, msg.value, msg.length); 115124208Sdes buffer_put_char(&b, '\n'); 116124208Sdes 117124208Sdes gss_release_buffer(&lmin, &msg); 118124208Sdes } while (ctx != 0); 119124208Sdes 120124208Sdes /* The mechanism specific error */ 121124208Sdes do { 122124208Sdes gss_display_status(&lmin, ctxt->minor, 123181111Sdes GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg); 124124208Sdes 125124208Sdes buffer_append(&b, msg.value, msg.length); 126124208Sdes buffer_put_char(&b, '\n'); 127124208Sdes 128124208Sdes gss_release_buffer(&lmin, &msg); 129124208Sdes } while (ctx != 0); 130124208Sdes 131124208Sdes buffer_put_char(&b, '\0'); 132124208Sdes ret = xmalloc(buffer_len(&b)); 133124208Sdes buffer_get(&b, ret, buffer_len(&b)); 134124208Sdes buffer_free(&b); 135124208Sdes return (ret); 136124208Sdes} 137124208Sdes 138124208Sdes/* 139124208Sdes * Initialise our GSSAPI context. We use this opaque structure to contain all 140124208Sdes * of the data which both the client and server need to persist across 141124208Sdes * {accept,init}_sec_context calls, so that when we do it from the userauth 142124208Sdes * stuff life is a little easier 143124208Sdes */ 144124208Sdesvoid 145124208Sdesssh_gssapi_build_ctx(Gssctxt **ctx) 146124208Sdes{ 147162852Sdes *ctx = xcalloc(1, sizeof (Gssctxt)); 148124208Sdes (*ctx)->context = GSS_C_NO_CONTEXT; 149124208Sdes (*ctx)->name = GSS_C_NO_NAME; 150124208Sdes (*ctx)->oid = GSS_C_NO_OID; 151124208Sdes (*ctx)->creds = GSS_C_NO_CREDENTIAL; 152124208Sdes (*ctx)->client = GSS_C_NO_NAME; 153124208Sdes (*ctx)->client_creds = GSS_C_NO_CREDENTIAL; 154124208Sdes} 155124208Sdes 156124208Sdes/* Delete our context, providing it has been built correctly */ 157124208Sdesvoid 158124208Sdesssh_gssapi_delete_ctx(Gssctxt **ctx) 159124208Sdes{ 160124208Sdes OM_uint32 ms; 161124208Sdes 162124208Sdes if ((*ctx) == NULL) 163124208Sdes return; 164124208Sdes if ((*ctx)->context != GSS_C_NO_CONTEXT) 165124208Sdes gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER); 166124208Sdes if ((*ctx)->name != GSS_C_NO_NAME) 167124208Sdes gss_release_name(&ms, &(*ctx)->name); 168124208Sdes if ((*ctx)->oid != GSS_C_NO_OID) { 169255767Sdes free((*ctx)->oid->elements); 170255767Sdes free((*ctx)->oid); 171124208Sdes (*ctx)->oid = GSS_C_NO_OID; 172124208Sdes } 173124208Sdes if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) 174124208Sdes gss_release_cred(&ms, &(*ctx)->creds); 175124208Sdes if ((*ctx)->client != GSS_C_NO_NAME) 176124208Sdes gss_release_name(&ms, &(*ctx)->client); 177124208Sdes if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL) 178124208Sdes gss_release_cred(&ms, &(*ctx)->client_creds); 179124208Sdes 180255767Sdes free(*ctx); 181124208Sdes *ctx = NULL; 182124208Sdes} 183124208Sdes 184124208Sdes/* 185124208Sdes * Wrapper to init_sec_context 186124208Sdes * Requires that the context contains: 187124208Sdes * oid 188124208Sdes * server name (from ssh_gssapi_import_name) 189124208Sdes */ 190124208SdesOM_uint32 191124208Sdesssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, 192124208Sdes gss_buffer_desc* send_tok, OM_uint32 *flags) 193124208Sdes{ 194124208Sdes int deleg_flag = 0; 195124208Sdes 196124208Sdes if (deleg_creds) { 197124208Sdes deleg_flag = GSS_C_DELEG_FLAG; 198124208Sdes debug("Delegating credentials"); 199124208Sdes } 200124208Sdes 201124208Sdes ctx->major = gss_init_sec_context(&ctx->minor, 202124208Sdes GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 203124208Sdes GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 204124208Sdes 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 205124208Sdes 206124208Sdes if (GSS_ERROR(ctx->major)) 207124208Sdes ssh_gssapi_error(ctx); 208124208Sdes 209124208Sdes return (ctx->major); 210124208Sdes} 211124208Sdes 212124208Sdes/* Create a service name for the given host */ 213124208SdesOM_uint32 214124208Sdesssh_gssapi_import_name(Gssctxt *ctx, const char *host) 215124208Sdes{ 216124208Sdes gss_buffer_desc gssbuf; 217162852Sdes char *val; 218124208Sdes 219162852Sdes xasprintf(&val, "host@%s", host); 220162852Sdes gssbuf.value = val; 221162852Sdes gssbuf.length = strlen(gssbuf.value); 222124208Sdes 223124208Sdes if ((ctx->major = gss_import_name(&ctx->minor, 224124208Sdes &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name))) 225124208Sdes ssh_gssapi_error(ctx); 226124208Sdes 227255767Sdes free(gssbuf.value); 228124208Sdes return (ctx->major); 229124208Sdes} 230124208Sdes 231124208SdesOM_uint32 232126274Sdesssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 233126274Sdes{ 234126274Sdes if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 235126274Sdes GSS_C_QOP_DEFAULT, buffer, hash))) 236126274Sdes ssh_gssapi_error(ctx); 237126274Sdes 238126274Sdes return (ctx->major); 239126274Sdes} 240126274Sdes 241126274Sdesvoid 242126274Sdesssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, 243126274Sdes const char *context) 244126274Sdes{ 245126274Sdes buffer_init(b); 246126274Sdes buffer_put_string(b, session_id2, session_id2_len); 247126274Sdes buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST); 248126274Sdes buffer_put_cstring(b, user); 249126274Sdes buffer_put_cstring(b, service); 250126274Sdes buffer_put_cstring(b, context); 251126274Sdes} 252126274Sdes 253162852Sdesint 254162852Sdesssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 255162852Sdes{ 256162852Sdes gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 257162852Sdes OM_uint32 major, minor; 258162852Sdes gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 259162852Sdes 260162852Sdes /* RFC 4462 says we MUST NOT do SPNEGO */ 261162852Sdes if (oid->length == spnego_oid.length && 262162852Sdes (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) 263162852Sdes return 0; /* false */ 264162852Sdes 265162852Sdes ssh_gssapi_build_ctx(ctx); 266162852Sdes ssh_gssapi_set_oid(*ctx, oid); 267162852Sdes major = ssh_gssapi_import_name(*ctx, host); 268162852Sdes if (!GSS_ERROR(major)) { 269162852Sdes major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 270162852Sdes NULL); 271162852Sdes gss_release_buffer(&minor, &token); 272162852Sdes if ((*ctx)->context != GSS_C_NO_CONTEXT) 273162852Sdes gss_delete_sec_context(&minor, &(*ctx)->context, 274162852Sdes GSS_C_NO_BUFFER); 275162852Sdes } 276162852Sdes 277162852Sdes if (GSS_ERROR(major)) 278162852Sdes ssh_gssapi_delete_ctx(ctx); 279162852Sdes 280162852Sdes return (!GSS_ERROR(major)); 281162852Sdes} 282162852Sdes 283124208Sdes#endif /* GSSAPI */ 284