+/* $OpenBSD: gss-serv.c,v 1.5 2003/11/17 11:06:07 markus Exp $ */
+
/*
* Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
*
#ifdef GSSAPI
-#include "ssh.h"
-#include "ssh2.h"
-#include "xmalloc.h"
#include "buffer.h"
#include "bufaux.h"
-#include "packet.h"
#include "compat.h"
#include <openssl/evp.h>
-#include "cipher.h"
#include "kex.h"
#include "auth.h"
#include "log.h"
#include "channels.h"
#include "session.h"
-#include "dispatch.h"
#include "servconf.h"
-#include "compat.h"
#include "monitor_wrap.h"
+#include "xmalloc.h"
+#include "getput.h"
#include "ssh-gss.h"
extern int session_id2_len;
static ssh_gssapi_client gssapi_client =
- { {0,NULL}, GSS_C_NO_CREDENTIAL, NULL, {NULL,NULL,NULL}};
+ { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
+ GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL}};
-ssh_gssapi_mech gssapi_null_mech
- = {NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
+ssh_gssapi_mech gssapi_null_mech =
+ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
#ifdef KRB5
extern ssh_gssapi_mech gssapi_kerberos_mech;
ssh_gssapi_mech* supported_mechs[]= {
#ifdef KRB5
- &gssapi_kerberos_mech,
- &gssapi_kerberos_mech_old, /* Support for legacy clients */
+ &gssapi_kerberos_mech,
+ &gssapi_kerberos_mech_old, /* Support for legacy clients */
#endif
#ifdef GSI
- &gssapi_gsi_mech,
- &gssapi_gsi_mech_old, /* Support for legacy clients */
+ &gssapi_gsi_mech,
+ &gssapi_gsi_mech_old, /* Support for legacy clients */
#endif
- &gssapi_null_mech,
+ &gssapi_null_mech,
};
+#ifdef GSS_C_GLOBUS_LIMITED_PROXY_FLAG
+static int limited = 0;
+#endif
+
+/* Unpriviledged */
+void
+ssh_gssapi_supported_oids(gss_OID_set *oidset)
+{
+ int i = 0;
+ OM_uint32 min_status;
+ int present;
+ gss_OID_set supported;
+
+ gss_create_empty_oid_set(&min_status, oidset);
+ /* Ask priviledged process what mechanisms it supports. */
+ PRIVSEP(gss_indicate_mechs(&min_status, &supported));
+
+ while (supported_mechs[i]->name != NULL) {
+ if (GSS_ERROR(gss_test_oid_set_member(&min_status,
+ &supported_mechs[i]->oid, supported, &present)))
+ present = 0;
+ if (present)
+ gss_add_oid_set_member(&min_status,
+ &supported_mechs[i]->oid, oidset);
+ i++;
+ }
+}
+
+
+/* Wrapper around accept_sec_context
+ * Requires that the context contains:
+ * oid
+ * credentials (from ssh_gssapi_acquire_cred)
+ */
+/* Priviledged */
+OM_uint32
+ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok,
+ gss_buffer_desc *send_tok, OM_uint32 *flags)
+{
+ OM_uint32 status;
+ gss_OID mech;
+
+ ctx->major = gss_accept_sec_context(&ctx->minor,
+ &ctx->context, ctx->creds, recv_tok,
+ GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech,
+ send_tok, flags, NULL, &ctx->client_creds);
+
+ if (GSS_ERROR(ctx->major))
+ ssh_gssapi_error(ctx);
+
+ if (ctx->client_creds)
+ debug("Received some client credentials");
+ else
+ debug("Got no client credentials");
+
+ status = ctx->major;
+
+ /* Now, if we're complete and we have the right flags, then
+ * we flag the user as also having been authenticated
+ */
+
+ if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
+ (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
+ if (ssh_gssapi_getclient(ctx, &gssapi_client))
+ fatal("Couldn't convert client name");
+#ifdef GSS_C_GLOBUS_LIMITED_PROXY_FLAG
+ if (flags && (*flags & GSS_C_GLOBUS_LIMITED_PROXY_FLAG))
+ limited=1;
+#endif
+ }
+
+ return (status);
+}
+
+/*
+ * This parses an exported name, extracting the mechanism specific portion
+ * to use for ACL checking. It verifies that the name belongs the mechanism
+ * originally selected.
+ */
+static OM_uint32
+ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
+{
+ char *tok;
+ OM_uint32 offset;
+ OM_uint32 oidl;
+
+ tok=ename->value;
+
+#ifdef GSI /* GSI gss_export_name() is broken. */
+ if ((ctx->oid->length == gssapi_gsi_mech.oid.length) &&
+ (memcmp(ctx->oid->elements, gssapi_gsi_mech.oid.elements,
+ gssapi_gsi_mech.oid.length) == 0)) {
+ name->length = ename->length;
+ name->value = xmalloc(ename->length+1);
+ memcpy(name->value, ename->value, ename->length);
+ return GSS_S_COMPLETE;
+ }
+#endif
+
+ /*
+ * Check that ename is long enough for all of the fixed length
+ * header, and that the initial ID bytes are correct
+ */
+
+ if (ename->length<6 || memcmp(tok,"\x04\x01", 2)!=0)
+ return GSS_S_FAILURE;
+
+ /*
+ * Extract the OID, and check it. Here GSSAPI breaks with tradition
+ * and does use the OID type and length bytes. To confuse things
+ * there are two lengths - the first including these, and the
+ * second without.
+ */
+
+ oidl = GET_16BIT(tok+2); /* length including next two bytes */
+ oidl = oidl-2; /* turn it into the _real_ length of the variable OID */
+
+ /*
+ * Check the BER encoding for correct type and length, that the
+ * string is long enough and that the OID matches that in our context
+ */
+ if (tok[4] != 0x06 || tok[5] != oidl ||
+ ename->length < oidl+6 ||
+ !ssh_gssapi_check_oid(ctx,tok+6,oidl))
+ return GSS_S_FAILURE;
+
+ offset = oidl+6;
+
+ if (ename->length < offset+4)
+ return GSS_S_FAILURE;
+
+ name->length = GET_32BIT(tok+offset);
+ offset += 4;
+
+ if (ename->length < offset+name->length)
+ return GSS_S_FAILURE;
+
+ name->value = xmalloc(name->length+1);
+ memcpy(name->value,tok+offset,name->length);
+ ((char *)name->value)[name->length] = 0;
+
+ return GSS_S_COMPLETE;
+}
+
+/* Extract the client details from a given context. This can only reliably
+ * be called once for a context */
+
+/* Priviledged (called from accept_secure_ctx) */
+OM_uint32
+ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
+{
+ int i = 0;
+
+ gss_buffer_desc ename;
+
+ client->mech = NULL;
+
+ while (supported_mechs[i]->name != NULL) {
+ if (supported_mechs[i]->oid.length == ctx->oid->length &&
+ (memcmp(supported_mechs[i]->oid.elements,
+ ctx->oid->elements, ctx->oid->length) == 0))
+ client->mech = supported_mechs[i];
+ i++;
+ }
+
+ if (client->mech == NULL)
+ return GSS_S_FAILURE;
+
+ if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
+ &client->displayname, NULL))) {
+ ssh_gssapi_error(ctx);
+ return (ctx->major);
+ }
+
+ if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
+ &ename))) {
+ ssh_gssapi_error(ctx);
+ return (ctx->major);
+ }
+
+ if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
+ &client->exportedname))) {
+ return (ctx->major);
+ }
+
+ /* We can't copy this structure, so we just move the pointer to it */
+ client->creds = ctx->client_creds;
+ ctx->client_creds = GSS_C_NO_CREDENTIAL;
+ return (ctx->major);
+}
+
+/* As user - called on fatal/exit */
+void
+ssh_gssapi_cleanup_creds(void)
+{
+ if (gssapi_client.store.filename != NULL) {
+ /* Unlink probably isn't sufficient */
+ debug("removing gssapi cred file\"%s\"", gssapi_client.store.filename);
+ unlink(gssapi_client.store.filename);
+ }
+}
+
+/* As user */
+void
+ssh_gssapi_storecreds(void)
+{
+ if (gssapi_client.mech && gssapi_client.mech->storecreds) {
+ (*gssapi_client.mech->storecreds)(&gssapi_client);
+ } else
+ debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
+}
+
+/* This allows GSSAPI methods to do things to the childs environment based
+ * on the passed authentication process and credentials.
+ */
+/* As user */
+void
+ssh_gssapi_do_child(char ***envp, u_int *envsizep)
+{
+
+ if (gssapi_client.store.envvar != NULL &&
+ gssapi_client.store.envval != NULL) {
+
+ debug("Setting %s to %s", gssapi_client.store.envvar,
+ gssapi_client.store.envval);
+ child_set_env(envp, envsizep, gssapi_client.store.envvar,
+ gssapi_client.store.envval);
+ }
+}
+
+/* Priviledged */
+int
+ssh_gssapi_userok(char *user)
+{
+ if (gssapi_client.exportedname.length == 0 ||
+ gssapi_client.exportedname.value == NULL) {
+ debug("No suitable client data");
+ return 0;
+ }
+#ifdef GSS_C_GLOBUS_LIMITED_PROXY_FLAG
+ if (limited) {
+ debug("limited proxy not acceptable for remote login");
+ return 0;
+ }
+#endif
+ if (gssapi_client.mech && gssapi_client.mech->userok)
+ return ((*gssapi_client.mech->userok)(&gssapi_client, user));
+ else
+ debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
+ return (0);
+}
+
/* Return a list of the gss-group1-sha1-x mechanisms supported by this
* program.
*
* that stores the results (in an expanded Gssctxt structure), which are
* then used by the first calls if that key exchange mechanism is chosen.
*/
-
+
+/* Unpriviledged */
char *
ssh_gssapi_server_mechanisms() {
gss_OID_set supported;
return(mechs);
}
-void ssh_gssapi_supported_oids(gss_OID_set *oidset) {
- int i =0;
- OM_uint32 maj_status,min_status;
- int present;
- gss_OID_set supported;
-
- gss_create_empty_oid_set(&min_status,oidset);
- PRIVSEP(gss_indicate_mechs(&min_status, &supported));
-
- while (supported_mechs[i]->name!=NULL) {
- if ((maj_status=gss_test_oid_set_member(&min_status,
- &supported_mechs[i]->oid,
- supported,
- &present))) {
- present=0;
- }
- if (present) {
- gss_add_oid_set_member(&min_status,
- &supported_mechs[i]->oid,
- oidset);
- }
- i++;
- }
-}
-
-/* Find out which GSS type (out of the list we define in ssh-gss.h) a
- * particular connection is using
- */
-ssh_gssapi_mech *
-ssh_gssapi_get_ctype(Gssctxt *ctxt) {
- int i=0;
-
- while(supported_mechs[i]->name!=NULL) {
- if (supported_mechs[i]->oid.length == ctxt->oid->length &&
- (memcmp(supported_mechs[i]->oid.elements,
- ctxt->oid->elements,ctxt->oid->length)==0)) {
- return supported_mechs[i];
- }
- i++;
- }
- return NULL;
-}
-
-/* Set the GSS context's OID to the oid indicated by the given key exchange
- * name. */
+/* Return the OID that corresponds to the given context name */
+/* Unpriviledged */
gss_OID
-ssh_gssapi_id_kex(Gssctxt *ctx, char *name) {
+ssh_gssapi_server_id_kex(char *name) {
int i=0;
if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) !=0) {
name+=sizeof(KEX_GSS_SHA1)-1; /* Move to the start of the MIME string */
while (supported_mechs[i]->name!=NULL &&
- strcmp(name,supported_mechs[i]->enc_name)!=0) {
+ strcmp(name,supported_mechs[i]->enc_name)!=0) {
i++;
}
if (supported_mechs[i]->name==NULL)
return (NULL);
- if (ctx) ssh_gssapi_set_oid(ctx,&supported_mechs[i]->oid);
-
debug("using GSSAPI mechanism %s (%s%s)", supported_mechs[i]->name,
KEX_GSS_SHA1, supported_mechs[i]->enc_name);
return &supported_mechs[i]->oid;
}
-/* Wrapper arround accept_sec_context
- * Requires that the context contains:
- * oid
- * credentials (from ssh_gssapi_acquire_cred)
- */
-OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx,gss_buffer_desc *recv_tok,
- gss_buffer_desc *send_tok, OM_uint32 *flags)
-{
- OM_uint32 status;
- gss_OID mech;
-
- ctx->major=gss_accept_sec_context(&ctx->minor,
- &ctx->context,
- ctx->creds,
- recv_tok,
- GSS_C_NO_CHANNEL_BINDINGS,
- &ctx->client,
- &mech, /* read-only pointer */
- send_tok,
- flags,
- NULL,
- &ctx->client_creds);
- if (GSS_ERROR(ctx->major)) {
- ssh_gssapi_error(ctx);
- }
-
- if (ctx->client_creds) {
- debug("Received some client credentials");
- } else {
- debug("Got no client credentials");
- }
-
- /* FIXME: We should check that the me
- * the one that we asked for (in ctx->oid) */
-
- status=ctx->major;
-
- /* Now, if we're complete and we have the right flags, then
- * we flag the user as also having been authenticated
- */
-
- if (((flags==NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
- (*flags & GSS_C_INTEG_FLAG))) &&
- (ctx->major == GSS_S_COMPLETE)) {
- if (ssh_gssapi_getclient(ctx,&gssapi_client.mech,
- &gssapi_client.name,
- &gssapi_client.creds))
- fatal("Couldn't convert client name");
- }
-
- /* Make sure that the getclient call hasn't stamped on this */
- return(status);
-}
-
-/* Extract the client details from a given context. This can only reliably
- * be called once for a context */
-
-OM_uint32
-ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_mech **type,
- gss_buffer_desc *name, gss_cred_id_t *creds) {
-
- *type=ssh_gssapi_get_ctype(ctx);
- if ((ctx->major=gss_display_name(&ctx->minor,ctx->client,name,NULL))) {
- ssh_gssapi_error(ctx);
- return(ctx->major);
- }
-
- /* This is icky. There appears to be no way to copy this structure,
- * rather than the pointer to it, so we simply copy the pointer and
- * mark the originator as empty so we don't destroy it.
- */
- *creds=ctx->client_creds;
- ctx->client_creds=GSS_C_NO_CREDENTIAL;
- return(ctx->major);
-}
-
-void
-ssh_gssapi_cleanup_creds(void *ignored)
-{
- if (gssapi_client.store.filename!=NULL) {
- /* Unlink probably isn't sufficient */
- debug("removing gssapi cred file\"%s\"",gssapi_client.store.filename);
- unlink(gssapi_client.store.filename);
- }
-}
-
-void
-ssh_gssapi_storecreds()
-{
- if (gssapi_client.mech && gssapi_client.mech->storecreds) {
- (*gssapi_client.mech->storecreds)(&gssapi_client);
- if (options.gss_cleanup_creds) {
- fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
- }
- } else {
- debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
- }
-}
-
-/* This allows GSSAPI methods to do things to the childs environment based
- * on the passed authentication process and credentials.
- *
- * Question: If we didn't use userauth_external for some reason, should we
- * still delegate credentials?
- */
-void
-ssh_gssapi_do_child(char ***envp, u_int *envsizep)
-{
-
- if (gssapi_client.store.envvar!=NULL &&
- gssapi_client.store.envval!=NULL) {
-
- debug("Setting %s to %s", gssapi_client.store.envvar,
- gssapi_client.store.envval);
- child_set_env(envp, envsizep, gssapi_client.store.envvar,
- gssapi_client.store.envval);
- }
-}
-
-int
-ssh_gssapi_userok(char *user)
-{
- if (gssapi_client.name.length==0 ||
- gssapi_client.name.value==NULL) {
- debug("No suitable client data");
- return 0;
- }
- if (gssapi_client.mech && gssapi_client.mech->userok) {
- return((*gssapi_client.mech->userok)(&gssapi_client,user));
- } else {
- debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
- }
- return(0);
-}
-
+/* Priviledged */
int
ssh_gssapi_localname(char **user)
{
*user = NULL;
- if (gssapi_client.name.length==0 ||
- gssapi_client.name.value==NULL) {
+ if (gssapi_client.displayname.length==0 ||
+ gssapi_client.displayname.value==NULL) {
debug("No suitable client data");
return(0);;
}
}
return(0);
}
+
+/* Priviledged */
+OM_uint32
+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
+{
+ ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
+ gssbuf, gssmic, NULL);
+
+ return (ctx->major);
+}
+
#endif