allow prompting for GSS creds gss-password-prompt
authorLuke Howard <lukeh@padl.com>
Fri, 28 Oct 2011 23:32:10 +0000 (10:32 +1100)
committerLuke Howard <lukeh@padl.com>
Sat, 29 Oct 2011 06:48:20 +0000 (17:48 +1100)
gss-genr.c
readconf.c
readconf.h
sshconnect2.c

index f9b39cf..69bf82d 100644 (file)
 #include "cipher.h"
 #include "key.h"
 #include "kex.h"
+#include "misc.h"
+#include "ssh.h"
+#include "readconf.h"
 #include <openssl/evp.h>
 
 #include "ssh-gss.h"
 
 extern u_char *session_id2;
 extern u_int session_id2_len;
+extern Options options;
 
 typedef struct {
        char *encoded;
@@ -63,6 +67,17 @@ Gssctxt *gss_kex_context = NULL;
 
 static ssh_gss_kex_mapping *gss_enc2oid = NULL;
 
+static char *gss_password = NULL;
+
+static void
+ssh_gssapi_cleanup_password(void)
+{
+       if (gss_password) {
+               memset(gss_password, 0, strlen(gss_password));
+               xfree(gss_password);
+       }
+}
+
 int 
 ssh_gssapi_oid_table_ok() {
        return (gss_enc2oid != NULL);
@@ -78,9 +93,20 @@ ssh_gssapi_oid_table_ok() {
 char *
 ssh_gssapi_client_mechanisms(const char *host, const char *client) {
        gss_OID_set gss_supported;
-       OM_uint32 min_status;
+       OM_uint32 maj_status, min_status;
+
+       if (options.gss_mechanism_oid) {
+               maj_status = gss_create_empty_oid_set(&min_status,
+                                                     &gss_supported);
+               if (!GSS_ERROR(maj_status))
+                       maj_status = gss_add_oid_set_member(&min_status,
+                                                           options.gss_mechanism_oid,
+                                                           &gss_supported);
+       } else {
+               maj_status = gss_indicate_mechs(&min_status, &gss_supported);
+       }
 
-       if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
+       if (GSS_ERROR(maj_status))
                return NULL;
 
        return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
@@ -398,18 +424,51 @@ ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
        ctx->major = gss_import_name(&ctx->minor, &gssbuf,
            GSS_C_NT_USER_NAME, &gssname);
 
-       if (!ctx->major)
-               ctx->major = gss_acquire_cred(&ctx->minor, 
-                   gssname, 0, oidset, GSS_C_INITIATE, 
-                   &ctx->client_creds, NULL, NULL);
+       if (GSS_ERROR(ctx->major)) {
+               ssh_gssapi_error(ctx);
+               return ctx->major;
+       }
+
+       if (options.gss_password_prompt) {
+               char prompt[150];
+
+               if (!gss_password) {
+                       snprintf(prompt, sizeof(prompt), "%.30s's password: ", name);
+                       gss_password = read_passphrase(prompt, 0);
+
+                       atexit(ssh_gssapi_cleanup_password);
+               }
+
+               gssbuf.value = gss_password;
+               gssbuf.length = strlen(gss_password);
+
+               ctx->major = gss_acquire_cred_with_password(&ctx->minor,
+                                                           gssname,
+                                                           &gssbuf,
+                                                           GSS_C_INDEFINITE,
+                                                           oidset,
+                                                           GSS_C_INITIATE,
+                                                           &ctx->client_creds,
+                                                           NULL,
+                                                           NULL);
+       } else {
+               ctx->major = gss_acquire_cred(&ctx->minor,
+                                             gssname,
+                                             GSS_C_INDEFINITE,
+                                             oidset,
+                                             GSS_C_INITIATE,
+                                             &ctx->client_creds,
+                                             NULL,
+                                             NULL);
+       }
 
        gss_release_name(&status, &gssname);
        gss_release_oid_set(&status, &oidset);
 
-       if (ctx->major)
+       if (GSS_ERROR(ctx->major))
                ssh_gssapi_error(ctx);
 
-       return(ctx->major);
+       return ctx->major;
 }
 
 OM_uint32
@@ -462,8 +521,12 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
        if (ctx == NULL)
                ctx = &intctx;
 
-       /* RFC 4462 says we MUST NOT do SPNEGO */
-       if (oid->length == spnego_oid.length && 
+       /*
+        * RFC 4462 says we MUST NOT do SPNEGO, but we relax that if
+        * the SPNEGO mechanism was explicitly specified by the user.
+        */
+       if (options.gss_mechanism_oid == GSS_C_NO_OID &&
+           oid->length == spnego_oid.length && 
            (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0))
                return 0; /* false */
 
@@ -545,5 +608,4 @@ ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
 
        return 0;
 }
-
 #endif /* GSSAPI */
index 60befde..179413a 100644 (file)
@@ -44,6 +44,9 @@
 #include "buffer.h"
 #include "kex.h"
 #include "mac.h"
+#if defined(GSSAPI)
+#include "ssh-gss.h"
+#endif
 
 /* Format of the configuration file:
 
@@ -130,7 +133,7 @@ typedef enum {
        oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
        oAddressFamily, oGssAuthentication, oGssDelegateCreds,
        oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
-       oGssServerIdentity, 
+       oGssServerIdentity, oGssPasswordPrompt, oGssMechanismOid,
        oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
        oSendEnv, oControlPath, oControlMaster, oControlPersist,
        oHashKnownHosts,
@@ -177,6 +180,8 @@ static struct {
        { "gssapiclientidentity", oGssClientIdentity },
        { "gssapiserveridentity", oGssServerIdentity },
        { "gssapirenewalforcesrekey", oGssRenewalRekey },
+       { "gssapipasswordprompt", oGssPasswordPrompt },
+       { "gssapimechanismoid", oGssMechanismOid },
 #else
        { "gssapiauthentication", oUnsupported },
        { "gssapikeyexchange", oUnsupported },
@@ -184,6 +189,8 @@ static struct {
        { "gssapitrustdns", oUnsupported },
        { "gssapiclientidentity", oUnsupported },
        { "gssapirenewalforcesrekey", oUnsupported },
+       { "gssapipasswordprompt", oUnsupported },
+       { "gssapimechanismoid", oUnsupported },
 #endif
        { "fallbacktorsh", oDeprecated },
        { "usersh", oDeprecated },
@@ -261,6 +268,11 @@ static struct {
        { NULL, oBadOption }
 };
 
+#ifdef GSSAPI
+static gss_OID
+mechanism_oid(const char *arg);
+#endif
+
 /*
  * Adds a local TCP/IP port forward to options.  Never returns if there is an
  * error.
@@ -517,6 +529,30 @@ parse_flag:
                intptr = &options->gss_renewal_rekey;
                goto parse_flag;
 
+       case oGssPasswordPrompt:
+               intptr = &options->gss_password_prompt;
+               goto parse_flag;
+
+#ifdef GSSAPI
+       case oGssMechanismOid: {
+               OM_uint32 minor;
+               gss_OID oid;
+
+               arg = strdelim(&s);
+               if (!arg || *arg == '\0')
+                       fatal("%.200s line %d: Missing argument.", filename, linenum);
+               oid = mechanism_oid(arg);
+               if (oid == GSS_C_NO_OID)
+                       fatal("%.200s line %d: Bad GSS mechanism OID '%s'.",
+                           filename, linenum, arg ? arg : "<NONE>");
+               if (*activep && options->gss_mechanism_oid == GSS_C_NO_OID)
+                       options->gss_mechanism_oid = oid;
+               else
+                       gss_release_oid(&minor, &oid);
+               break;
+       }
+#endif
+
        case oBatchMode:
                intptr = &options->batch_mode;
                goto parse_flag;
@@ -1175,6 +1211,8 @@ initialize_options(Options * options)
        options->gss_renewal_rekey = -1;
        options->gss_client_identity = NULL;
        options->gss_server_identity = NULL;
+       options->gss_password_prompt = -1;
+       options->gss_mechanism_oid = NULL;
        options->password_authentication = -1;
        options->kbd_interactive_authentication = -1;
        options->kbd_interactive_devices = NULL;
@@ -1282,6 +1320,8 @@ fill_default_options(Options * options)
                options->gss_trust_dns = 0;
        if (options->gss_renewal_rekey == -1)
                options->gss_renewal_rekey = 0;
+       if (options->gss_password_prompt == -1)
+               options->gss_password_prompt = 0;
        if (options->password_authentication == -1)
                options->password_authentication = 1;
        if (options->kbd_interactive_authentication == -1)
@@ -1513,3 +1553,37 @@ parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd)
        }
        return (0);
 }
+
+#ifdef GSSAPI
+static gss_OID
+mechanism_oid(const char *oidstr)
+{
+       OM_uint32 minor;
+       gss_buffer_desc oidBuf;
+       size_t oidstrLen, i;
+       char *p;
+       gss_OID ret = GSS_C_NO_OID;
+
+       oidstrLen = strlen(oidstr);
+
+       oidBuf.length = 2 + oidstrLen + 2;
+       oidBuf.value = xmalloc(oidBuf.length + 1);
+       if (oidBuf.value == NULL)
+               return NULL;
+
+       p = (char *)oidBuf.value;
+       *p++ = '{';
+       *p++ = ' ';
+       for (i = 0; i < oidstrLen; i++)
+               *p++ = oidstr[i] == '.' ? ' ' : oidstr[i];
+       *p++ = ' ';
+       *p++ = '}';
+       *p = '\0';
+
+       gss_str_to_oid(&minor, &oidBuf, &ret);
+
+       xfree(oidBuf.value);
+
+       return ret;
+}
+#endif
index 617686f..00a2218 100644 (file)
@@ -51,8 +51,10 @@ typedef struct {
        int     gss_deleg_creds;        /* Delegate GSS credentials */
        int     gss_trust_dns;          /* Trust DNS for GSS canonicalization */
        int     gss_renewal_rekey;      /* Credential renewal forces rekey */
+       int     gss_password_prompt;    /* Prompt and acquire cred with password */
        char    *gss_client_identity;   /* Principal to initiate GSSAPI with */
        char    *gss_server_identity;   /* GSSAPI target principal */
+       void    *gss_mechanism_oid;     /* GSS mechanism OID */
        int     password_authentication;        /* Try password
                                                 * authentication. */
        int     kbd_interactive_authentication; /* Try keyboard-interactive auth. */
index 3ddef32..a358345 100644 (file)
@@ -686,7 +686,7 @@ userauth_gssapi(Authctxt *authctxt)
        Gssctxt *gssctxt = NULL;
        static gss_OID_set gss_supported = NULL;
        static u_int mech = 0;
-       OM_uint32 min;
+       OM_uint32 maj, min;
        int ok = 0;
        const char *gss_host;
 
@@ -700,11 +700,21 @@ userauth_gssapi(Authctxt *authctxt)
        /* Try one GSSAPI method at a time, rather than sending them all at
         * once. */
 
-       if (gss_supported == NULL)
-               if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
+       if (gss_supported == NULL) {
+               if (options.gss_mechanism_oid) {
+                       maj = gss_create_empty_oid_set(&min, &gss_supported);
+                       if (!GSS_ERROR(maj))
+                               maj = gss_add_oid_set_member(&min,
+                                                    options.gss_mechanism_oid,
+                                                    &gss_supported);
+               } else {
+                       maj = gss_indicate_mechs(&min, &gss_supported);
+               }
+               if (GSS_ERROR(maj)) {
                        gss_supported = NULL;
                        return 0;
                }
+       }
 
        /* Check to see if the mechanism is usable before we offer it */
        while (mech < gss_supported->count && !ok) {