]> andersk Git - gssapi-openssh.git/commitdiff
initial checkin of Bin He's support for GSI authentication in protocol 1,
authorjbasney <jbasney>
Mon, 11 Mar 2002 22:40:45 +0000 (22:40 +0000)
committerjbasney <jbasney>
Mon, 11 Mar 2002 22:40:45 +0000 (22:40 +0000)
based on Von Welch's old SSH patch

13 files changed:
openssh/Makefile.in
openssh/auth-gssapi.c [new file with mode: 0644]
openssh/auth1.c
openssh/configure.ac
openssh/packet.c
openssh/packet.h
openssh/readconf.c
openssh/readconf.h
openssh/ssh.c
openssh/ssh.h
openssh/ssh1.h
openssh/sshconnect1.c
openssh/sshd.c

index f4ab6f0be1a3b16eddd69f33b63f91effc0a79e3..dd512f3d47e91c26bbb2283e503b19e3f46bf177 100644 (file)
@@ -50,7 +50,7 @@ LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels
 
 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
diff --git a/openssh/auth-gssapi.c b/openssh/auth-gssapi.c
new file mode 100644 (file)
index 0000000..1868869
--- /dev/null
@@ -0,0 +1,1030 @@
+/*
+ * 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 */
+}
+               
+
+    
index 1fbfad90ae8c0da76590e57724e58b5164898052..dd59a7454a61b97dd2e0e626890b89ce8f051c31 100644 (file)
@@ -26,6 +26,12 @@ RCSID("$OpenBSD: auth1.c,v 1.25 2001/06/26 16:15:23 dugsong Exp $");
 #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;
 
@@ -156,6 +162,31 @@ do_authloop(Authctxt *authctxt)
                        }
                        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. */
@@ -375,6 +406,21 @@ do_authentication()
        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';
 
index 717d19d08f7c021ff7085293f132ca49eea06abd..b51181d95bc4b9d8c0c827ca68d5373d5e292154 100644 (file)
@@ -1645,6 +1645,92 @@ AC_ARG_WITH(kerberos4,
        ]
 )
 
+# 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,
index 7b94169a34eff0d1e3faeee21e8fc6300c5b9e2b..49c6e7a459838318f46dbb7f0912a4edb1ffad5f 100644 (file)
@@ -1024,6 +1024,15 @@ packet_get_string(u_int *length_ptr)
        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
index d5473001c5041ec78a0312c6779fb1af0b0a085d..4fbfee350d4c05827e878fcc844b7d29558e2666 100644 (file)
@@ -51,6 +51,9 @@ void     packet_get_bignum(BIGNUM * value, int *length_ptr);
 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)));
 
index 1f35670d33739e7e62227d6ccbc324aec81b54da..4243c295fb64b7e3af4af350318d94d584e946c6 100644 (file)
@@ -788,6 +788,10 @@ initialize_options(Options * options)
         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 */
 
@@ -879,6 +883,12 @@ fill_default_options(Options * options)
 #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)
index 6a3823b02b78e19d26f8d1fa376c6c0bb08067a8..6983644874b36bf720d790d3d2c34bbd7745739f 100644 (file)
@@ -53,6 +53,10 @@ typedef struct {
        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 */
 
index 2984a597fb88c200042e137c9f62ae49ea24414a..e02a2ae846a2ecffabbe07b92e846d4d3165a487 100644 (file)
@@ -347,6 +347,13 @@ again:
                        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;
index 383c7fe9b1c9214225718f62c951d0bbb5063d6f..877d271d5dd02da905a14c5e97b3d6ddc92059c5 100644 (file)
 # 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.
index 98d1dc9303cc8627019ad864a6cf4ad224270d1a..72c47f9d3c82d5bfba54eb5314345c568e1a924d 100644 (file)
 #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 */
index 1c35f0319783aecb7ee6c700268b0f8f0801097c..e0c3676c2abbf3a9974ecf344a949652aaf44793 100644 (file)
@@ -51,6 +51,20 @@ RCSID("$OpenBSD: sshconnect1.c,v 1.41 2001/10/06 11:18:19 markus Exp $");
 #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;
@@ -961,6 +975,441 @@ try_password_authentication(char *prompt)
        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
  */
@@ -1023,6 +1472,47 @@ ssh_kex(char *host, struct sockaddr *hostaddr)
                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);
@@ -1168,6 +1658,14 @@ void
 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;
@@ -1178,12 +1676,71 @@ ssh_userauth1(const char *local_user, const char *server_user, char *host,
        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
@@ -1196,6 +1753,33 @@ ssh_userauth1(const char *local_user, const char *server_user, char *host,
                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)) &&
index 5c7d8d0f5322862e76b1d669d218881c1a8e58da..6a4d6e56cecd718cd24aeefedf9b99af59eb675d 100644 (file)
@@ -77,6 +77,13 @@ RCSID("$OpenBSD: sshd.c,v 1.209 2001/11/10 13:19:45 markus Exp $");
 #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>
@@ -782,6 +789,12 @@ main(int ac, char **av)
        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
@@ -1302,6 +1315,14 @@ do_ssh1_kex(void)
 #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);
@@ -1427,6 +1448,51 @@ do_ssh1_kex(void)
                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();
 
This page took 0.549973 seconds and 5 git commands to generate.