diff options
Diffstat (limited to 'gss-genr.c')
-rw-r--r-- | gss-genr.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/gss-genr.c b/gss-genr.c new file mode 100644 index 0000000..2cd695e --- /dev/null +++ b/gss-genr.c @@ -0,0 +1,303 @@ +/* $OpenBSD: gss-genr.c,v 1.28 2021/01/27 10:05:28 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 <limits.h> +#include <stdarg.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> + +#include "xmalloc.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "log.h" +#include "ssh2.h" + +#include "ssh-gss.h" + +/* sshbuf_get for gss_buffer_desc */ +int +ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) +{ + int r; + u_char *p; + size_t len; + + if ((r = sshbuf_get_string(b, &p, &len)) != 0) + return r; + g->value = p; + g->length = len; + return 0; +} + +/* 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; + struct sshbuf *b; + char *ret; + int r; + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + 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); + + if ((r = sshbuf_put(b, msg.value, msg.length)) != 0 || + (r = sshbuf_put_u8(b, '\n')) != 0) + fatal_fr(r, "assemble GSS_CODE"); + + 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); + + if ((r = sshbuf_put(b, msg.value, msg.length)) != 0 || + (r = sshbuf_put_u8(b, '\n')) != 0) + fatal_fr(r, "assemble MECH_CODE"); + + gss_release_buffer(&lmin, &msg); + } while (ctx != 0); + + if ((r = sshbuf_put_u8(b, '\n')) != 0) + fatal_fr(r, "assemble newline"); + ret = xstrdup((const char *)sshbuf_ptr(b)); + sshbuf_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(struct sshbuf *b, const char *user, const char *service, + const char *context, const struct sshbuf *session_id) +{ + int r; + + sshbuf_reset(b); + if ((r = sshbuf_put_stringb(b, session_id)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshbuf_put_cstring(b, user)) != 0 || + (r = sshbuf_put_cstring(b, service)) != 0 || + (r = sshbuf_put_cstring(b, context)) != 0) + fatal_fr(r, "assemble buildmic"); +} + +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 */ |