SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o sshtty.o readconf.o clientloop.o
-SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-chall.o auth2-chall.o auth-rhosts.o auth-options.o auth-krb4.o auth-krb5.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o auth-sia.o sshpty.o sshlogin.o loginrec.o servconf.o serverloop.o md5crypt.o session.o groupaccess.o auth-skey.o auth-bsdauth.o gss-serv.o
+SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-gssapi.o auth-chall.o auth2-chall.o auth-rhosts.o auth-options.o auth-krb4.o auth-krb5.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o auth-sia.o sshpty.o sshlogin.o loginrec.o servconf.o serverloop.o md5crypt.o session.o groupaccess.o auth-skey.o auth-bsdauth.o gss-serv.o
MANPAGES = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out
MANPAGES_IN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1
--- /dev/null
+/*
+ * auth-gssapi.c
+ *
+ * Authentication using a gssapi library.
+ *
+ * Written by Von Welch (vwelch@ncsa.uiuc.edu)
+ */
+
+
+/*
+ * This code stolen from the gss-server.c sample program from MIT's
+ * kerberos 5 distribution.
+ */
+
+#include <config.h>
+
+#include <gssapi.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h> /* For MAXPATHLEN */
+
+#include "xmalloc.h"
+
+#ifdef GSSAPI_KRB5
+#include <krb5.h>
+#endif /* GSSAPI_KRB5 */
+
+#include "includes.h"
+#include "ssh.h"
+#include "packet.h"
+#include "log.h"
+#include "ssh1.h"
+
+/* Version Tag */
+static char gssapi_patch_version[] = GSSAPI_PATCH_VERSION;
+
+/*
+ * Declarations for mechanism-specific OIDs
+ */
+#ifdef GSSAPI_KRB5
+extern const gss_OID gss_nt_service_name;
+
+#ifndef GSS_C_NT_HOSTBASED_SERVICE
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+extern const gss_OID gss_nt_service_name;
+#endif /* GSS_C_NT_HOSTBASED_SERVICE */
+
+#endif /* GSSAPI_KRB5 */
+
+/*
+ * String describing our authentication type
+ */
+#ifdef GSI
+#define GSSAPI_AUTH_TYPE "Globus/ssleay"
+
+#elif defined(GSSAPI_KRB5)
+#define GSSAPI_AUTH_TYPE "Kerberos 5"
+#else
+#define GSSAPI_AUTH_TYE "Unknown"
+#endif
+
+/*
+ * Environment variables pointing to delegated credentials
+ */
+static char *delegation_env[] = {
+ "X509_USER_PROXY", /* GSSAPI/SSLeay */
+ "KRB5CCNAME", /* Krb5 and possibly SSLeay */
+ NULL
+};
+
+/*
+ * Other GSSAPI environment variables that should be passed to the
+ * child process
+ */
+static char *pass_to_child_env[] = {
+ "X509_CERT_DIR", /* GSSAPI/SSLeay */
+ NULL
+};
+
+/*
+ * Internal functions
+ */
+static int gssapi_setenv(const char *var, const char *value, const int override);
+static void gssapi_unsetenv(const char *var);
+
+/*
+ * This holds the MD5 hash of the server and host keys. It's
+ * filled in in sshd.c
+ */
+unsigned char ssh_key_digest[16];
+
+
+
+static void display_status_1(m, code, type)
+ char *m;
+ OM_uint32 code;
+ int type;
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc msg;
+ OM_uint32 msg_ctx;
+
+ msg_ctx = 0;
+ while (1) {
+ maj_stat = gss_display_status(&min_stat, code,
+ type, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ debug("GSS-API error %s: %s", m, (char *)msg.value);
+ (void) gss_release_buffer(&min_stat, &msg);
+
+ if (!msg_ctx)
+ break;
+ }
+}
+
+static void display_gssapi_status(msg, maj_stat, min_stat)
+ char *msg;
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+{
+ display_status_1(msg, maj_stat, GSS_C_GSS_CODE);
+ display_status_1(msg, min_stat, GSS_C_MECH_CODE);
+}
+
+
+static int gssapi_mechs_match(mech1, mech2)
+ gss_OID mech1;
+ gss_OID mech2;
+{
+ if (mech1->length != mech2->length)
+ return 0;
+
+ return (memcmp(mech1->elements, mech2->elements, mech1->length) == 0);
+}
+
+
+/*
+ * Authenticate and authorize a GSSAPI user. target_user should be
+ * the name of the local account the user is trying to access.
+ * source_host should be the name of the host the connection is
+ * coming from.
+ *
+ * Returns 0 on failure. On success returns 1 and fills in client_name
+ * with gssapi identity of user.
+ */
+int auth_gssapi(const char *target_account,
+ const char *source_host,
+ gss_buffer_desc *client_name)
+{
+ gss_buffer_desc send_tok;
+ gss_buffer_desc recv_tok;
+ gss_buffer_desc name_buf;
+ int type;
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ char *service_name = NULL;
+ gss_cred_id_t server_creds;
+ gss_name_t server_name;
+ gss_name_t client;
+ gss_OID mech_oid;
+ gss_OID_desc requested_mech;
+ gss_OID_set my_mechs;
+ unsigned int num_mechs;
+ unsigned int mech_len;
+ gss_OID_set_desc requested_mech_set;
+ int found_mech_match = 0;
+ gss_OID name_type;
+ OM_uint32 ret_flags = 0;
+ gss_ctx_id_t context = GSS_C_NO_CONTEXT;
+ int ret_stat = 0; /* 1 success */
+ char local_hostname[256]; /* Arbitrary size */
+ char *gssapi_auth_type = GSSAPI_AUTH_TYPE;
+ char *gssapi_identity = NULL;
+ int payload_len;
+
+
+ debug("Attempting %s GSSAPI authentication (%s). Reading mech oid from client",
+ gssapi_auth_type, gssapi_patch_version);
+
+ /*
+ * Read mech OID from initial packet
+ */
+ num_mechs = packet_get_int();
+
+ if (num_mechs == 0) {
+ /*
+ * At some point this will mean that we should do GSSAPI session
+ * negotiation. Currently we don't handle it.
+ */
+ debug("Number of GSSAPI mechanism is zero. Sending failure.");
+
+ packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
+ packet_send();
+ goto cleanup;
+ }
+
+ /* Set up our environment */
+ gssapi_setup_env();
+
+ maj_stat = gss_indicate_mechs(&min_stat, &my_mechs);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_gssapi_status("getting our mechanisms", maj_stat, min_stat);
+
+ /* Send abort message */
+ packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
+ packet_send();
+
+ goto cleanup;
+ }
+
+ debug("Got %d mechanisms from client. Looking for one I know...",
+ num_mechs);
+
+ while (num_mechs && (found_mech_match == 0)) {
+ int my_mech_num;
+
+
+ requested_mech.elements = packet_get_string(&mech_len);
+ requested_mech.length = mech_len;
+
+ /*
+ * See if we support this mech
+ */
+ for (my_mech_num = 0; my_mech_num < my_mechs->count; my_mech_num++)
+ {
+ if (gssapi_mechs_match(&requested_mech,
+ &(my_mechs->elements[my_mech_num]))) {
+ found_mech_match = 1;
+ break;
+ }
+ }
+ num_mechs--;
+ }
+
+ packet_get_all();
+
+ if (found_mech_match == 0) {
+ /*
+ * Found no supported mechanisms
+ */
+ debug("Unsupported GSSAPI mech(s)");
+
+ packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
+ packet_send();
+ goto cleanup;
+
+ }
+
+ /*
+ * Build our service name for importing credentials
+ */
+
+ if (gethostname(local_hostname, sizeof(local_hostname))) {
+ debug("Failure getting local hostname (gethostname() failed)");
+ goto cleanup;
+ }
+
+ service_name = (char *) malloc(strlen(local_hostname) +
+ strlen(GSSAPI_SERVICE_NAME) +
+ strlen(GSSAPI_SERVICE_NAME_FORMAT) +
+ 1 /* for NUL */);
+
+ if (service_name == NULL) {
+ debug("malloc() failed");
+ packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
+ packet_send();
+ goto cleanup;
+ }
+
+ sprintf(service_name, GSSAPI_SERVICE_NAME_FORMAT,
+ GSSAPI_SERVICE_NAME, local_hostname);
+
+ name_type = GSS_C_NT_HOSTBASED_SERVICE;
+
+ debug("Attempting %s authentication", gssapi_auth_type);
+ debug("Service name is %s", service_name);
+
+ /*
+ * Import the service name
+ */
+ name_buf.value = service_name;
+ name_buf.length = strlen(name_buf.value) + 1;
+ maj_stat = gss_import_name(&min_stat, &name_buf,
+ name_type, &server_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_gssapi_status("importing name", maj_stat, min_stat);
+
+ /* Send abort message */
+ packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
+ packet_send();
+
+ goto cleanup;
+ }
+
+ /*
+ * Build set of mech oids
+ */
+ requested_mech_set.elements = &requested_mech;
+ requested_mech_set.count = 1;
+
+ /*
+ * Get server credentials
+ */
+ maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
+ &requested_mech_set, GSS_C_ACCEPT,
+ &server_creds, NULL, NULL);
+
+ (void) gss_release_name(&min_stat, &server_name);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_gssapi_status("acquiring credentials", maj_stat, min_stat);
+
+ /* Send abort message */
+ packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
+ packet_send();
+
+ goto cleanup;
+ }
+
+ /*
+ * Respond to let client know mechanism was OK.
+ */
+ packet_start(SSH_SMSG_AUTH_GSSAPI_RESPONSE);
+ packet_put_string(requested_mech.elements, requested_mech.length);
+ packet_send();
+
+ do {
+ debug("Reading token from client...");
+
+ type = packet_read(&payload_len);
+
+ switch(type) {
+
+ case SSH_MSG_AUTH_GSSAPI_TOKEN:
+ /* This is what we expect */
+ break;
+
+ case SSH_MSG_AUTH_GSSAPI_ABORT:
+ debug("Client aborted connection");
+ packet_get_all();
+ goto cleanup;
+
+ default:
+ packet_disconnect("Protocol error during GSSAPI authentication: "
+ "Unknown packet type %d",
+ type);
+ /* Does not return */
+ }
+
+ recv_tok.value = packet_get_string((unsigned int *) &recv_tok.length);
+ packet_get_all();
+
+ debug("Got %d byte token from client", recv_tok.length);
+
+#ifdef GSI
+#ifdef GSS_C_GLOBUS_LIMITED_PROXY_FLAGS
+ /*
+ * We will not accept limited proxies for authentication, as
+ * they may have been stolen. This enforces part of the
+ * Globus security policy.
+ */
+ ret_flags = GSS_C_GLOBUS_LIMITED_PROXY_FLAG;
+
+#endif /* GSS_C_GLOBUS_LIMITED_PROXY_FLAGS */
+#endif /* GSI */
+
+ maj_stat =
+ gss_accept_sec_context(&min_stat,
+ &context,
+ server_creds,
+ &recv_tok,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client,
+ &mech_oid,
+ &send_tok,
+ &ret_flags,
+ NULL, /* ignore time_rec */
+ NULL); /* ignore del_cred_handle */
+
+
+ (void) gss_release_buffer(&min_stat, &recv_tok);
+
+ if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
+ display_gssapi_status("accepting context", maj_stat, min_stat);
+
+ /* Send abort message */
+ packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
+ packet_send();
+
+ goto cleanup;
+ }
+
+ if (send_tok.length != 0) {
+ debug("Sending response (%d bytes)...", send_tok.length);
+ packet_start(SSH_MSG_AUTH_GSSAPI_TOKEN);
+ packet_put_string((char *) send_tok.value, send_tok.length);
+ packet_send();
+ packet_write_wait();
+
+ (void) gss_release_buffer(&min_stat, &send_tok);
+ }
+
+ if (maj_stat == GSS_S_CONTINUE_NEEDED)
+ debug("Continue needed...");
+
+ } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+ maj_stat = gss_display_name(&min_stat, client, client_name, &mech_oid);
+
+#ifdef HAVE_GSS_EXPORT_NAME
+ if (maj_stat != GSS_S_COMPLETE) {
+ /*
+ * gss_display_name() in the globus gssapi_ssleay currently always
+ * fails, so fall back on gss_export_name().
+ */
+ maj_stat = gss_export_name(&min_stat, client, client_name);
+ }
+#endif /* HAVE_GSS_EXPORT_NAME */
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_gssapi_status("getting client name", maj_stat, min_stat);
+
+ /* Send an abort message */
+ packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
+ packet_send();
+
+ goto cleanup;
+ }
+
+ /* Successful authentication */
+
+ debug("%s authentication of %s successful",
+ gssapi_auth_type, client_name->value);
+
+ {
+ /*
+ * Send over hash of host and server keys.
+ */
+ gss_buffer_desc unwrapped_buf;
+ gss_buffer_desc wrapped_buf;
+ int conf_req_flag = 0; /* No encryption, just integrity */
+ int qop_req = GSS_C_QOP_DEFAULT; /* Default */
+ int conf_state;
+
+ debug("Sending hash of server and host keys...");
+
+ unwrapped_buf.value = ssh_key_digest;
+ unwrapped_buf.length = sizeof(ssh_key_digest);
+
+ maj_stat = gss_wrap(&min_stat,
+ context,
+ conf_req_flag,
+ qop_req,
+ &unwrapped_buf,
+ &conf_state,
+ &wrapped_buf);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ char *bogus = GSSAPI_NO_HASH_STRING;
+
+ display_gssapi_status("wrapping SSHD key hash",
+ maj_stat, min_stat);
+
+ /*
+ * Send over bogus packet and let client fail or not.
+ */
+ packet_start(SSH_SMSG_AUTH_GSSAPI_HASH);
+ packet_put_string(bogus, strlen(bogus));
+ packet_send();
+ packet_write_wait();
+
+ } else {
+
+ packet_start(SSH_SMSG_AUTH_GSSAPI_HASH);
+ packet_put_string(wrapped_buf.value, wrapped_buf.length);
+ packet_send();
+ packet_write_wait();
+
+ gss_release_buffer(&min_stat, &wrapped_buf);
+ }
+ }
+
+ /*
+ * Now check to see if user is authorized
+ */
+
+ /* Get copy of name we know is NUL terminated */
+ gssapi_identity = malloc(client_name->length + 1);
+
+ if (gssapi_identity == NULL) {
+ debug("Out of memory");
+ goto cleanup;
+ }
+
+ memcpy(gssapi_identity, (char *) client_name->value, client_name->length);
+ gssapi_identity[client_name->length] = '\0';
+
+ debug("Checking to see if \"%s\" is authorized to access account %s",
+ gssapi_identity, target_account);
+
+ /*
+ * Use mechanism-specific code to check authorization
+ */
+#if defined(GSSAPI_KRB5)
+ {
+ /*
+ * Kerberos 5 authorization
+ */
+ krb5_context k5context;
+ krb5_principal princ;
+ krb5_error_code k5error;
+
+
+ k5error = krb5_init_context(&k5context);
+ if (k5error) {
+ debug("Initialization of Kerberos context failed: %s",
+ error_message(k5error));
+ goto cleanup;
+ }
+
+ k5error = krb5_parse_name(k5context, gssapi_identity, &princ);
+ if (k5error) {
+ debug("Parsing of Kerberos principal \"%s\" failed: %s",
+ gssapi_identity, error_message(k5error));
+ krb5_free_context(k5context);
+ goto cleanup;
+ }
+
+ if (krb5_kuserok(k5context, princ, target_account)) {
+ /* Success */
+ ret_stat = 1;
+ }
+
+ krb5_free_principal(k5context, princ);
+ krb5_free_context(k5context);
+
+ }
+#endif /* GSSAPI_KRB5 */
+
+#if defined(GSI)
+ {
+ if (globus_gss_assist_userok(gssapi_identity, target_account) != 0) {
+ debug("globus_gss_assist_userok() failed");
+
+ } else {
+ /* Success */
+ ret_stat = 1;
+ }
+ }
+#endif /* GSI */
+
+ debug("%s authorization of \"%s\" from %s to account %s %s",
+ gssapi_auth_type, gssapi_identity, source_host, target_account,
+ (ret_stat ? "successful" : "failed"));
+
+ cleanup:
+ if (gssapi_identity != NULL)
+ free(gssapi_identity);
+
+ if (service_name != NULL)
+ free(service_name);
+
+ return ret_stat;
+}
+
+
+/*
+ * SSLeay GSSAPI clients may send us a user name of the form:
+ *
+ * (1) username:x:SSL Subject Name
+ * or
+ * (2) username:i:SSL Subject Name
+ * or
+ * (3) username
+ *
+ * if case 1, then uname is an explicit name (ssh -l uname). Keep this
+ * name always, rewrite the user parameter to be just uname. We'll pull
+ * the GSSAPI idenity out and deal with (or skip it) later.
+ *
+ * if case 2, then uname is implicit (user didn't use the -l option), so
+ * use the default gridmap mapping and replace uname with whatever
+ * the gridmap maps to. If the gridmap mapping fails, drop down
+ * to just uname
+ *
+ * if case 3, then leave it be.
+ *
+ * This function may return the original pointer to the orginal string,
+ * the original pointer to a modified string, or a completely new pointer.
+ */
+char *
+gssapi_parse_userstring(char *userstring)
+{
+ char name_type = '\0'; /* explicit 'x' or implicit 'i' */
+ char *ssl_subject_name = NULL;
+ char *delim = NULL;
+ char *gridmapped_name = NULL;
+
+ debug("Looking at username '%s' for gssapi-ssleay type name", userstring);
+ if(delim = strchr(userstring, ':')) {
+ /* Parse and split into components */
+ ssl_subject_name = strchr(delim + 1, ':');
+
+ if (ssl_subject_name) {
+ /* Successful parse, split into components */
+ *delim = '\0';
+ name_type = *(delim + 1);
+ *ssl_subject_name = '\0';
+ ssl_subject_name++;
+
+ debug("Name parsed. type = '%c'. ssl subject name is \"%s\"",
+ name_type, ssl_subject_name);
+
+ } else {
+
+ debug("Don't understand name format. Letting it pass.");
+ }
+ }
+
+#ifdef GSI
+ if(ssl_subject_name) {
+ switch (name_type) {
+ case 'x':
+ debug("explicit name given, using %s as username", userstring);
+ break;
+
+ case 'i':
+ /* gridmap check */
+ debug("implicit name given. gridmapping '%s'", ssl_subject_name);
+
+ /* Need to setup environment early for this call */
+ gssapi_setup_env();
+
+ if(globus_gss_assist_gridmap(ssl_subject_name,
+ &gridmapped_name) == 0) {
+ userstring = gridmapped_name;
+ debug("I gridmapped and got %s", userstring);
+
+ } else {
+ debug("I gridmapped and got null, reverting to %s", userstring);
+ }
+ break;
+
+ default:
+ debug("Unknown name type '%c'. Ignoring.", name_type);
+ break;
+ }
+ } else {
+ debug("didn't find any :'s so I assume it's just a user name");
+ }
+#endif /* GSI */
+
+ return userstring;
+}
+
+
+/*
+ * Setup our environment with defaults for the GSSAPI library.
+ */
+void
+gssapi_setup_env(void)
+{
+ char ccname[MAXPATHLEN + 10 /* just to be sure */];
+ struct stat st;
+ int tmp_index = 0;
+ static int gssapi_env_setup_done = 0; /* Have been called */
+#ifdef KERBEROS
+ extern char *ticket;
+#endif /* KERBEROS */
+
+
+ if (gssapi_env_setup_done)
+ return; /* Already have been called */
+
+ debug("Setting up environment variables for GSSAPI library");
+
+#ifdef GSI
+#ifdef PATH_GSSAPI_GLOBUS_USER_KEY
+ gssapi_setenv("X509_USER_KEY", PATH_GSSAPI_GLOBUS_USER_KEY, 0);
+#endif /* PATH_GSSAPI_GLOBUS_USER_KEY */
+#ifdef PATH_GSSAPI_GLOBUS_USER_CERT
+ gssapi_setenv("X509_USER_CERT", PATH_GSSAPI_GLOBUS_USER_CERT, 0);
+#endif /* PATH_GSSAPI_GLOBUS_USER_CERT */
+#ifdef PATH_GSSAPI_GLOBUS_CERT_DIR
+ gssapi_setenv("X509_CERT_DIR", PATH_GSSAPI_GLOBUS_CERT_DIR, 0);
+#endif /* PATH_GSSAPI_GLOBUS_CERT_DIR */
+#ifdef PATH_GSSAPI_GLOBUS_GRIDMAP
+ gssapi_setenv("GRIDMAP", PATH_GSSAPI_GLOBUS_GRIDMAP, 0);
+#endif /* PATH_GSSAPI_GLOBUS_GRIDMAP */
+#endif /* GSI */
+
+ /*
+ * Always make sure that KRB5CCNAME is set.
+ *
+ * For the Kerberos 5 GSSAPI library this is needed so that we know
+ * where to put the delegated credentials, since the library itself
+ * will try to put them in a default location.
+ *
+ * For the Globus/GSI GSSAPI library this is needed in case we want
+ * to run sslk5 afterwards and get a Kerberos 5 ticket, we'll know
+ * where sslk5 put it.
+ */
+
+ debug("Making sure KRB5CCNAME is set");
+
+ sprintf(ccname, "FILE:/tmp/krb5cc_p%d", getpid());
+
+ /* Make sure we have a unique name */
+ while(stat(ccname, &st) == 0) {
+ sprintf(ccname, "FILE:/tmp/krb5cc_p%d.%d", getpid(), tmp_index++);
+ }
+
+#ifdef KERBEROS
+ /*
+ * If Kerberos already has a cache picked, then use it instead.
+ * Otherwise tell it what we're up to.
+ */
+ if (!ticket || strcmp(ticket, "none") == 0) {
+ ticket = xstrdup(ccname);
+
+ } else {
+ debug("Using KRB5CCNAME generated by Kerberos code");
+ strncpy(ccname, ticket, sizeof(ccname));
+ }
+#endif /* KERBEROS */
+
+ debug("Setting KRB5CCNAME to %s", ccname);
+ gssapi_setenv("KRB5CCNAME", ccname, 1);
+
+ gssapi_env_setup_done = 1;
+}
+
+
+
+/*
+ * Clean our environment on startup. This means removing any environment
+ * strings that might inadvertantly been in root's environment and
+ * could cause serious security problems if we think we set them.
+ */
+void
+gssapi_clean_env(void)
+{
+ char *envstr;
+ int envstr_index;
+
+
+ for (envstr_index = 0;
+ (envstr = delegation_env[envstr_index]) != NULL;
+ envstr_index++) {
+
+ if (getenv(envstr)) {
+ debug("Clearing environment variable %s", envstr);
+ gssapi_unsetenv(envstr);
+ }
+ }
+}
+
+
+
+/*
+ * Fix up our environment after GSSAPI authentication
+ */
+int
+gssapi_fix_env(void)
+{
+ int status = 0;
+#ifdef KERBEROS
+ extern char *ticket;
+#endif /* KERBEROS */
+
+
+#ifdef GSI
+ /*
+ * The gssapi library puts the user's credentials into
+ * X509_USER_DELEG_PROXY. We need to copy that over into
+ * X509_USER_PROXY for actual use.
+ */
+ if (getenv("X509_USER_DELEG_PROXY")) {
+ debug("Setting X509_USER_PROXY to '%s'",
+ getenv("X509_USER_DELEG_PROXY"));
+
+ if (gssapi_setenv("X509_USER_PROXY", getenv("X509_USER_DELEG_PROXY"), 1)) {
+ debug("Failed to set X509_USER_PROXY environment variable");
+ status = 1;
+ }
+ }
+#endif /* GSI */
+
+#ifdef GSSAPI_KRB5
+#ifdef KERBEROS
+ {
+ /*
+ * If the Kerberos 5 GSSAPI library set KRB5CCNAME make sure it's
+ * reflected in the ticket variable.
+ */
+ char *env_ticket;
+
+ env_ticket = getenv("KRB5CCNAME");
+
+ if (env_ticket && *env_ticket &&
+ (!ticket ||
+ strcmp(env_ticket, ticket))) {
+
+ /* Deallocate old string if present */
+ if (ticket && strcmp(ticket, "none"))
+ free(ticket);
+
+ ticket = xstrdup(env_ticket);
+
+ debug("Using KRB5CCNAME set by GSSAPI code: %s", ticket);
+ }
+ }
+#endif /* KERBEROS */
+#endif /* GSSAPI_KRB5 */
+
+ return status;
+}
+
+/*
+ * Fix the ownership on delegated credentials. Returns 0 on success,
+ * non-zero on error.
+ */
+int
+gssapi_chown_delegation(uid_t uid, gid_t gid)
+{
+ char *envstr;
+ int envstr_index;
+ char *cred_path;
+ int status = 0;
+ struct stat buf;
+
+
+ for (envstr_index = 0;
+ (envstr = delegation_env[envstr_index]) != NULL;
+ envstr_index++) {
+
+ cred_path = getenv(envstr);
+
+ if (!cred_path || !*cred_path)
+ continue;
+
+ /* For Kerberos strip leading 'FILE:' if present */
+ if (strncmp(cred_path, "FILE:", 5) == 0)
+ cred_path += 5;
+
+ if (stat(cred_path, &buf) != 0)
+ continue; /* File does not exist */
+
+ /* Do some sanity checking on the file */
+ if (!S_ISREG(buf.st_mode)) {
+ debug("Environment variable %s points at %s which is not a regular file",
+ envstr, cred_path);
+ continue;
+ }
+
+ debug("Changing ownership of credentials cache '%s'", cred_path);
+
+ if (chown(cred_path, uid, gid) != 0) {
+ debug("Warning: chown of '%s' failed (errno = %d)",
+ cred_path, errno);
+ status = 1;
+ }
+ }
+
+ return status;
+}
+
+
+/*
+ * Pass all the GSSAPI environment variables down to the child
+ */
+void
+gssapi_child_set_env(char ***p_env,
+ unsigned int *p_envsize)
+{
+ char *envstr;
+ int envstr_index;
+ char *env_value;
+
+
+ /* First all the pointers to credentials caches */
+ for (envstr_index = 0;
+ (envstr = delegation_env[envstr_index]) != NULL;
+ envstr_index++) {
+
+ env_value = getenv(envstr);
+
+ if (env_value == NULL)
+ continue;
+
+ debug("Setting %s for child process to '%s'",
+ envstr, env_value);
+
+ child_set_env(p_env, p_envsize, envstr, env_value);
+ }
+
+ /* And other environment variables */
+ for (envstr_index = 0;
+ (envstr = pass_to_child_env[envstr_index]) != NULL;
+ envstr_index++) {
+
+ env_value = getenv(envstr);
+
+ if (env_value == NULL)
+ continue;
+
+ debug("Setting %s for child process to '%s'",
+ envstr, env_value);
+
+ child_set_env(p_env, p_envsize, envstr, env_value);
+ }
+}
+
+
+/*
+ * Remove the forwarded proxy credentials
+ */
+void
+gssapi_remove_delegation(void)
+{
+ char *envstr;
+ int envstr_index;
+ char *cred_path;
+ struct stat buf;
+
+
+ for (envstr_index = 0;
+ (envstr = delegation_env[envstr_index]) != NULL;
+ envstr_index++) {
+
+ cred_path = getenv(envstr);
+
+ if (!cred_path)
+ continue;
+
+ /* For Kerberos strip leading 'FILE:' if present */
+ if (strncmp(cred_path, "FILE:", 5) == 0)
+ cred_path += 5;
+
+ /*
+ * If this is a DCE context, then don't remove it as we may
+ * be sharing it with other PAGSs
+ */
+ if (strncmp(cred_path, "/opt/dcelocal/var/security/creds", 32) == 0)
+ continue;
+
+ if (stat(cred_path, &buf) != 0)
+ continue; /* File does not exist */
+
+ if (remove(cred_path) != 0) {
+ debug("Error removing credentials cache '%s' (errno = %d)",
+ cred_path, errno);
+ }
+ }
+}
+
+
+/*
+ * Wrapper around putenv. Return zero on success, non-zero on error.
+ */
+static int
+gssapi_setenv(const char *var,
+ const char *value,
+ const int override)
+{
+ char *envstr = NULL;
+ int status;
+
+
+ /* If we're not overriding and it's already set, then return */
+ if (!override && getenv(var))
+ return 0;
+
+ envstr = xmalloc(strlen(var) + strlen(value) + 2 /* '=' and NUL */);
+
+ sprintf(envstr, "%s=%s", var, value);
+
+ status = putenv(envstr);
+
+ /* Don't free envstr as it may still be in use */
+
+ return status;
+}
+
+
+/*
+ * Wrapper around unsetenv.
+ */
+static void
+gssapi_unsetenv(const char *var)
+{
+#ifdef HAVE_UNSETENV
+ unsetenv(var);
+
+#else /* !HAVE_UNSETENV */
+ extern char **environ;
+ char **p1 = environ; /* New array list */
+ char **p2 = environ; /* Current array list */
+ int len = strlen(var);
+
+ /*
+ * Walk through current environ array (p2) copying each pointer
+ * to new environ array (p1) unless the pointer is to the item
+ * we want to delete. Copy happens in place.
+ */
+ while (*p2) {
+ if ((strncmp(*p2, var, len) == 0) &&
+ ((*p2)[len] == '=')) {
+ /*
+ * *p2 points at item to be deleted, just skip over it
+ */
+ p2++;
+ } else {
+ /*
+ * *p2 points at item we want to save, so copy it
+ */
+ *p1 = *p2;
+ p1++;
+ p2++;
+ }
+ }
+
+ /* And make sure new array is NULL terminated */
+ *p1 = NULL;
+#endif /* HAVE_UNSETENV */
+}
+
+
+
#include "misc.h"
#include "uidswap.h"
+/*modified by bine*/
+#include <gssapi.h>
+#include "ssh.h"
+#include "canohost.h"
+/*end of modification*/
+
/* import */
extern ServerOptions options;
}
break;
#endif /* KRB4 || KRB5 */
+
+/*modified by binhe*/
+#ifdef GSSAPI
+ case SSH_CMSG_AUTH_GSSAPI:
+ if (options.gss_authentication) {
+ gss_buffer_desc client_name;
+ OM_uint32 min_stat;
+
+
+ if (auth_gssapi(authctxt->user, get_canonical_hostname(options.reverse_mapping_check), &client_name)) {
+
+ /* Successful authentication and authorization */
+ authenticated = 1;
+
+ gss_release_buffer(&min_stat, &client_name);
+ }
+
+ } else {
+ packet_get_all();
+ debug("GSSAPI authentication disabled.");
+ }
+
+ break;
+#endif /* GSSAPI */
+/*end of modification*/
#if defined(AFS) || defined(KRB5)
/* XXX - punt on backward compatibility here. */
user = packet_get_string(&ulen);
packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER);
+/*modified by binhe*/
+#ifndef GSSAPI
+ /* GSSAPI clients may legitimately send long names (see below) */
+
+ if (strlen(user) > 255)
+ do_authentication_fail_loop();
+
+#else /* GSSAPI */
+
+ /* Parse GSSAPI identity from userstring */
+ user = gssapi_parse_userstring(user);
+
+#endif /* GSSAPI */
+/*end of modification*/
+
if ((style = strchr(user, ':')) != NULL)
*style++ = '\0';
]
)
+# Check whether the user wants GSI (Globus) support
+gsi_path="no"
+AC_ARG_WITH(gsi,
+ [ --with-gsi=PATH Enable GSI/Globus authentication support],
+ [
+ gsi_path="$withval"
+ ]
+)
+
+AC_ARG_WITH(globus,
+ [ --with-globus=PATH Enable GSI/Globus authentication support],
+ [
+ gsi_path="$withval"
+ ]
+)
+
+if test "x$gsi_path" != "xno" ; then
+ # Globus GSSAPI configuration
+ AC_DEFINE(GSSAPI)
+ AC_DEFINE(GSI)
+
+ # Find GLOBUS/GSI installation Directory
+ AC_MSG_CHECKING(for Globus/GSI installation directory)
+
+ globus_install_dir=$gsi_path
+
+ if test "x$globus_install_dir" = "xyes" ; then
+ if test -n "$GLOBUS_INSTALL_PATH" ; then
+ globus_install_dir=$GLOBUS_INSTALL_PATH
+ elif test -n "$GSI_INSTALL_PATH" ; then
+ globus_install_dir=$GSI_INSTALL_PATH
+ elif test -d /usr/local/globus ; then
+ globus_install_dir="/usr/local/globus"
+ elif test -d /usr/local/gsi ; then
+ globus_install_dir="/usr/local/gsi"
+ else
+ AC_MSG_ERROR(Cannot find Globus/GSI installation directory)
+ fi
+ fi
+ AC_MSG_RESULT($globus_install_dir)
+
+ # Find GLOBUS/GSI development directory
+ AC_MSG_CHECKING(for Globus/GSI development directory)
+
+ if test -d ${globus_install_dir}/lib ; then
+ # Looks like a flat directory structure from configure/make
+ # and not globus-install or gsi-install
+ globus_dev_dir=$globus_install_dir
+
+ else
+ # Assume a true globus installation with architecture
+ # directories and run globus-development-path to find
+ # the development directory
+
+ # Set GLOBUS_INSTALL_PATH
+ GLOBUS_INSTALL_PATH=$globus_install_dir
+ export GLOBUS_INSTALL_PATH
+
+ dev_path_program=${globus_install_dir}/bin/globus-development-path
+
+ if test ! -x ${dev_path_program} ; then
+ AC_MSG_ERROR(Cannot find Globus/GSI installation directory: program ${dev_path_program} does not exist or is not executable)
+ fi
+
+ globus_dev_dir=`${dev_path_program}`
+
+ if test -z "$globus_dev_dir" -o "X$globus_dev_dir" = "X<not found>" ; then
+ AC_MSG_ERROR(Cannot find Globus/GSI development directory)
+ fi
+
+ if test ! -d "$globus_dev_dir" ; then
+ AC_MSG_ERROR(Cannot find Globus/GSI development directory: $globus_dev_dir does not exist)
+ fi
+ fi
+ AC_MSG_RESULT($globus_dev_dir)
+
+ GSI_LIBS="-lglobus_gss_assist -lglobus_gss -lglobus_gaa -lssl -lcrypto"
+ GSI_LDFLAGS="-L${globus_dev_dir}/lib"
+ GSI_CFLAGS="-I${globus_dev_dir}/include"
+
+ LIBS="$LIBS $GSI_LIBS"
+ LDFLAGS="$LDFLAGS $GSI_LDFLAGS"
+ CFLAGS="$CFLAGS $GSI_CFLAGS"
+# End Globus/GSI section
+fi
+
# Check whether user wants AFS support
AFS_MSG="no"
AC_ARG_WITH(afs,
return buffer_get_string(&incoming_packet, length_ptr);
}
+/*modified by binhe*/
+/* Clears incoming data buffer */
+
+void packet_get_all(void)
+{
+ buffer_clear(&incoming_packet);
+}
+/*end of modification*/
+
/*
* Sends a diagnostic message from the server to the client. This message
* can be sent at any time (but not while constructing another message). The
void packet_get_bignum2(BIGNUM * value, int *length_ptr);
char *packet_get_raw(int *length_ptr);
char *packet_get_string(u_int *length_ptr);
+/*modified by binhe*/
+void packet_get_all(void);
+/*end of modification*/
void packet_disconnect(const char *fmt,...) __attribute__((format(printf, 1, 2)));
void packet_send_debug(const char *fmt,...) __attribute__((format(printf, 1, 2)));
options->gss_deleg_creds = -1;
#ifdef GSI
options->gss_globus_deleg_limited_proxy = -1;
+/*modified by binhe*/
+ options->forward_gssapi_globus_proxy = -1;
+ options->forward_gssapi_globus_limited_proxy = -1;
+/*end of modification*/
#endif /* GSI */
#endif /* GSSAPI */
#ifdef GSI
if (options->gss_globus_deleg_limited_proxy == -1)
options->gss_globus_deleg_limited_proxy = 0;
+/*modified by binhe*/
+ if (options->forward_gssapi_globus_proxy == -1)
+ options->forward_gssapi_globus_proxy = 0;
+ if (options->forward_gssapi_globus_limited_proxy == -1)
+ options->forward_gssapi_globus_limited_proxy = 0;
+/*end of modification*/
#endif /* GSI */
#endif /* GSSAPI */
#if defined(KRB4) || defined(KRB5)
int gss_deleg_creds;
#ifdef GSI
int gss_globus_deleg_limited_proxy;
+/*modified by binhe*/
+ int forward_gssapi_globus_proxy;
+ int forward_gssapi_globus_limited_proxy;
+/*end of modification*/
#endif /* GSI */
#endif /* GSSAPI */
break;
case 'a':
options.forward_agent = 0;
+/*modified by binhe*/
+#ifdef GSSAPI
+#ifdef GSI
+ options.forward_gssapi_globus_proxy = 0;
+#endif /* GSI */
+#endif /* GSSAPI */
+/*end of modification*/
break;
case 'A':
options.forward_agent = 1;
# define SSHD_PAM_SERVICE __progname
#endif
+/*modified by binhe*/
+#ifdef GSSAPI
+/*------------ GSSAPI-related functions -----------------------*/
+
+#include <gssapi.h>
+/*
+ * Given a target account, and source host, perform GSSAPI authentication
+ * and authorization. Returns 1 on success, 0 on failure. On success fills
+ * in client_name with the GSSAPI identity of the user.
+ */
+int auth_gssapi(const char *target_account,
+ const char *source_host,
+ gss_buffer_desc *client_name);
+
+/*
+ * The userstring sent by the client may contain a GSSAPI identity which
+ * the server can use to determine the target account. This function
+ * parses the userstring and does the local account determination,
+ * if needed.
+ */
+char *
+gssapi_parse_userstring(char *username);
+
+/*
+ * Change the ownership of all delegated credentials to the user.
+ * Returns 0 on success, non-zero on error.
+ */
+int
+gssapi_chown_delegation(uid_t uid, gid_t gid);
+
+/*
+ * Remove the forwarded proxy credentials
+ */
+void
+gssapi_remove_delegation(void);
+
+/*
+ * Clean our environment on startup. This means removing any environment
+ * strings that might inadvertantly been in root's environment and
+ * could cause serious security problems if we think we set them.
+ */
+void
+gssapi_clean_env(void);
+
+/*
+ * Set up our environment for GSSAPI authentication
+ */
+void
+gssapi_setup_env(void);
+
+/*
+ * Fix up our environment after GSSAPI authentication
+ */
+int
+gssapi_fix_env(void);
+
+/*
+ * Pass all the GSSAPI environment variables to the child.
+ */
+void
+gssapi_child_set_env(char ***p_env,
+ unsigned int *p_envsize);
+
+/*
+ * A string containing the version of the GSSAPI patch applied
+ */
+#define GSSAPI_PATCH_VERSION "GSSAPI_PATCH FOR OPENSSH-3.0.2p1"
+
+#ifndef GSSAPI_SERVICE_NAME
+#define GSSAPI_SERVICE_NAME "host"
+#endif /* GSSAPI_SERVICE_NAME */
+
+#ifndef GSSAPI_SERVICE_NAME_FORMAT
+#define GSSAPI_SERVICE_NAME_FORMAT "%s@%s" /* host@fqdn */
+#endif /* GSSAPI_SERVICE_NAME_FORMAT */
+
+/* String to send if we don't have a valid hash of sshd keys */
+#define GSSAPI_NO_HASH_STRING "GSSAPI_NO_HASH"
+#endif /* GSSAPI */
+/*end of modification*/
+
/*
* Name of the environment variable containing the pathname of the
* authentication socket.
#define SSH_MSG_CHANNEL_INPUT_EOF SSH_MSG_CHANNEL_CLOSE
#define SSH_MSG_CHANNEL_OUTPUT_CLOSE SSH_MSG_CHANNEL_CLOSE_CONFIRMATION
+/*modified by binhe*/
+/* GSS-API authentication */
+#define SSH_CMSG_AUTH_GSSAPI 88 /* int, strings... */
+#define SSH_SMSG_AUTH_GSSAPI_RESPONSE 89 /* string */
+#define SSH_MSG_AUTH_GSSAPI_TOKEN 90 /* string */
+#define SSH_SMSG_AUTH_GSSAPI_HASH 91 /* string */
+#define SSH_MSG_AUTH_GSSAPI_ABORT 92 /* */
+/*end of modification*/
+
/*
* Authentication methods. New types can be added, but old types should not
* be removed for compatibility. The maximum allowed value is 31.
/* 8 to 15 are reserved */
#define SSH_PASS_AFS_TOKEN 21
+/*modified by binhe*/
+#define SSH_AUTH_GSSAPI 24
+/*end of modification*/
+
/* Protocol flags. These are bit masks. */
#define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes screen */
#define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain host */
#include "canohost.h"
#include "auth.h"
+/*modified by binhe*/
+#ifdef GSSAPI
+#include <gssapi.h>
+#include <openssl/md5.h>
+#include "bufaux.h"
+static char gssapi_patch_version[] = GSSAPI_PATCH_VERSION;
+/*
+ * MD5 hash of host and session keys for verification. This is filled
+ * in in ssh_login() and then checked in try_gssapi_authentication().
+ */
+unsigned char ssh_key_digest[16];
+#endif /* GSSAPI */
+/*end of modification*/
+
/* Session id for the current session. */
u_char session_id[16];
u_int supported_authentications = 0;
return 0;
}
+/*modified by binhe*/
+#ifdef GSSAPI
+/*
+ * This code stolen from the gss-client.c sample program from MIT's
+ * kerberos 5 distribution.
+ */
+
+gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
+
+static void display_status_1(m, code, type)
+ char *m;
+ OM_uint32 code;
+ int type;
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc msg;
+ OM_uint32 msg_ctx;
+
+ msg_ctx = 0;
+ while (1) {
+ maj_stat = gss_display_status(&min_stat, code,
+ type, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ debug("GSS-API error %s: %s", m, (char *)msg.value);
+ (void) gss_release_buffer(&min_stat, &msg);
+
+ if (!msg_ctx)
+ break;
+ }
+}
+
+static void display_gssapi_status(msg, maj_stat, min_stat)
+ char *msg;
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+{
+ display_status_1(msg, maj_stat, GSS_C_GSS_CODE);
+ display_status_1(msg, min_stat, GSS_C_MECH_CODE);
+}
+
+#ifdef GSI
+int get_gssapi_cred()
+{
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+
+
+ debug("calling gss_acquire_cred");
+ maj_stat = gss_acquire_cred(&min_stat,
+ GSS_C_NO_NAME,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET,
+ GSS_C_INITIATE,
+ &gss_cred,
+ NULL,
+ NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_gssapi_status("Failuring acquiring GSSAPI credentials",
+ maj_stat, min_stat);
+ gss_cred = GSS_C_NO_CREDENTIAL; /* should not be needed */
+ return 0;
+ }
+
+ return 1; /* Success */
+}
+
+char * get_gss_our_name()
+{
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_name_t pname = GSS_C_NO_NAME;
+ gss_buffer_desc tmpname;
+ gss_buffer_t tmpnamed = &tmpname;
+ char *retname;
+
+ debug("calling gss_inquire_cred");
+ maj_stat = gss_inquire_cred(&min_stat,
+ gss_cred,
+ &pname,
+ NULL,
+ NULL,
+ NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ return NULL;
+ }
+
+ maj_stat = gss_export_name(&min_stat,
+ pname,
+ tmpnamed);
+ if (maj_stat != GSS_S_COMPLETE) {
+ return NULL;
+ }
+ debug("gss_export_name finsished");
+ retname = (char *)malloc(tmpname.length + 1);
+ if (!retname) {
+ return NULL;
+ }
+ memcpy(retname, tmpname.value, tmpname.length);
+ retname[tmpname.length] = '\0';
+
+ gss_release_name(&min_stat, &pname);
+ gss_release_buffer(&min_stat, tmpnamed);
+
+ return retname;
+}
+#endif /* GSI */
+
+int try_gssapi_authentication(char *host, Options *options)
+{
+ char *service_name = NULL;
+ gss_buffer_desc name_tok;
+ gss_buffer_desc send_tok;
+ gss_buffer_desc recv_tok;
+ gss_buffer_desc *token_ptr;
+ gss_name_t target_name = NULL;
+ gss_ctx_id_t gss_context;
+ gss_OID_desc mech_oid;
+ gss_OID name_type;
+ gss_OID_set my_mechs;
+ int my_mech_num;
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ int ret_stat = 0; /* 1 == success */
+ OM_uint32 req_flags = 0;
+ OM_uint32 ret_flags;
+ int type;
+ char *gssapi_auth_type = NULL;
+ struct hostent *hostinfo;
+ int len;
+
+
+ /*
+ * host is not guarenteed to be a FQDN, so we need to make sure it is.
+ */
+ hostinfo = gethostbyname(host);
+
+ if ((hostinfo == NULL) || (hostinfo->h_name == NULL)) {
+ debug("GSSAPI authentication: Unable to get FQDN for \"%s\"", host);
+ goto cleanup;
+ }
+
+ /*
+ * Default flags
+ */
+ req_flags |= GSS_C_REPLAY_FLAG;
+
+ /* Do mutual authentication */
+ req_flags |= GSS_C_MUTUAL_FLAG;
+
+#ifdef GSSAPI_KRB5
+
+ gssapi_auth_type = "GSSAPI/Kerberos 5";
+
+#endif /* GSSAPI_KRB5 */
+
+#ifdef GSI
+
+ gssapi_auth_type = "GSSAPI/GLOBUS";
+
+#endif /* GSI */
+
+ if (gssapi_auth_type == NULL) {
+ debug("No GSSAPI type defined during compile");
+ goto cleanup;
+ }
+
+ debug("Attempting %s authentication", gssapi_auth_type);
+
+ service_name = (char *) malloc(strlen(GSSAPI_SERVICE_NAME) +
+ strlen(hostinfo->h_name) +
+ 2 /* 1 for '@', 1 for NUL */);
+
+ if (service_name == NULL) {
+ debug("malloc() failed");
+ goto cleanup;
+ }
+
+
+ sprintf(service_name, "%s@%s", GSSAPI_SERVICE_NAME, hostinfo->h_name);
+
+ name_type = GSS_C_NT_HOSTBASED_SERVICE;
+
+ debug("Service name is %s", service_name);
+
+ /* Forward credentials? */
+
+#ifdef GSSAPI_KRB5
+ if (options->kerberos_tgt_passing) {
+ debug("Forwarding Kerberos credentials");
+ req_flags |= GSS_C_DELEG_FLAG;
+ }
+#endif /* GSSAPI_KRB5 */
+
+#ifdef GSI
+ if(options->forward_gssapi_globus_proxy) {
+ debug("Forwarding X509 proxy certificate");
+ req_flags |= GSS_C_DELEG_FLAG;
+ }
+#ifdef GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG
+ /* Forward limited credentials, overrides forward_gssapi_globus_proxy */
+ if(options->forward_gssapi_globus_limited_proxy) {
+ debug("Forwarding limited X509 proxy certificate");
+ req_flags |= (GSS_C_DELEG_FLAG | GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG);
+ }
+#endif /* GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG */
+
+#endif /* GSI */
+
+ debug("req_flags = %lu", req_flags);
+
+ name_tok.value = service_name;
+ name_tok.length = strlen(service_name) + 1;
+ maj_stat = gss_import_name(&min_stat, &name_tok,
+ name_type, &target_name);
+
+ free(service_name);
+ service_name = NULL;
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_gssapi_status("importing service name", maj_stat, min_stat);
+ goto cleanup;
+ }
+
+ maj_stat = gss_indicate_mechs(&min_stat, &my_mechs);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_gssapi_status("indicating mechs", maj_stat, min_stat);
+ goto cleanup;
+ }
+
+ /*
+ * Send over a packet to the daemon, letting it know we're doing
+ * GSSAPI and our mech_oid(s).
+ */
+ debug("Sending mech oid to server");
+ packet_start(SSH_CMSG_AUTH_GSSAPI);
+ packet_put_int(my_mechs->count); /* Number of mechs we're sending */
+ for (my_mech_num = 0; my_mech_num < my_mechs->count; my_mech_num++)
+ packet_put_string(my_mechs->elements[my_mech_num].elements,
+ my_mechs->elements[my_mech_num].length);
+ packet_send();
+ packet_write_wait();
+
+ /*
+ * Get reply from the daemon to see if our mech was acceptable
+ */
+ type = packet_read(&len);
+
+ switch (type) {
+ case SSH_SMSG_AUTH_GSSAPI_RESPONSE:
+ debug("Server accepted mechanism");
+ /* Successful negotiation */
+ break;
+
+ case SSH_MSG_AUTH_GSSAPI_ABORT:
+ debug("Unable to negotiate GSSAPI mechanism type with server");
+ packet_get_all();
+ goto cleanup;
+
+ default:
+ packet_disconnect("Protocol error during GSSAPI authentication:"
+ " packet type %d received",
+ type);
+ /* Does not return */
+ }
+
+ /* Read the mechanism the server returned */
+ mech_oid.elements = packet_get_string((unsigned int *) &(mech_oid.length));
+ packet_get_all();
+
+ /*
+ * Perform the context-establishement loop.
+ *
+ * On each pass through the loop, token_ptr points to the token
+ * to send to the server (or GSS_C_NO_BUFFER on the first pass).
+ * Every generated token is stored in send_tok which is then
+ * transmitted to the server; every received token is stored in
+ * recv_tok, which token_ptr is then set to, to be processed by
+ * the next call to gss_init_sec_context.
+ *
+ * GSS-API guarantees that send_tok's length will be non-zero
+ * if and only if the server is expecting another token from us,
+ * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
+ * and only if the server has another token to send us.
+ */
+
+ token_ptr = GSS_C_NO_BUFFER;
+ gss_context = GSS_C_NO_CONTEXT;
+
+ do {
+ maj_stat =
+ gss_init_sec_context(&min_stat,
+ gss_cred,
+ &gss_context,
+ target_name,
+ &mech_oid,
+ req_flags,
+ 0,
+ NULL, /* no channel bindings */
+ token_ptr,
+ NULL, /* ignore mech type */
+ &send_tok,
+ &ret_flags,
+ NULL); /* ignore time_rec */
+
+ if (token_ptr != GSS_C_NO_BUFFER)
+ (void) gss_release_buffer(&min_stat, &recv_tok);
+
+ if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
+ display_gssapi_status("initializing context", maj_stat, min_stat);
+
+ /* Send an abort message */
+ packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
+ packet_send();
+ packet_write_wait();
+
+ goto cleanup;
+ }
+
+ if (send_tok.length != 0) {
+ debug("Sending authenticaton token...");
+ packet_start(SSH_MSG_AUTH_GSSAPI_TOKEN);
+ packet_put_string((char *) send_tok.value, send_tok.length);
+ packet_send();
+ packet_write_wait();
+
+ (void) gss_release_buffer(&min_stat, &send_tok);
+ }
+
+ if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+
+ debug("Continue needed. Reading response...");
+
+ type = packet_read(&len);
+
+ switch(type) {
+
+ case SSH_MSG_AUTH_GSSAPI_TOKEN:
+ /* This is what we expected */
+ break;
+
+ case SSH_MSG_AUTH_GSSAPI_ABORT:
+ debug("Server aborted GSSAPI authentication.");
+ packet_get_all();
+ goto cleanup;
+
+ default:
+ packet_disconnect("Protocol error during GSSAPI authentication:"
+ " packet type %d received",
+ type);
+ /* Does not return */
+ }
+
+ recv_tok.value = packet_get_string((unsigned int *) &recv_tok.length);
+ packet_get_all();
+ token_ptr = &recv_tok;
+ }
+ } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+ /* Success */
+ ret_stat = 1;
+
+ debug("%s authentication successful", gssapi_auth_type);
+
+ /*
+ * Read hash of host and server keys and make sure it
+ * matches what we got earlier.
+ */
+ debug("Reading hash of server and host keys...");
+ type = packet_read(&len);
+
+ if (type == SSH_MSG_AUTH_GSSAPI_ABORT) {
+ debug("Server aborted GSSAPI authentication.");
+ packet_get_all();
+ ret_stat = 0;
+ goto cleanup;
+
+ } else if (type == SSH_SMSG_AUTH_GSSAPI_HASH) {
+ gss_buffer_desc wrapped_buf;
+ gss_buffer_desc unwrapped_buf;
+ int conf_state;
+ gss_qop_t qop_state;
+
+
+ wrapped_buf.value = packet_get_string(&(wrapped_buf.length));
+ packet_get_all();
+
+ maj_stat = gss_unwrap(&min_stat,
+ gss_context,
+ &wrapped_buf,
+ &unwrapped_buf,
+ &conf_state,
+ &qop_state);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_gssapi_status("unwraping SSHD key hash",
+ maj_stat, min_stat);
+ packet_disconnect("Verification of SSHD keys through GSSAPI-secured channel failed: "
+ "Unwrapping of hash failed.");
+ }
+
+ if (unwrapped_buf.length != sizeof(ssh_key_digest)) {
+ packet_disconnect("Verification of SSHD keys through GSSAPI-secured channel failed: "
+ "Size of key hashes do not match (%d != %d)!",
+ unwrapped_buf.length, sizeof(ssh_key_digest));
+ }
+
+ if (memcmp(ssh_key_digest, unwrapped_buf.value, sizeof(ssh_key_digest)) != 0) {
+ packet_disconnect("Verification of SSHD keys through GSSAPI-secured channel failed: "
+ "Hashes don't match!");
+ }
+
+ debug("Verified SSHD keys through GSSAPI-secured channel.");
+
+ gss_release_buffer(&min_stat, &unwrapped_buf);
+
+ } else {
+ packet_disconnect("Protocol error during GSSAPI authentication:"
+ "packet type %d received", type);
+ /* Does not return */
+ }
+
+
+ cleanup:
+ if (target_name != NULL)
+ (void) gss_release_name(&min_stat, &target_name);
+
+ return ret_stat;
+}
+
+#endif /* GSSAPI */
+
+/*end of modification*/
+
/*
* SSH1 key exchange
*/
log("Warning: This may be due to an old implementation of ssh.");
}
+/*modified by binhe*/
+#ifdef GSSAPI
+ {
+ MD5_CTX md5context;
+ Buffer buf;
+ unsigned char *data;
+ unsigned int data_len;
+
+ /*
+ * Hash the server and host keys. Later we will check them against
+ * a hash sent over a secure channel to make sure they are legit.
+ */
+ debug("Calculating MD5 hash of server and host keys...");
+
+ /* Write all the keys to a temporary buffer */
+ buffer_init(&buf);
+
+ /* Server key */
+ buffer_put_bignum(&buf, public_key->e);
+ buffer_put_bignum(&buf, public_key->n);
+
+ /* Host key */
+ buffer_put_bignum(&buf, host_key->e);
+ buffer_put_bignum(&buf, host_key->n);
+
+ /* Get the resulting data */
+ data = (unsigned char *) buffer_ptr(&buf);
+ data_len = buffer_len(&buf);
+
+ /* And hash it */
+ MD5_Init(&md5context);
+ MD5_Update(&md5context, data, data_len);
+ MD5_Final(ssh_key_digest, &md5context);
+
+ /* Clean up */
+ buffer_clear(&buf);
+ buffer_free(&buf);
+ }
+#endif /* GSSAPI */
+/*end of modification*/
+
/* Get protocol flags. */
server_flags = packet_get_int();
packet_set_protocol_flags(server_flags);
ssh_userauth1(const char *local_user, const char *server_user, char *host,
Key **keys, int nkeys)
{
+/*modified by binhe*/
+#ifdef GSSAPI
+#ifdef GSI
+ const char *save_server_user = NULL;
+#endif /* GSI */
+#endif /* GSSAPI */
+/*end of modification*/
+
#ifdef KRB5
krb5_context context = NULL;
krb5_auth_context auth_context = NULL;
if (supported_authentications == 0)
fatal("ssh_userauth1: server supports no auth methods");
+/*modified by binhe*/
+#ifdef GSSAPI
+#ifdef GSI
+ /* if no user given, tack on the subject name after the server_user.
+ * This will allow us to run gridmap early to get real user
+ * This name will start with /C=
+ */
+ if ((supported_authentications & (1 << SSH_AUTH_GSSAPI)) &&
+ options.gss_authentication) {
+ if (get_gssapi_cred()) {
+ char * retname;
+ char * newname;
+
+
+ save_server_user = server_user;
+
+ retname = get_gss_our_name();
+
+ if (retname) {
+ debug("passing gssapi name '%s'", retname);
+ if (server_user) {
+ newname = (char *) malloc(strlen(retname) + strlen(server_user) + 4);
+ if (newname) {
+ strcpy(newname, server_user);
+ if(options.user == NULL)
+ {
+ strcat(newname,":i:");
+ }
+ else
+ {
+ strcat(newname,":x:");
+ }
+ strcat(newname, retname);
+ server_user = newname;
+ free(retname);
+ }
+ }
+ }
+ } else {
+ /*
+ * If we couldn't successfully get our GSSAPI credentials then
+ * turn off gssapi authentication
+ */
+ options.gss_authentication = 0;
+ }
+ debug("server_user %s", server_user);
+ }
+#endif /* GSI */
+#endif /* GSSAPI */
+/*end of modification*/
+
/* Send the name of the user to log in as on the server. */
packet_start(SSH_CMSG_USER);
packet_put_cstring(server_user);
packet_send();
packet_write_wait();
+/*modified by binhe*/
+#if defined(GSI)
+ if(save_server_user)
+ {
+ server_user = save_server_user;
+ }
+#endif
+/*end of modification*/
/*
* The server should respond with success if no authentication is
* needed (the user has no password). Otherwise the server responds
goto success;
if (type != SSH_SMSG_FAILURE)
packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", type);
+
+/*modified by binhe*/
+#ifdef GSSAPI
+ /* Try GSSAPI authentication */
+ if ((supported_authentications & (1 << SSH_AUTH_GSSAPI)) &&
+ options.gss_authentication)
+ {
+ debug("Trying GSSAPI authentication (%s)...", gssapi_patch_version);
+ try_gssapi_authentication(host, &options);
+
+ /*
+ * XXX Hmmm. Kerberos authentication only reads a packet if it thinks
+ * the authentication went OK, but the server seems to always send
+ * a packet back. So I'm not sure if I'm missing something or
+ * the Kerberos code is broken. - vwelch 1/27/99
+ */
+
+ type = packet_read(&payload_len);
+ if (type == SSH_SMSG_SUCCESS)
+ return; /* Successful connection. */
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error: got %d in response to Kerberos auth", type);
+
+ debug("GSSAPI authentication failed");
+ }
+#endif /* GSSAPI */
+/*end of modification*/
#ifdef KRB5
if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) &&
#include "ssh-gss.h"
#endif
+/*modified by binhe*/
+#ifdef GSSAPI
+#include <openssl/md5.h>
+#include "bufaux.h"
+#endif /* GSSAPI */
+/*end of modification*/
+
#ifdef LIBWRAP
#include <tcpd.h>
#include <syslog.h>
if (test_flag)
exit(0);
+/*modified by binhe*/
+#ifdef GSSAPI
+ gssapi_clean_env();
+#endif /* GSSAPI */
+/*end of modification*/
+
#ifdef HAVE_SCO_PROTECTED_PW
(void) set_auth_parameters(ac, av);
#endif
#endif
if (options.challenge_response_authentication == 1)
auth_mask |= 1 << SSH_AUTH_TIS;
+
+/*modified by binhe*/
+#ifdef GSSAPI
+ if (options.gss_authentication)
+ auth_mask |= 1 << SSH_AUTH_GSSAPI;
+#endif
+/*end of modification*/
+
if (options.password_authentication)
auth_mask |= 1 << SSH_AUTH_PASSWORD;
packet_put_int(auth_mask);
for (i = 0; i < 16; i++)
session_id[i] = session_key[i] ^ session_key[i + 16];
}
+
+/*modified by binhe*/
+#ifdef GSSAPI
+ /*
+ * Before we destroy the host and server keys, hash them so we can
+ * send the hash over to the client via a secure channel so that it
+ * can verify them.
+ */
+ {
+ MD5_CTX md5context;
+ Buffer buf;
+ unsigned char *data;
+ unsigned int data_len;
+ extern unsigned char ssh_key_digest[]; /* in auth_gssapi.c */
+
+
+ debug("Calculating MD5 hash of server and host keys...");
+
+ /* Write all the keys to a temporary buffer */
+ buffer_init(&buf);
+
+ /* Server key */
+ buffer_put_bignum(&buf, sensitive_data.server_key->rsa->e);
+ buffer_put_bignum(&buf, sensitive_data.server_key->rsa->n);
+
+ /* Host key */
+ buffer_put_bignum(&buf, sensitive_data.ssh1_host_key->rsa->e);
+ buffer_put_bignum(&buf, sensitive_data.ssh1_host_key->rsa->n);
+
+ /* Get the resulting data */
+ data = (unsigned char *) buffer_ptr(&buf);
+ data_len = buffer_len(&buf);
+
+ /* And hash it */
+ MD5_Init(&md5context);
+ MD5_Update(&md5context, data, data_len);
+ MD5_Final(ssh_key_digest, &md5context);
+
+ /* Clean up */
+ buffer_clear(&buf);
+ buffer_free(&buf);
+ }
+#endif /* GSSAPI */
+/*end of modification*/
+
/* Destroy the private and public keys. They will no longer be needed. */
destroy_sensitive_data();