1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Initial commit of GSSAPI Kerberos support.

[originally from svn r8138]
This commit is contained in:
Owen Dunn 2008-08-10 13:10:31 +00:00
parent 0677c73c1a
commit de5dd9d65c
10 changed files with 851 additions and 6 deletions

9
Recipe
View File

@ -95,6 +95,10 @@
# 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
@ -166,6 +170,7 @@ version.o: FORCE
# 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
@ -255,8 +260,8 @@ NONSSH = telnet raw rlogin ldisc pinger
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).

View File

@ -2051,6 +2051,13 @@ void setup_config_box(struct controlbox *b, int midsession,
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',
@ -2060,6 +2067,12 @@ void setup_config_box(struct controlbox *b, int midsession,
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),

View File

@ -924,6 +924,8 @@ if (defined $makefiles{'gtk'}) {
"# 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".
@ -939,6 +941,11 @@ if (defined $makefiles{'gtk'}) {
" -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",

View File

@ -459,6 +459,8 @@ struct config_tag {
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 */

View File

@ -321,6 +321,7 @@ void save_open_settings(void *sesskey, Config *cfg)
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);
@ -330,6 +331,7 @@ void save_open_settings(void *sesskey, Config *cfg)
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);
@ -581,7 +583,7 @@ void load_open_settings(void *sesskey, Config *cfg)
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);
@ -589,6 +591,7 @@ void load_open_settings(void *sesskey, Config *cfg)
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);
{
@ -614,6 +617,7 @@ void load_open_settings(void *sesskey, Config *cfg)
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,

189
ssh.c
View File

@ -12,6 +12,7 @@
#include "putty.h"
#include "tree234.h"
#include "ssh.h"
#include "sshgss.h"
#ifndef FALSE
#define FALSE 0
@ -112,6 +113,12 @@
#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
@ -127,6 +134,7 @@ typedef enum {
SSH2_PKTCTX_NOAUTH,
SSH2_PKTCTX_PUBLICKEY,
SSH2_PKTCTX_PASSWORD,
SSH2_PKTCTX_GSSAPI,
SSH2_PKTCTX_KBDINTER
} Pkt_ACtx;
@ -339,6 +347,12 @@ static char *ssh1_pkt_type(int type)
}
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);
@ -896,6 +910,11 @@ struct ssh_tag {
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)
@ -2875,6 +2894,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
sk_addr_free(addr);
return err;
}
ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */
/*
* Open socket.
@ -7043,12 +7063,15 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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;
@ -7072,6 +7095,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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);
@ -7079,6 +7107,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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.
@ -7366,7 +7396,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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;
@ -7437,6 +7467,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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;
@ -7765,6 +7800,157 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
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) {
/*
@ -8949,6 +9135,7 @@ static void ssh_free(void *handle)
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;

112
sshgss.h Normal file
View File

@ -0,0 +1,112 @@
#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);

196
unix/uxgss.c Normal file
View File

@ -0,0 +1,196 @@
#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

View File

@ -107,8 +107,8 @@ char *platform_default_s(const char *name)
{
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;

319
windows/wingss.c Normal file
View File

@ -0,0 +1,319 @@
#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