|  | /* $OpenBSD: gss-genr.c,v 1.22 2013/11/08 00:39:15 djm Exp $ */ | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR | 
|  | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
|  | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
|  | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
|  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include "includes.h" | 
|  |  | 
|  | #ifdef GSSAPI | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/param.h> | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include "xmalloc.h" | 
|  | #include "buffer.h" | 
|  | #include "log.h" | 
|  | #include "ssh2.h" | 
|  |  | 
|  | #include "ssh-gss.h" | 
|  |  | 
|  | extern u_char *session_id2; | 
|  | extern u_int session_id2_len; | 
|  |  | 
|  | /* Check that the OID in a data stream matches that in the context */ | 
|  | int | 
|  | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) | 
|  | { | 
|  | return (ctx != NULL && ctx->oid != GSS_C_NO_OID && | 
|  | ctx->oid->length == len && | 
|  | memcmp(ctx->oid->elements, data, len) == 0); | 
|  | } | 
|  |  | 
|  | /* Set the contexts OID from a data stream */ | 
|  | void | 
|  | ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) | 
|  | { | 
|  | if (ctx->oid != GSS_C_NO_OID) { | 
|  | free(ctx->oid->elements); | 
|  | free(ctx->oid); | 
|  | } | 
|  | ctx->oid = xcalloc(1, sizeof(gss_OID_desc)); | 
|  | ctx->oid->length = len; | 
|  | ctx->oid->elements = xmalloc(len); | 
|  | memcpy(ctx->oid->elements, data, len); | 
|  | } | 
|  |  | 
|  | /* Set the contexts OID */ | 
|  | void | 
|  | ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) | 
|  | { | 
|  | ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length); | 
|  | } | 
|  |  | 
|  | /* All this effort to report an error ... */ | 
|  | void | 
|  | ssh_gssapi_error(Gssctxt *ctxt) | 
|  | { | 
|  | char *s; | 
|  |  | 
|  | s = ssh_gssapi_last_error(ctxt, NULL, NULL); | 
|  | debug("%s", s); | 
|  | free(s); | 
|  | } | 
|  |  | 
|  | char * | 
|  | ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, | 
|  | OM_uint32 *minor_status) | 
|  | { | 
|  | OM_uint32 lmin; | 
|  | gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; | 
|  | OM_uint32 ctx; | 
|  | Buffer b; | 
|  | char *ret; | 
|  |  | 
|  | buffer_init(&b); | 
|  |  | 
|  | if (major_status != NULL) | 
|  | *major_status = ctxt->major; | 
|  | if (minor_status != NULL) | 
|  | *minor_status = ctxt->minor; | 
|  |  | 
|  | ctx = 0; | 
|  | /* The GSSAPI error */ | 
|  | do { | 
|  | gss_display_status(&lmin, ctxt->major, | 
|  | GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg); | 
|  |  | 
|  | buffer_append(&b, msg.value, msg.length); | 
|  | buffer_put_char(&b, '\n'); | 
|  |  | 
|  | gss_release_buffer(&lmin, &msg); | 
|  | } while (ctx != 0); | 
|  |  | 
|  | /* The mechanism specific error */ | 
|  | do { | 
|  | gss_display_status(&lmin, ctxt->minor, | 
|  | GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg); | 
|  |  | 
|  | buffer_append(&b, msg.value, msg.length); | 
|  | buffer_put_char(&b, '\n'); | 
|  |  | 
|  | gss_release_buffer(&lmin, &msg); | 
|  | } while (ctx != 0); | 
|  |  | 
|  | buffer_put_char(&b, '\0'); | 
|  | ret = xmalloc(buffer_len(&b)); | 
|  | buffer_get(&b, ret, buffer_len(&b)); | 
|  | buffer_free(&b); | 
|  | return (ret); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Initialise our GSSAPI context. We use this opaque structure to contain all | 
|  | * of the data which both the client and server need to persist across | 
|  | * {accept,init}_sec_context calls, so that when we do it from the userauth | 
|  | * stuff life is a little easier | 
|  | */ | 
|  | void | 
|  | ssh_gssapi_build_ctx(Gssctxt **ctx) | 
|  | { | 
|  | *ctx = xcalloc(1, sizeof (Gssctxt)); | 
|  | (*ctx)->context = GSS_C_NO_CONTEXT; | 
|  | (*ctx)->name = GSS_C_NO_NAME; | 
|  | (*ctx)->oid = GSS_C_NO_OID; | 
|  | (*ctx)->creds = GSS_C_NO_CREDENTIAL; | 
|  | (*ctx)->client = GSS_C_NO_NAME; | 
|  | (*ctx)->client_creds = GSS_C_NO_CREDENTIAL; | 
|  | } | 
|  |  | 
|  | /* Delete our context, providing it has been built correctly */ | 
|  | void | 
|  | ssh_gssapi_delete_ctx(Gssctxt **ctx) | 
|  | { | 
|  | OM_uint32 ms; | 
|  |  | 
|  | if ((*ctx) == NULL) | 
|  | return; | 
|  | if ((*ctx)->context != GSS_C_NO_CONTEXT) | 
|  | gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER); | 
|  | if ((*ctx)->name != GSS_C_NO_NAME) | 
|  | gss_release_name(&ms, &(*ctx)->name); | 
|  | if ((*ctx)->oid != GSS_C_NO_OID) { | 
|  | free((*ctx)->oid->elements); | 
|  | free((*ctx)->oid); | 
|  | (*ctx)->oid = GSS_C_NO_OID; | 
|  | } | 
|  | if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) | 
|  | gss_release_cred(&ms, &(*ctx)->creds); | 
|  | if ((*ctx)->client != GSS_C_NO_NAME) | 
|  | gss_release_name(&ms, &(*ctx)->client); | 
|  | if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL) | 
|  | gss_release_cred(&ms, &(*ctx)->client_creds); | 
|  |  | 
|  | free(*ctx); | 
|  | *ctx = NULL; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Wrapper to init_sec_context | 
|  | * Requires that the context contains: | 
|  | *	oid | 
|  | *	server name (from ssh_gssapi_import_name) | 
|  | */ | 
|  | OM_uint32 | 
|  | ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, | 
|  | gss_buffer_desc* send_tok, OM_uint32 *flags) | 
|  | { | 
|  | int deleg_flag = 0; | 
|  |  | 
|  | if (deleg_creds) { | 
|  | deleg_flag = GSS_C_DELEG_FLAG; | 
|  | debug("Delegating credentials"); | 
|  | } | 
|  |  | 
|  | ctx->major = gss_init_sec_context(&ctx->minor, | 
|  | GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, | 
|  | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, | 
|  | 0, NULL, recv_tok, NULL, send_tok, flags, NULL); | 
|  |  | 
|  | if (GSS_ERROR(ctx->major)) | 
|  | ssh_gssapi_error(ctx); | 
|  |  | 
|  | return (ctx->major); | 
|  | } | 
|  |  | 
|  | /* Create a service name for the given host */ | 
|  | OM_uint32 | 
|  | ssh_gssapi_import_name(Gssctxt *ctx, const char *host) | 
|  | { | 
|  | gss_buffer_desc gssbuf; | 
|  | char *val; | 
|  |  | 
|  | xasprintf(&val, "host@%s", host); | 
|  | gssbuf.value = val; | 
|  | gssbuf.length = strlen(gssbuf.value); | 
|  |  | 
|  | if ((ctx->major = gss_import_name(&ctx->minor, | 
|  | &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name))) | 
|  | ssh_gssapi_error(ctx); | 
|  |  | 
|  | free(gssbuf.value); | 
|  | return (ctx->major); | 
|  | } | 
|  |  | 
|  | OM_uint32 | 
|  | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) | 
|  | { | 
|  | if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, | 
|  | GSS_C_QOP_DEFAULT, buffer, hash))) | 
|  | ssh_gssapi_error(ctx); | 
|  |  | 
|  | return (ctx->major); | 
|  | } | 
|  |  | 
|  | void | 
|  | ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, | 
|  | const char *context) | 
|  | { | 
|  | buffer_init(b); | 
|  | buffer_put_string(b, session_id2, session_id2_len); | 
|  | buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST); | 
|  | buffer_put_cstring(b, user); | 
|  | buffer_put_cstring(b, service); | 
|  | buffer_put_cstring(b, context); | 
|  | } | 
|  |  | 
|  | int | 
|  | ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | 
|  | { | 
|  | gss_buffer_desc token = GSS_C_EMPTY_BUFFER; | 
|  | OM_uint32 major, minor; | 
|  | gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; | 
|  |  | 
|  | /* RFC 4462 says we MUST NOT do SPNEGO */ | 
|  | if (oid->length == spnego_oid.length && | 
|  | (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) | 
|  | return 0; /* false */ | 
|  |  | 
|  | ssh_gssapi_build_ctx(ctx); | 
|  | ssh_gssapi_set_oid(*ctx, oid); | 
|  | major = ssh_gssapi_import_name(*ctx, host); | 
|  | if (!GSS_ERROR(major)) { | 
|  | major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, | 
|  | NULL); | 
|  | gss_release_buffer(&minor, &token); | 
|  | if ((*ctx)->context != GSS_C_NO_CONTEXT) | 
|  | gss_delete_sec_context(&minor, &(*ctx)->context, | 
|  | GSS_C_NO_BUFFER); | 
|  | } | 
|  |  | 
|  | if (GSS_ERROR(major)) | 
|  | ssh_gssapi_delete_ctx(ctx); | 
|  |  | 
|  | return (!GSS_ERROR(major)); | 
|  | } | 
|  |  | 
|  | #endif /* GSSAPI */ |