/*
- * Copyright (c) 2001 Simon Wilkinson. All rights reserved. *
+ * Copyright (c) 2001,2002 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:
#include "kex.h"
#include "log.h"
#include "compat.h"
+#include "monitor_wrap.h"
+#include "canohost.h"
#include <netdb.h>
gss_cred_id_t gssapi_client_creds = GSS_C_NO_CREDENTIAL; /* Their credentials */
enum ssh_gss_id gssapi_client_type = GSS_LAST_ENTRY;
+unsigned char ssh1_key_digest[16]; /* used for ssh1 gssapi */
+
/* The mechanism name used in the list below is defined in the internet
* draft as the Base 64 encoding of the MD5 hash of the ASN.1 DER encoding
* of the underlying GSSAPI mechanism's OID.
Buffer buf;
int i = 0;
int present;
+ int mech_count=0;
char * mechs;
- Gssctxt ctx;
- gss_buffer_desc token;
+ Gssctxt * ctx = NULL;
if (datafellows & SSH_OLD_GSSAPI) return NULL;
present=0;
}
if (present) {
- ssh_gssapi_build_ctx(&ctx);
- ssh_gssapi_set_oid(&ctx,&supported_mechs[i].oid);
- if (server) {
- if (ssh_gssapi_acquire_cred(&ctx)) {
- ssh_gssapi_delete_ctx(&ctx);
- continue;
+ if ((server &&
+ !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx,
+ &supported_mechs[i].oid))))
+ || (!server &&
+ !GSS_ERROR(ssh_gssapi_client_ctx(&ctx,
+ &supported_mechs[i].oid,
+ host)))) {
+ /* Append gss_group1_sha1_x to our list */
+ if (++mech_count > 1) {
+ buffer_append(&buf, ",", 1);
}
+ buffer_append(&buf, gssprefix,
+ strlen(gssprefix));
+ buffer_append(&buf,
+ supported_mechs[i].enc_name,
+ strlen(supported_mechs[i].enc_name));
+ debug("GSSAPI mechanism %s (%s%s) supported",
+ supported_mechs[i].name, gssprefix,
+ supported_mechs[i].enc_name);
+ } else {
+ debug("no credentials for GSSAPI mechanism %s",
+ supported_mechs[i].name);
}
- else { /* client */
- if (ssh_gssapi_import_name(&ctx,host))
- continue;
- maj_status=ssh_gssapi_init_ctx(&ctx, 0,
- GSS_C_NO_BUFFER,
- &token,
- NULL);
- ssh_gssapi_delete_ctx(&ctx);
- if (GSS_ERROR(maj_status)) {
- continue;
- }
- }
-
- /* Append gss_group1_sha1_x to our list */
- buffer_append(&buf, gssprefix,
- strlen(gssprefix));
- buffer_append(&buf, supported_mechs[i].enc_name,
- strlen(supported_mechs[i].enc_name));
- }
+ } else {
+ debug("GSSAPI mechanism %s not supported",
+ supported_mechs[i].name);
+ }
} while (supported_mechs[++i].name != NULL);
buffer_put_char(&buf,'\0');
enum ssh_gss_id ssh_gssapi_get_ctype(Gssctxt *ctxt) {
enum ssh_gss_id i=0;
- while(supported_mechs[i].name!=NULL &&
- supported_mechs[i].oid.length != ctxt->oid->length &&
- (memcmp(supported_mechs[i].oid.elements,
- ctxt->oid->elements,ctxt->oid->length) !=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 i;
i++;
}
- return(i);
+ return(GSS_LAST_ENTRY);
}
/* Set the GSS context's OID to the oid indicated by the given key exchange
* name. */
-int ssh_gssapi_id_kex(Gssctxt *ctx, char *name) {
+gss_OID ssh_gssapi_id_kex(Gssctxt *ctx, char *name) {
enum ssh_gss_id i=0;
if (strncmp(name, gssprefix, strlen(gssprefix)-1) !=0) {
- return(1);
+ return(NULL);
}
name+=strlen(gssprefix); /* Move to the start of the MIME string */
}
if (supported_mechs[i].name==NULL)
- return (1);
+ return (NULL);
- ssh_gssapi_set_oid(ctx,&supported_mechs[i].oid);
+ if (ctx) ssh_gssapi_set_oid(ctx,&supported_mechs[i].oid);
- return 0;
+ debug("using GSSAPI mechanism %s (%s%s)", supported_mechs[i].name,
+ gssprefix, supported_mechs[i].enc_name);
+
+ return &supported_mechs[i].oid;
}
/* All this effort to report an error ... */
-void
-ssh_gssapi_error(OM_uint32 major_status,OM_uint32 minor_status) {
+static void
+ssh_gssapi_error_ex(gss_OID mech, OM_uint32 major_status,
+ OM_uint32 minor_status,
+ int send_packet) {
OM_uint32 lmaj, lmin;
- gss_buffer_desc msg;
+ gss_buffer_desc msg = {0,NULL};
OM_uint32 ctx;
ctx = 0;
do {
lmaj = gss_display_status(&lmin, major_status,
GSS_C_GSS_CODE,
- GSS_C_NULL_OID,
+ mech,
&ctx, &msg);
if (lmaj == GSS_S_COMPLETE) {
debug((char *)msg.value);
+ if (send_packet) packet_send_debug((char *)msg.value);
(void) gss_release_buffer(&lmin, &msg);
}
} while (ctx!=0);
do {
lmaj = gss_display_status(&lmin, minor_status,
GSS_C_MECH_CODE,
- GSS_C_NULL_OID,
+ mech,
&ctx, &msg);
if (lmaj == GSS_S_COMPLETE) {
debug((char *)msg.value);
+ if (send_packet) packet_send_debug((char *)msg.value);
(void) gss_release_buffer(&lmin, &msg);
}
} while (ctx!=0);
}
+void
+ssh_gssapi_error(gss_OID mech,OM_uint32 major_status,OM_uint32 minor_status) {
+ ssh_gssapi_error_ex(mech, major_status, minor_status, 0);
+}
+
+void
+ssh_gssapi_send_error(gss_OID mech,
+ OM_uint32 major_status,OM_uint32 minor_status) {
+ ssh_gssapi_error_ex(mech, major_status, minor_status, 1);
+}
+
+
+
+
/* 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)
+ssh_gssapi_build_ctx(Gssctxt **ctx)
{
- 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;
+ *ctx=xmalloc(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)
+ssh_gssapi_delete_ctx(Gssctxt **ctx)
{
OM_uint32 ms;
- 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;
+ /* Return if there's no context */
+ if ((*ctx)==NULL)
+ return;
+
+#if !defined(MECHGLUE) /* mechglue has some memory management issues */
+ 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);
+ 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);
+#endif
+
+ xfree(*ctx);
+ *ctx=NULL;
}
/* Wrapper to init_sec_context
NULL);
ctx->status=maj_status;
if (GSS_ERROR(maj_status)) {
- ssh_gssapi_error(maj_status,min_status);
+ ssh_gssapi_error(ctx->oid,maj_status,min_status);
}
return(maj_status);
}
gss_buffer_desc *send_tok, OM_uint32 *flags)
{
OM_uint32 maj_status, min_status;
- gss_OID mech;
maj_status=gss_accept_sec_context(&min_status,
&ctx->context,
recv_tok,
GSS_C_NO_CHANNEL_BINDINGS,
&ctx->client,
- &mech,
+ &ctx->oid,
send_tok,
flags,
NULL,
&ctx->client_creds);
if (GSS_ERROR(maj_status)) {
- ssh_gssapi_error(maj_status,min_status);
+ ssh_gssapi_send_error(ctx->oid,maj_status,min_status);
}
if (ctx->client_creds) {
debug("Got no client credentials");
}
- /* FIXME: We should check that the mechanism thats being used is
+ /* FIXME: We should check that the me
* the one that we asked for (in ctx->oid) */
ctx->status=maj_status;
+ /* 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))) &&
+ (maj_status == GSS_S_COMPLETE)) {
+ if (ssh_gssapi_getclient(ctx,&gssapi_client_type,
+ &gssapi_client_name,
+ &gssapi_client_creds))
+ fatal("Couldn't convert client name");
+ }
+
return(maj_status);
}
/* Create a service name for the given host */
OM_uint32
-ssh_gssapi_import_name(Gssctxt *ctx,char *host) {
- gss_buffer_desc gssbuf;
+ssh_gssapi_import_name(Gssctxt *ctx, const char *host) {
+ gss_buffer_desc gssbuf = {0,NULL};
OM_uint32 maj_status, min_status;
- struct hostent *hostinfo = NULL;
char *xhost;
/* Make a copy of the host name, in case it was returned by a
* previous call to gethostbyname(). */
xhost = xstrdup(host);
- /* Make sure we have the FQDN. Some GSSAPI implementations don't do
+ /* If xhost is the loopback interface, switch it to our
+ true local hostname. */
+ resolve_localhost(&xhost);
+
+ /* Make sure we have the FQHN. Some GSSAPI implementations don't do
* this for us themselves */
-
- hostinfo = gethostbyname(xhost);
-
- if ((hostinfo == NULL) || (hostinfo->h_name == NULL)) {
- debug("Unable to get FQDN for \"%s\"", xhost);
- } else {
- xfree(xhost);
- xhost = hostinfo->h_name;
- }
-
+ make_fqhn(&xhost);
+
gssbuf.length = sizeof("host@")+strlen(xhost);
gssbuf.value = xmalloc(gssbuf.length);
if (gssbuf.value == NULL) {
+ xfree(xhost);
return(-1);
}
snprintf(gssbuf.value,gssbuf.length,"host@%s",xhost);
&gssbuf,
GSS_C_NT_HOSTBASED_SERVICE,
&ctx->name))) {
- ssh_gssapi_error(maj_status,min_status);
+ ssh_gssapi_error(ctx->oid, maj_status,min_status);
}
+ xfree(xhost);
xfree(gssbuf.value);
return(maj_status);
}
/* Acquire credentials for a server running on the current host.
* Requires that the context structure contains a valid OID
- */
+ */
+
+/* Returns a GSSAPI error code */
OM_uint32
ssh_gssapi_acquire_cred(Gssctxt *ctx) {
OM_uint32 maj_status, min_status;
&ctx->creds,
NULL,
NULL))) {
- ssh_gssapi_error(maj_status,min_status);
+ ssh_gssapi_error(GSS_C_NO_OID,maj_status,min_status);
}
gss_release_oid_set(&min_status, &oidset);
*type=ssh_gssapi_get_ctype(ctx);
if ((maj_status=gss_display_name(&min_status,ctx->client,name,NULL))) {
- ssh_gssapi_error(maj_status,min_status);
+ ssh_gssapi_error(GSS_C_NO_OID,maj_status,min_status);
}
/* This is icky. There appears to be no way to copy this structure,
ctx->client_creds=GSS_C_NO_CREDENTIAL;
return(maj_status);
}
+
+OM_uint32
+ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) {
+ OM_uint32 maj_status,min_status;
+ /* ssh1 needs to exchange the hash of the keys */
+ /* will us this hash to return it */
+ if (!compat20) {
+ if ((maj_status=gss_wrap(&min_status,ctx->context,
+ 0,
+ GSS_C_QOP_DEFAULT,
+ buffer,
+ NULL,
+ hash)))
+ ssh_gssapi_error(ctx->oid,maj_status,min_status);
+ }
+ else
+
+ if ((maj_status=gss_get_mic(&min_status,ctx->context,
+ GSS_C_QOP_DEFAULT, buffer, hash))) {
+ ssh_gssapi_error(ctx->oid,maj_status,min_status);
+ }
+
+ return(maj_status);
+}
+
+OM_uint32
+ssh_gssapi_server_ctx(Gssctxt **ctx,gss_OID oid) {
+ if (*ctx) ssh_gssapi_delete_ctx(ctx);
+ ssh_gssapi_build_ctx(ctx);
+ ssh_gssapi_set_oid(*ctx,oid);
+ return(ssh_gssapi_acquire_cred(*ctx));
+}
+
+OM_uint32
+ssh_gssapi_client_ctx(Gssctxt **ctx,gss_OID oid, char *host) {
+ gss_buffer_desc token = {0,NULL};
+ OM_uint32 major,minor;
+
+ if (*ctx) ssh_gssapi_delete_ctx(ctx);
+ ssh_gssapi_build_ctx(ctx);
+ ssh_gssapi_set_oid(*ctx,oid);
+ ssh_gssapi_import_name(*ctx,host);
+ major=ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, NULL);
+ gss_release_buffer(&minor,&token);
+ return(major);
+}
+
#endif /* GSSAPI */