# it to compile under development environments which do not
# support IPv6 in their header files.
#
+# - COMPAT=/DNO_GSSAPI
+# Disables PuTTY's ability to use GSSAPI functions for
+# authentication and key exchange.
+#
# - COMPAT=/DMSVC4 (Windows only)
# - RCFL=/DMSVC4
# Makes a couple of minor changes so that PuTTY compiles using
# Add VER to Windows resource targets, and force them to be rebuilt every
# time, on the assumption that they will contain version information.
!begin vc vars
+CFLAGS = $(CFLAGS) /DHAS_GSSAPI
RCFLAGS = $(RCFLAGS) $(VER)
!end
!begin cygwin vars
SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshsh256 sshsh512 sshbn wildcard pinger ssharcf
-WINSSH = SSH winnoise winpgntc
-UXSSH = SSH uxnoise uxagentc
+WINSSH = SSH winnoise winpgntc wingss
+UXSSH = SSH uxnoise uxagentc uxgss
MACSSH = SSH macnoise
# SFTP implementation (pscp, psftp).
dlg_stdcheckbox_handler,
I(offsetof(Config,try_ki_auth)));
+#ifndef NO_GSSAPI
+ ctrl_checkbox(s, "Attempt GSSAPI auth (SSH-2)",
+ NO_SHORTCUT, HELPCTX(no_help),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,try_gssapi_auth)));
+#endif
+
s = ctrl_getset(b, "Connection/SSH/Auth", "params",
"Authentication parameters");
ctrl_checkbox(s, "Allow agent forwarding", 'f',
HELPCTX(ssh_auth_changeuser),
dlg_stdcheckbox_handler,
I(offsetof(Config,change_username)));
+#ifndef NO_GSSAPI
+ ctrl_checkbox(s, "Allow GSSAPI credential delegation in SSH-2", NO_SHORTCUT,
+ HELPCTX(no_help),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,gssapifwd)));
+#endif
ctrl_filesel(s, "Private key file for authentication:", 'k',
FILTER_KEY_FILES, FALSE, "Select private key file",
HELPCTX(ssh_auth_privkey),
"# You can define this path to point at your tools if you need to\n".
"# TOOLPATH = /opt/gcc/bin\n".
"CC = \$(TOOLPATH)cc\n".
+ "# If necessary set the path to krb5-config here\n".
+ "KRB5CONFIG=krb5-config\n".
"# You can manually set this to `gtk-config' or `pkg-config gtk+-1.2'\n".
"# (depending on what works on your system) if you want to enforce\n".
"# building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0'\n".
" -D _FILE_OFFSET_BITS=64\n".
"XLDFLAGS = \$(LDFLAGS) `\$(GTK_CONFIG) --libs`\n".
"ULDFLAGS = \$(LDFLAGS)\n".
+ "ifeq (,\$(findstring NO_GSSAPI,\$(COMPAT)))\n".
+ "CFLAGS+= `\$(KRB5CONFIG) --cflags gssapi`\n".
+ "XLDFLAGS+= `\$(KRB5CONFIG) --libs gssapi`\n".
+ "ULDFLAGS = `\$(KRB5CONFIG) --libs gssapi`\n".
+ "endif\n";
"INSTALL=install\n",
"INSTALL_PROGRAM=\$(INSTALL)\n",
"INSTALL_DATA=\$(INSTALL)\n",
int ssh_no_userauth; /* bypass "ssh-userauth" (SSH-2 only) */
int try_tis_auth;
int try_ki_auth;
+ int try_gssapi_auth; /* attempt gssapi auth */
+ int gssapifwd; /* forward tgt via gss */
int ssh_subsys; /* run a subsystem rather than a command */
int ssh_subsys2; /* fallback to go with remote_cmd_ptr2 */
int ssh_no_shell; /* avoid running a shell */
write_setting_i(sesskey, "Compression", cfg->compression);
write_setting_i(sesskey, "TryAgent", cfg->tryagent);
write_setting_i(sesskey, "AgentFwd", cfg->agentfwd);
+ write_setting_i(sesskey, "GssapiFwd", cfg->gssapifwd);
write_setting_i(sesskey, "ChangeUsername", cfg->change_username);
wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX,
cfg->ssh_cipherlist);
write_setting_i(sesskey, "SshNoAuth", cfg->ssh_no_userauth);
write_setting_i(sesskey, "AuthTIS", cfg->try_tis_auth);
write_setting_i(sesskey, "AuthKI", cfg->try_ki_auth);
+ write_setting_i(sesskey, "AuthGSSAPI", cfg->try_gssapi_auth);
write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell);
write_setting_i(sesskey, "SshProt", cfg->sshprot);
write_setting_s(sesskey, "LogHost", cfg->loghost);
gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n",
cfg->proxy_telnet_command, sizeof(cfg->proxy_telnet_command));
gppmap(sesskey, "Environment", "", cfg->environmt, lenof(cfg->environmt));
- gpps(sesskey, "UserName", "", cfg->username, sizeof(cfg->username));
+ gpps(sesskey, "UserName", get_username(), cfg->username, sizeof(cfg->username));
gpps(sesskey, "LocalUserName", "", cfg->localusername,
sizeof(cfg->localusername));
gppi(sesskey, "NoPTY", 0, &cfg->nopty);
gppi(sesskey, "TryAgent", 1, &cfg->tryagent);
gppi(sesskey, "AgentFwd", 0, &cfg->agentfwd);
gppi(sesskey, "ChangeUsername", 0, &cfg->change_username);
+ gppi(sesskey, "GssapiFwd", 0, &cfg->gssapifwd);
gprefs(sesskey, "Cipher", "\0",
ciphernames, CIPHER_MAX, cfg->ssh_cipherlist);
{
gppi(sesskey, "SshNoAuth", 0, &cfg->ssh_no_userauth);
gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth);
gppi(sesskey, "AuthKI", 1, &cfg->try_ki_auth);
+ gppi(sesskey, "AuthGSSAPI", 1, &cfg->try_gssapi_auth);
gppi(sesskey, "SshNoShell", 0, &cfg->ssh_no_shell);
gppfile(sesskey, "PublicKeyFile", &cfg->keyfile);
gpps(sesskey, "RemoteCommand", "", cfg->remote_cmd,
#include "putty.h"
#include "tree234.h"
#include "ssh.h"
+#include "sshgss.h"
#ifndef FALSE
#define FALSE 0
#define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */
#define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */
#define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */
+#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60
+#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61
+#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63
+#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64
+#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65
+#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66
/*
* Packet type contexts, so that ssh2_pkt_type can correctly decode
SSH2_PKTCTX_NOAUTH,
SSH2_PKTCTX_PUBLICKEY,
SSH2_PKTCTX_PASSWORD,
+ SSH2_PKTCTX_GSSAPI,
SSH2_PKTCTX_KBDINTER
} Pkt_ACtx;
}
static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
{
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI);
translate(SSH2_MSG_DISCONNECT);
translate(SSH2_MSG_IGNORE);
translate(SSH2_MSG_UNIMPLEMENTED);
int kex_in_progress;
long next_rekey, last_rekey;
char *deferred_rekey_reason; /* points to STATIC string; don't free */
+
+ /*
+ * Fully qualified host name, which we need if doing GSSAPI.
+ */
+ char *fullhostname;
};
#define logevent(s) logevent(ssh->frontend, s)
sk_addr_free(addr);
return err;
}
+ ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */
/*
* Open socket.
AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
AUTH_TYPE_PUBLICKEY_OFFER_QUIET,
AUTH_TYPE_PASSWORD,
+ AUTH_TYPE_GSSAPI,
AUTH_TYPE_KEYBOARD_INTERACTIVE,
AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
} type;
int done_service_req;
int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter;
int tried_pubkey_config, done_agent;
+ int can_gssapi;
+ int tried_gssapi;
int kbd_inter_refused;
int we_are_in;
prompts_t *cur_prompt;
int try_send;
int num_env, env_left, env_ok;
struct Packet *pktout;
+ Ssh_gss_ctx gss_ctx;
+ Ssh_gss_buf gss_buf;
+ Ssh_gss_buf gss_rcvtok, gss_sndtok;
+ Ssh_gss_name gss_srv_name;
+ Ssh_gss_stat gss_stat;
};
crState(do_ssh2_authconn_state);
s->done_service_req = FALSE;
s->we_are_in = FALSE;
+ s->tried_gssapi = FALSE;
+
if (!ssh->cfg.ssh_no_userauth) {
/*
* Request userauth protocol, and await a response to it.
break;
}
- if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) {
+ if (pktin->type != SSH2_MSG_USERAUTH_FAILURE && s->type != AUTH_TYPE_GSSAPI) {
bombout(("Strange packet received during authentication: "
"type %d", pktin->type));
crStopV;
in_commasep_string("password", methods, methlen);
s->can_keyb_inter = ssh->cfg.try_ki_auth &&
in_commasep_string("keyboard-interactive", methods, methlen);
+#ifndef NO_GSSAPI
+ s->can_gssapi = ssh->cfg.try_gssapi_auth &&
+ in_commasep_string("gssapi-with-mic", methods, methlen) &&
+ ssh_gss_init();
+#endif
}
ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
key->alg->freekey(key->data);
}
+#ifndef NO_GSSAPI
+ } else if (s->can_gssapi && !s->tried_gssapi) {
+
+ /* GSSAPI Authentication */
+
+ int micoffset;
+ Ssh_gss_buf mic;
+ s->type = AUTH_TYPE_GSSAPI;
+ s->tried_gssapi = TRUE;
+ s->gotit = TRUE;
+ ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;
+
+ /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ ssh2_pkt_addstring(s->pktout, s->username);
+ ssh2_pkt_addstring(s->pktout, "ssh-connection");
+ ssh2_pkt_addstring(s->pktout, "gssapi-with-mic");
+
+ /* add mechanism info */
+ ssh_gss_indicate_mech(&s->gss_buf);
+
+ /* number of GSSAPI mechanisms */
+ ssh2_pkt_adduint32(s->pktout,1);
+
+ /* length of OID + 2 */
+ ssh2_pkt_adduint32(s->pktout, s->gss_buf.len + 2);
+ ssh2_pkt_addbyte(s->pktout, SSH2_GSS_OIDTYPE);
+
+ /* length of OID */
+ ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.len);
+
+ ssh_pkt_adddata(s->pktout, s->gss_buf.data, s->gss_buf.len);
+ ssh2_pkt_send(ssh, s->pktout);
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) {
+ logevent("GSSAPI authentication request refused");
+ continue;
+ }
+
+ /* check returned packet ... */
+
+ ssh_pkt_getstring(pktin,&s->gss_rcvtok.data,&s->gss_rcvtok.len);
+ if (s->gss_rcvtok.len != s->gss_buf.len + 2 ||
+ s->gss_rcvtok.data[0] != SSH2_GSS_OIDTYPE ||
+ s->gss_rcvtok.data[1] != s->gss_buf.len ||
+ memcmp(s->gss_rcvtok.data+2,s->gss_buf.data,s->gss_buf.len) ) {
+ logevent("GSSAPI authentication - wrong response from server");
+ continue;
+ }
+
+ /* now start running */
+ s->gss_stat = ssh_gss_import_name(ssh->fullhostname,
+ &s->gss_srv_name);
+ if (s->gss_stat != SSH_GSS_OK) {
+ if (s->gss_stat == SSH_GSS_BAD_HOST_NAME)
+ logevent("GSSAPI import name failed - Bad service name");
+ else
+ logevent("GSSAPI import name failed");
+ continue;
+ }
+
+ /* fetch TGT into GSS engine */
+ s->gss_stat = ssh_gss_acquire_cred(&s->gss_ctx);
+
+ if (s->gss_stat != SSH_GSS_OK) {
+ logevent("GSSAPI authentication failed to get credentials");
+ ssh_gss_release_name(&s->gss_srv_name);
+ continue;
+ }
+
+ /* initial tokens are empty */
+ s->gss_rcvtok.len = s->gss_sndtok.len = 0;
+ s->gss_rcvtok.data = s->gss_sndtok.data = NULL;
+
+ /* now enter the loop */
+ do {
+ s->gss_stat = ssh_gss_init_sec_context(&s->gss_ctx,
+ s->gss_srv_name,
+ ssh->cfg.gssapifwd,
+ &s->gss_rcvtok,
+ &s->gss_sndtok);
+
+ if (s->gss_stat!=SSH_GSS_S_COMPLETE &&
+ s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) {
+ logevent("GSSAPI authentication initialisation failed");
+
+ if (ssh_gss_display_status(s->gss_ctx,&s->gss_buf) == SSH_GSS_OK) {
+ logevent(s->gss_buf.data);
+ sfree(s->gss_buf.data);
+ }
+
+ break;
+ }
+ logevent("GSSAPI authentication initialised");
+
+ /* Client and server now exchange tokens until GSSAPI
+ * no longer says CONTINUE_NEEDED */
+
+ if (s->gss_sndtok.len != 0) {
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+ ssh_pkt_addstring_start(s->pktout);
+ ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.data,s->gss_sndtok.len);
+ ssh2_pkt_send(ssh, s->pktout);
+ ssh_gss_free_tok(&s->gss_sndtok);
+ }
+
+ if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_TOKEN) {
+ logevent("GSSAPI authentication - bad server response");
+ s->gss_stat = SSH_GSS_FAILURE;
+ break;
+ }
+ ssh_pkt_getstring(pktin,&s->gss_rcvtok.data,&s->gss_rcvtok.len);
+ }
+ } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
+
+ if (s->gss_stat != SSH_GSS_OK) {
+ ssh_gss_release_name(&s->gss_srv_name);
+ ssh_gss_release_cred(&s->gss_ctx);
+ continue;
+ }
+ logevent("GSSAPI authentication loop finished OK");
+
+ /* Now send the MIC */
+
+ s->pktout = ssh2_pkt_init(0);
+ micoffset = s->pktout->length;
+ ssh_pkt_addstring_start(s->pktout);
+ ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len);
+ ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST);
+ ssh_pkt_addstring(s->pktout, s->username);
+ ssh_pkt_addstring(s->pktout, "ssh-connection");
+ ssh_pkt_addstring(s->pktout, "gssapi-with-mic");
+
+ s->gss_buf.data = (char *)s->pktout->data + micoffset;
+ s->gss_buf.len = s->pktout->length - micoffset;
+
+ ssh_gss_get_mic(s->gss_ctx, &s->gss_buf, &mic);
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC);
+ ssh_pkt_addstring_start(s->pktout);
+ ssh_pkt_addstring_data(s->pktout, mic.data, mic.len);
+ ssh2_pkt_send(ssh, s->pktout);
+ ssh_gss_free_mic(&mic);
+
+ s->gotit = FALSE;
+
+ ssh_gss_release_name(&s->gss_srv_name);
+ ssh_gss_release_cred(&s->gss_ctx);
+ continue;
+#endif
} else if (s->can_keyb_inter && !s->kbd_inter_refused) {
/*
sfree(ssh->do_ssh2_authconn_state);
sfree(ssh->v_c);
sfree(ssh->v_s);
+ sfree(ssh->fullhostname);
if (ssh->crcda_ctx) {
crcda_free_context(ssh->crcda_ctx);
ssh->crcda_ctx = NULL;
--- /dev/null
+#define SSH2_GSS_OIDTYPE 0x06
+typedef void *Ssh_gss_ctx;
+typedef void *Ssh_gss_name;
+
+typedef enum Ssh_gss_stat {
+ SSH_GSS_OK = 0,
+ SSH_GSS_S_CONTINUE_NEEDED,
+ SSH_GSS_NO_MEM,
+ SSH_GSS_BAD_HOST_NAME,
+ SSH_GSS_FAILURE
+} Ssh_gss_stat;
+
+#define SSH_GSS_S_COMPLETE SSH_GSS_OK
+
+typedef struct Ssh_gss_buf {
+ int len;
+ char *data;
+} Ssh_gss_buf;
+
+#define SSH_GSS_EMPTY_BUF (Ssh_gss_buf) {0,NULL}
+
+#define SSH_GSS_CLEAR_BUF(buf) do { \
+ (*buf).len = 0; \
+ (*buf).data = NULL; \
+} while (0)
+
+/* Functions, provided by either wingss.c or uxgss.c */
+
+/*
+ * Do startup-time initialisation for using GSSAPI. (On Windows,
+ * for instance, this dynamically loads the GSSAPI DLL and
+ * retrieves some function pointers.)
+ *
+ * Return value is 1 on success, or 0 if initialisation failed.
+ *
+ * May be called multiple times (since the most convenient place
+ * to call it _from_ is the ssh.c setup code), and will harmlessly
+ * return success if already initialised.
+ */
+int ssh_gss_init(void);
+
+/*
+ * Fills in buf with a string describing the GSSAPI mechanism in
+ * use. buf->data is not dynamically allocated.
+ */
+Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *buf);
+
+/*
+ * Converts a name such as a hostname into a GSSAPI internal form,
+ * which is placed in "out". The result should be freed by
+ * ssh_gss_release_name().
+ */
+Ssh_gss_stat ssh_gss_import_name(char *in, Ssh_gss_name *out);
+
+/*
+ * Frees the contents of an Ssh_gss_name structure filled in by
+ * ssh_gss_import_name().
+ */
+Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *name);
+
+/*
+ * The main GSSAPI security context setup function. The "out"
+ * parameter will need to be freed by ssh_gss_free_tok.
+ */
+Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate,
+ Ssh_gss_buf *in, Ssh_gss_buf *out);
+
+/*
+ * Frees the contents of an Ssh_gss_buf filled in by
+ * ssh_gss_init_sec_context(). Do not accidentally call this on
+ * something filled in by ssh_gss_get_mic() (which requires a
+ * different free function) or something filled in by any other
+ * way.
+ */
+Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *);
+
+/*
+ * Acquires the credentials to perform authentication in the first
+ * place. Needs to be freed by ssh_gss_release_cred().
+ */
+Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *);
+
+/*
+ * Frees the contents of an Ssh_gss_ctx filled in by
+ * ssh_gss_acquire_cred().
+ */
+Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *);
+
+/*
+ * Gets a MIC for some input data. "out" needs to be freed by
+ * ssh_gss_free_mic().
+ */
+Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *in,
+ Ssh_gss_buf *out);
+
+/*
+ * Frees the contents of an Ssh_gss_buf filled in by
+ * ssh_gss_get_mic(). Do not accidentally call this on something
+ * filled in by ssh_gss_init_sec_context() (which requires a
+ * different free function) or something filled in by any other
+ * way.
+ */
+Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *);
+
+/*
+ * Return an error message after authentication failed. The
+ * message string is returned in "buf", with buf->len giving the
+ * number of characters of printable message text and buf->data
+ * containing one more character which is a trailing NUL.
+ * buf->data should be manually freed by the caller.
+ */
+Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx, Ssh_gss_buf *buf);
--- /dev/null
+#ifndef NO_GSSAPI
+
+#include <string.h>
+#include <gssapi/gssapi_krb5.h>
+#include "sshgss.h"
+#include "misc.h"
+
+typedef struct uxSsh_gss_ctx {
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_ctx_id_t ctx;
+} uxSsh_gss_ctx;
+
+int ssh_gss_init(void)
+{
+ /* On Windows this tries to load the SSPI library functions. On
+ Unix we assume we have GSSAPI at runtime if we were linked with
+ it at compile time */
+ return 1;
+}
+
+Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech)
+{
+ /* Copy constant into mech */
+ mech->len = gss_mech_krb5->length;
+ mech->data = gss_mech_krb5->elements;
+
+ return SSH_GSS_OK;
+}
+
+Ssh_gss_stat ssh_gss_import_name(char *host,
+ Ssh_gss_name *srv_name)
+{
+ OM_uint32 min_stat,maj_stat;
+ gss_buffer_desc host_buf;
+ char *pStr;
+
+ pStr = dupcat("host@", host, NULL);
+
+ host_buf.value = pStr;
+ host_buf.length = strlen(pStr);
+
+ maj_stat = gss_import_name(&min_stat, &host_buf,
+ GSS_C_NT_HOSTBASED_SERVICE,
+ (gss_name_t *)srv_name);
+ /* Release buffer */
+ sfree(pStr);
+ if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+ return SSH_GSS_FAILURE;
+}
+
+Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx)
+{
+ uxSsh_gss_ctx *uxctx = snew(uxSsh_gss_ctx);
+
+ uxctx->maj_stat = uxctx->min_stat = GSS_S_COMPLETE;
+ uxctx->ctx = GSS_C_NO_CONTEXT;
+ *ctx = (Ssh_gss_ctx) uxctx;
+
+ return SSH_GSS_OK;
+}
+
+Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx,
+ Ssh_gss_name srv_name,
+ int to_deleg,
+ Ssh_gss_buf *recv_tok,
+ Ssh_gss_buf *send_tok)
+{
+ uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx*) *ctx;
+ OM_uint32 ret_flags;
+
+ if (to_deleg) to_deleg = GSS_C_DELEG_FLAG;
+ uxctx->maj_stat = gss_init_sec_context(&uxctx->min_stat,
+ GSS_C_NO_CREDENTIAL,
+ &uxctx->ctx,
+ (gss_name_t) srv_name,
+ (gss_OID) gss_mech_krb5,
+ GSS_C_MUTUAL_FLAG |
+ GSS_C_INTEG_FLAG | to_deleg,
+ 0,
+ NULL, /* no channel bindings */
+ (gss_buffer_desc *)recv_tok,
+ NULL, /* ignore mech type */
+ (gss_buffer_desc *)send_tok,
+ &ret_flags,
+ NULL); /* ignore time_rec */
+
+ if (uxctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE;
+ if (uxctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
+ return SSH_GSS_FAILURE;
+}
+
+Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
+{
+ uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) ctx;
+ OM_uint32 lmin,lmax;
+ OM_uint32 ccc;
+ gss_buffer_desc msg_maj=GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc msg_min=GSS_C_EMPTY_BUFFER;
+
+ /* Return empty buffer in case of failure */
+ SSH_GSS_CLEAR_BUF(buf);
+
+ /* get first mesg from GSS */
+ ccc=0;
+ lmax=gss_display_status(&lmin,uxctx->maj_stat,GSS_C_GSS_CODE,(gss_OID) gss_mech_krb5,&ccc,&msg_maj);
+
+ if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE;
+
+ /* get first mesg from Kerberos */
+ ccc=0;
+ lmax=gss_display_status(&lmin,uxctx->min_stat,GSS_C_MECH_CODE,(gss_OID) gss_mech_krb5,&ccc,&msg_min);
+
+ if (lmax != GSS_S_COMPLETE) {
+ gss_release_buffer(&lmin, &msg_maj);
+ return SSH_GSS_FAILURE;
+ }
+
+ /* copy data into buffer */
+ buf->len = msg_maj.length + msg_min.length + 1;
+ buf->data = snewn(buf->len + 1, char);
+
+ /* copy mem */
+ memcpy(buf->data, msg_maj.value, msg_maj.length);
+ buf->data[msg_maj.length] = ' ';
+ memcpy(buf->data + msg_maj.length + 1, msg_min.value, msg_min.length);
+ buf->data[buf->len] = 0;
+ /* free mem & exit */
+ gss_release_buffer(&lmin, &msg_maj);
+ gss_release_buffer(&lmin, &msg_min);
+ return SSH_GSS_OK;
+}
+
+Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok)
+{
+ OM_uint32 min_stat,maj_stat;
+ maj_stat = gss_release_buffer(&min_stat, (gss_buffer_desc *)send_tok);
+
+ if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+ return SSH_GSS_FAILURE;
+}
+
+Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
+{
+ uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) *ctx;
+ OM_uint32 min_stat;
+ OM_uint32 maj_stat=GSS_S_COMPLETE;
+
+ if (uxctx == NULL) return SSH_GSS_FAILURE;
+ if (uxctx->ctx != GSS_C_NO_CONTEXT)
+ maj_stat = gss_delete_sec_context(&min_stat,&uxctx->ctx,GSS_C_NO_BUFFER);
+ sfree(uxctx);
+
+ if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+ return SSH_GSS_FAILURE;
+}
+
+
+Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name)
+{
+ OM_uint32 min_stat,maj_stat;
+ maj_stat = gss_release_name(&min_stat, (gss_name_t) srv_name);
+
+ if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+ return SSH_GSS_FAILURE;
+}
+
+Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
+ Ssh_gss_buf *hash)
+{
+ uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) ctx;
+ if (uxctx == NULL) return SSH_GSS_FAILURE;
+ return gss_get_mic(&(uxctx->min_stat),
+ uxctx->ctx,
+ 0,
+ (gss_buffer_desc *)buf,
+ (gss_buffer_desc *)hash);
+}
+
+Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash)
+{
+ /* On Unix this is the same freeing process as ssh_gss_free_tok. */
+ return ssh_gss_free_tok(hash);
+}
+
+#else
+
+/* Dummy function so this source file defines something if NO_GSSAPI
+ is defined. */
+
+int ssh_gss_init(void)
+{
+ return 1;
+}
+
+#endif
{
if (!strcmp(name, "TermType"))
return dupstr(getenv("TERM"));
- if (!strcmp(name, "UserName"))
- return get_username();
+ if (!strcmp(name, "UserName"))
+ return get_username();
if (!strcmp(name, "SerialLine"))
return dupstr("/dev/ttyS0");
return NULL;
--- /dev/null
+#ifndef NO_GSSAPI
+
+#include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include "sshgss.h"
+#include "misc.h"
+
+#define NOTHING
+#define DECL_SSPI_FUNCTION(linkage, rettype, name, params) \
+ typedef rettype (WINAPI *t_##name) params; \
+ linkage t_##name p_##name
+#define GET_SSPI_FUNCTION(module, name) \
+ p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL
+
+DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
+ AcquireCredentialsHandleA,
+ (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
+ PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp));
+DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
+ InitializeSecurityContextA,
+ (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG,
+ ULONG, PSecBufferDesc, ULONG, PCtxtHandle,
+ PSecBufferDesc, PULONG, PTimeStamp));
+DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
+ FreeContextBuffer,
+ (PVOID));
+DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
+ FreeCredentialsHandle,
+ (PCredHandle));
+DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
+ DeleteSecurityContext,
+ (PCtxtHandle));
+DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
+ QueryContextAttributesA,
+ (PCtxtHandle, ULONG, PVOID));
+DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
+ MakeSignature,
+ (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
+
+static HMODULE security_module = NULL;
+
+typedef struct winSsh_gss_ctx {
+ unsigned long maj_stat;
+ unsigned long min_stat;
+ CredHandle cred_handle;
+ CtxtHandle context;
+ PCtxtHandle context_handle;
+ TimeStamp expiry;
+} winSsh_gss_ctx;
+
+
+const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
+
+int ssh_gss_init(void)
+{
+ if (security_module)
+ return 1; /* already initialised */
+
+ security_module = LoadLibrary("secur32.dll");
+ if (security_module) {
+ GET_SSPI_FUNCTION(security_module, AcquireCredentialsHandleA);
+ GET_SSPI_FUNCTION(security_module, InitializeSecurityContextA);
+ GET_SSPI_FUNCTION(security_module, FreeContextBuffer);
+ GET_SSPI_FUNCTION(security_module, FreeCredentialsHandle);
+ GET_SSPI_FUNCTION(security_module, DeleteSecurityContext);
+ GET_SSPI_FUNCTION(security_module, QueryContextAttributesA);
+ GET_SSPI_FUNCTION(security_module, MakeSignature);
+ return 1;
+ }
+ return 0;
+}
+
+Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech)
+{
+ *mech = gss_mech_krb5;
+ return SSH_GSS_OK;
+}
+
+
+Ssh_gss_stat ssh_gss_import_name(char *host, Ssh_gss_name *srv_name)
+{
+ char *pStr;
+
+ /* Check hostname */
+ if (host == NULL) return SSH_GSS_FAILURE;
+
+ /* copy it into form host/FQDN */
+ pStr = dupcat("host/", host, NULL);
+
+ *srv_name = (Ssh_gss_name) pStr;
+
+ return SSH_GSS_OK;
+}
+
+Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx)
+{
+ winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
+
+ /* prepare our "wrapper" structure */
+ winctx->maj_stat = winctx->min_stat = SEC_E_OK;
+ winctx->context_handle = NULL;
+
+ /* Specifying no principal name here means use the credentials of
+ the current logged-in user */
+
+ winctx->maj_stat = p_AcquireCredentialsHandleA(NULL,
+ "Kerberos",
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &winctx->cred_handle,
+ &winctx->expiry);
+
+ if (winctx->maj_stat != SEC_E_OK) return SSH_GSS_FAILURE;
+
+ *ctx = (Ssh_gss_ctx) winctx;
+ return SSH_GSS_OK;
+}
+
+
+Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx,
+ Ssh_gss_name srv_name,
+ int to_deleg,
+ Ssh_gss_buf *recv_tok,
+ Ssh_gss_buf *send_tok)
+{
+ winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;
+ SecBuffer wsend_tok = {send_tok->len,SECBUFFER_TOKEN,send_tok->data};
+ SecBuffer wrecv_tok = {recv_tok->len,SECBUFFER_TOKEN,recv_tok->data};
+ SecBufferDesc output_desc={SECBUFFER_VERSION,1,&wsend_tok};
+ SecBufferDesc input_desc ={SECBUFFER_VERSION,1,&wrecv_tok};
+ unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT|
+ ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY;
+ unsigned long ret_flags=0;
+
+ /* check if we have to delegate ... */
+ if (to_deleg) flags |= ISC_REQ_DELEGATE;
+ winctx->maj_stat = p_InitializeSecurityContextA(&winctx->cred_handle,
+ winctx->context_handle,
+ (char*) srv_name,
+ flags,
+ 0, /* reserved */
+ SECURITY_NATIVE_DREP,
+ &input_desc,
+ 0, /* reserved */
+ &winctx->context,
+ &output_desc,
+ &ret_flags,
+ &winctx->expiry);
+
+ /* prepare for the next round */
+ winctx->context_handle = &winctx->context;
+ send_tok->data = (char*) wsend_tok.pvBuffer;
+ send_tok->len = wsend_tok.cbBuffer;
+
+ /* check & return our status */
+ if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE;
+ if (winctx->maj_stat==SEC_I_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
+
+ return SSH_GSS_FAILURE;
+}
+
+Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok)
+{
+ /* check input */
+ if (send_tok == NULL) return SSH_GSS_FAILURE;
+
+ /* free Windows buffer */
+ p_FreeContextBuffer(send_tok->data);
+ send_tok->len = 0; send_tok->data = NULL;
+
+ return SSH_GSS_OK;
+}
+
+Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
+{
+ winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;
+
+ /* check input */
+ if (winctx == NULL) return SSH_GSS_FAILURE;
+
+ /* free Windows data */
+ p_FreeCredentialsHandle(&winctx->cred_handle);
+ p_DeleteSecurityContext(&winctx->context);
+
+ /* delete our "wrapper" structure */
+ sfree(winctx);
+ *ctx = (Ssh_gss_ctx) NULL;
+
+ return SSH_GSS_OK;
+}
+
+
+Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name)
+{
+ char *pStr= (char *) *srv_name;
+
+ if (pStr == NULL) return SSH_GSS_FAILURE;
+ sfree(pStr);
+ *srv_name = (Ssh_gss_name) NULL;
+
+ return SSH_GSS_OK;
+}
+
+Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
+{
+ winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;
+ char *msg;
+
+ if (winctx == NULL) return SSH_GSS_FAILURE;
+
+ /* decode the error code */
+ switch (winctx->maj_stat) {
+ case SEC_E_OK: msg="SSPI status OK"; break;
+ case SEC_E_INVALID_HANDLE: msg="The handle passed to the function"
+ " is invalid.";
+ break;
+ case SEC_E_TARGET_UNKNOWN: msg="The target was not recognized."; break;
+ case SEC_E_LOGON_DENIED: msg="The logon failed."; break;
+ case SEC_E_INTERNAL_ERROR: msg="The Local Security Authority cannot"
+ " be contacted.";
+ break;
+ case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the"
+ " security package.";
+ break;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ msg="No authority could be contacted for authentication."
+ "The domain name of the authenticating party could be wrong,"
+ " the domain could be unreachable, or there might have been"
+ " a trust relationship failure.";
+ break;
+ case SEC_E_INSUFFICIENT_MEMORY:
+ msg="One or more of the SecBufferDesc structures passed as"
+ " an OUT parameter has a buffer that is too small.";
+ break;
+ case SEC_E_INVALID_TOKEN:
+ msg="The error is due to a malformed input token, such as a"
+ " token corrupted in transit, a token"
+ " of incorrect size, or a token passed into the wrong"
+ " security package. Passing a token to"
+ " the wrong package can happen if client and server did not"
+ " negotiate the proper security package.";
+ break;
+ default:
+ msg = "Internal SSPI error";
+ break;
+ }
+
+ buf->data = dupstr(msg);
+ buf->len = strlen(buf->data);
+
+ return SSH_GSS_OK;
+}
+
+Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
+ Ssh_gss_buf *hash)
+{
+ winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;
+ SecPkgContext_Sizes ContextSizes;
+ SecBufferDesc InputBufferDescriptor;
+ SecBuffer InputSecurityToken[2];
+
+ if (winctx == NULL) return SSH_GSS_FAILURE;
+
+ winctx->maj_stat = 0;
+
+ memset(&ContextSizes, 0, sizeof(ContextSizes));
+
+ winctx->maj_stat = p_QueryContextAttributesA(&winctx->context,
+ SECPKG_ATTR_SIZES,
+ &ContextSizes);
+
+ if (winctx->maj_stat != SEC_E_OK ||
+ ContextSizes.cbMaxSignature == 0)
+ return winctx->maj_stat;
+
+ InputBufferDescriptor.cBuffers = 2;
+ InputBufferDescriptor.pBuffers = InputSecurityToken;
+ InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ InputSecurityToken[0].BufferType = SECBUFFER_DATA;
+ InputSecurityToken[0].cbBuffer = buf->len;
+ InputSecurityToken[0].pvBuffer = buf->data;
+ InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;
+ InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature;
+ InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char);
+
+ winctx->maj_stat = p_MakeSignature(&winctx->context,
+ 0,
+ &InputBufferDescriptor,
+ 0);
+
+ if (winctx->maj_stat == SEC_E_OK) {
+ hash->len = InputSecurityToken[1].cbBuffer;
+ hash->data = InputSecurityToken[1].pvBuffer;
+ }
+
+ return winctx->maj_stat;
+}
+
+Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash)
+{
+ sfree(hash->data);
+ return SSH_GSS_OK;
+}
+
+#else
+
+/* Dummy function so this source file defines something if NO_GSSAPI
+ is defined. */
+
+int ssh_gss_init(void)
+{
+ return 0;
+}
+
+#endif