b7aa600c41
I have worked hard to reduce diffs against the vendor branch. One notable change in that respect is that we no longer prefer DSA over RSA - the reasons for doing so went away years ago. This may cause some surprises, as ssh will warn about unknown host keys even for hosts whose keys haven't changed. MFC after: 6 weeks
282 lines
7.3 KiB
C
282 lines
7.3 KiB
C
/* $OpenBSD: gss-genr.c,v 1.19 2007/06/12 11:56:15 dtucker 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) {
|
|
xfree(ctx->oid->elements);
|
|
xfree(ctx->oid);
|
|
}
|
|
ctx->oid = xmalloc(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);
|
|
xfree(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) {
|
|
xfree((*ctx)->oid->elements);
|
|
xfree((*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);
|
|
|
|
xfree(*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);
|
|
|
|
xfree(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 */
|