aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/plink/cmdline.c124
-rw-r--r--tools/plink/ldisc.c2
-rw-r--r--tools/plink/logging.c13
-rw-r--r--tools/plink/putty.h21
-rw-r--r--tools/plink/puttyps.h4
-rw-r--r--tools/plink/settings.c27
-rw-r--r--tools/plink/ssh.c240
-rw-r--r--tools/plink/ssh.h14
-rw-r--r--tools/plink/sshaes.c16
-rw-r--r--tools/plink/sshgss.h294
-rw-r--r--tools/plink/sshrand.c5
-rw-r--r--tools/plink/sshsh512.c16
-rw-r--r--tools/plink/version.c4
-rw-r--r--tools/plink/wingss.c800
-rw-r--r--tools/plink/winhelp.h4
-rw-r--r--tools/plink/winmisc.c86
-rw-r--r--tools/plink/winnet.c171
-rw-r--r--tools/plink/winplink.c12
-rw-r--r--tools/plink/winproxy.c2
-rw-r--r--tools/plink/winstore.c23
-rw-r--r--tools/plink/winstuff.h58
21 files changed, 1298 insertions, 638 deletions
diff --git a/tools/plink/cmdline.c b/tools/plink/cmdline.c
index f3a0e22f1..6492132f1 100644
--- a/tools/plink/cmdline.c
+++ b/tools/plink/cmdline.c
@@ -56,20 +56,30 @@ static void cmdline_save_param(char *p, char *value, int pri)
saves[pri].nsaved++;
}
+static char *cmdline_password = NULL;
+
void cmdline_cleanup(void)
{
int pri;
- for (pri = 0; pri < NPRIORITIES; pri++)
+ if (cmdline_password) {
+ memset(cmdline_password, 0, strlen(cmdline_password));
+ sfree(cmdline_password);
+ cmdline_password = NULL;
+ }
+
+ for (pri = 0; pri < NPRIORITIES; pri++) {
sfree(saves[pri].params);
+ saves[pri].params = NULL;
+ saves[pri].savesize = 0;
+ saves[pri].nsaved = 0;
+ }
}
#define SAVEABLE(pri) do { \
if (need_save) { cmdline_save_param(p, value, pri); return ret; } \
} while (0)
-static char *cmdline_password = NULL;
-
/*
* Similar interface to get_userpass_input(), except that here a -1
* return means that we aren't capable of processing the prompt and
@@ -99,6 +109,8 @@ int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen) {
p->prompts[0]->result_len);
p->prompts[0]->result[p->prompts[0]->result_len-1] = '\0';
memset(cmdline_password, 0, strlen(cmdline_password));
+ sfree(cmdline_password);
+ cmdline_password = NULL;
tried_once = 1;
return 1;
@@ -192,6 +204,16 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
SAVEABLE(0);
default_protocol = cfg->protocol = PROT_RAW;
}
+ if (!strcmp(p, "-serial")) {
+ RETURN(1);
+ /* Serial is not NONNETWORK in an odd sense of the word */
+ UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
+ SAVEABLE(0);
+ default_protocol = cfg->protocol = PROT_SERIAL;
+ /* The host parameter will already be loaded into cfg->host, so copy it across */
+ strncpy(cfg->serline, cfg->host, sizeof(cfg->serline) - 1);
+ cfg->serline[sizeof(cfg->serline) - 1] = '\0';
+ }
if (!strcmp(p, "-v")) {
RETURN(1);
flags |= FLAG_VERBOSE;
@@ -312,6 +334,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
cfg->remote_cmd_ptr = command;
cfg->remote_cmd_ptr2 = NULL;
cfg->nopty = TRUE; /* command => no terminal */
+ fclose(fp);
}
if (!strcmp(p, "-P")) {
RETURN(2);
@@ -435,7 +458,100 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
SAVEABLE(1);
cfg->addressfamily = ADDRTYPE_IPV6;
}
-
+ if (!strcmp(p, "-sercfg")) {
+ char* nextitem;
+ RETURN(2);
+ UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
+ SAVEABLE(1);
+ if (cfg->protocol != PROT_SERIAL)
+ cmdline_error("the -sercfg option can only be used with the "
+ "serial protocol");
+ /* Value[0] contains one or more , separated values, like 19200,8,n,1,X */
+ nextitem = value;
+ while (nextitem[0] != '\0') {
+ int length, skip;
+ char *end = strchr(nextitem, ',');
+ if (!end) {
+ length = strlen(nextitem);
+ skip = 0;
+ } else {
+ length = end - nextitem;
+ nextitem[length] = '\0';
+ skip = 1;
+ }
+ if (length == 1) {
+ switch (*nextitem) {
+ case '1':
+ cfg->serstopbits = 2;
+ break;
+ case '2':
+ cfg->serstopbits = 4;
+ break;
+
+ case '5':
+ cfg->serdatabits = 5;
+ break;
+ case '6':
+ cfg->serdatabits = 6;
+ break;
+ case '7':
+ cfg->serdatabits = 7;
+ break;
+ case '8':
+ cfg->serdatabits = 8;
+ break;
+ case '9':
+ cfg->serdatabits = 9;
+ break;
+
+ case 'n':
+ cfg->serparity = SER_PAR_NONE;
+ break;
+ case 'o':
+ cfg->serparity = SER_PAR_ODD;
+ break;
+ case 'e':
+ cfg->serparity = SER_PAR_EVEN;
+ break;
+ case 'm':
+ cfg->serparity = SER_PAR_MARK;
+ break;
+ case 's':
+ cfg->serparity = SER_PAR_SPACE;
+ break;
+
+ case 'N':
+ cfg->serflow = SER_FLOW_NONE;
+ break;
+ case 'X':
+ cfg->serflow = SER_FLOW_XONXOFF;
+ break;
+ case 'R':
+ cfg->serflow = SER_FLOW_RTSCTS;
+ break;
+ case 'D':
+ cfg->serflow = SER_FLOW_DSRDTR;
+ break;
+
+ default:
+ cmdline_error("Unrecognised suboption \"-sercfg %c\"",
+ *nextitem);
+ }
+ } else if (length == 3 && !strncmp(nextitem,"1.5",3)) {
+ /* Messy special case */
+ cfg->serstopbits = 3;
+ } else {
+ int serspeed = atoi(nextitem);
+ if (serspeed != 0) {
+ cfg->serspeed = serspeed;
+ } else {
+ cmdline_error("Unrecognised suboption \"-sercfg %s\"",
+ nextitem);
+ }
+ }
+ nextitem += length + skip;
+ }
+ }
return ret; /* unrecognised */
}
diff --git a/tools/plink/ldisc.c b/tools/plink/ldisc.c
index 20fa3c568..119a02acb 100644
--- a/tools/plink/ldisc.c
+++ b/tools/plink/ldisc.c
@@ -147,7 +147,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
if (EDITING) {
while (len--) {
int c;
- c = *buf++ + keyflag;
+ c = (unsigned char)(*buf++) + keyflag;
if (!interactive && c == '\r')
c += KCTRL('@');
switch (ldisc->quotenext ? ' ' : c) {
diff --git a/tools/plink/logging.c b/tools/plink/logging.c
index 6b8eaa7c5..4c7aa918c 100644
--- a/tools/plink/logging.c
+++ b/tools/plink/logging.c
@@ -43,7 +43,13 @@ static void logwrite(struct LogContext *ctx, void *data, int len)
bufchain_add(&ctx->queue, data, len);
} else if (ctx->state == L_OPEN) {
assert(ctx->lgfp);
- fwrite(data, 1, len, ctx->lgfp);
+ if (fwrite(data, 1, len, ctx->lgfp) < len) {
+ logfclose(ctx);
+ ctx->state = L_ERROR;
+ /* Log state is L_ERROR so this won't cause a loop */
+ logevent(ctx->frontend,
+ "Disabled writing session log due to error while writing");
+ }
} /* else L_ERROR, so ignore the write */
}
@@ -101,8 +107,9 @@ static void logfopen_callback(void *handle, int mode)
}
event = dupprintf("%s session log (%s mode) to file: %s",
- (mode == 0 ? "Disabled writing" :
- mode == 1 ? "Appending" : "Writing new"),
+ ctx->state == L_ERROR ?
+ (mode == 0 ? "Disabled writing" : "Error writing") :
+ (mode == 1 ? "Appending" : "Writing new"),
(ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" :
ctx->cfg.logtype == LGTYP_DEBUG ? "raw" :
ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" :
diff --git a/tools/plink/putty.h b/tools/plink/putty.h
index 1fff1e74f..ac2701133 100644
--- a/tools/plink/putty.h
+++ b/tools/plink/putty.h
@@ -348,6 +348,19 @@ enum {
SER_FLOW_NONE, SER_FLOW_XONXOFF, SER_FLOW_RTSCTS, SER_FLOW_DSRDTR
};
+/*
+ * Tables of string <-> enum value mappings used in settings.c.
+ * Defined here so that backends can export their GSS library tables
+ * to the cross-platform settings code.
+ */
+struct keyval { char *s; int v; };
+
+#ifndef NO_GSSAPI
+extern const int ngsslibs;
+extern const char *const gsslibnames[];/* for displaying in configuration */
+extern const struct keyval gsslibkeywords[]; /* for storing by settings.c */
+#endif
+
extern const char *const ttymodes[];
enum {
@@ -461,6 +474,8 @@ struct config_tag {
int try_ki_auth;
int try_gssapi_auth; /* attempt gssapi auth */
int gssapifwd; /* forward tgt via gss */
+ int ssh_gsslist[4]; /* preference order for local GSS libs */
+ Filename ssh_gss_custom;
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 */
@@ -592,7 +607,8 @@ struct config_tag {
/* SSH bug compatibility modes */
int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,
sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,
- sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2;
+ sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2,
+ sshbug_ignore2;
/*
* ssh_simple means that we promise never to open any channel other
* than the main one, which means it can safely use a very large
@@ -825,6 +841,7 @@ void term_free(Terminal *);
void term_size(Terminal *, int, int, int);
void term_paint(Terminal *, Context, int, int, int, int, int);
void term_scroll(Terminal *, int, int);
+void term_scroll_to_selection(Terminal *, int);
void term_pwron(Terminal *, int);
void term_clrsb(Terminal *);
void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action,
@@ -854,6 +871,8 @@ char *term_get_ttymode(Terminal *term, const char *mode);
int term_get_userpass_input(Terminal *term, prompts_t *p,
unsigned char *in, int inlen);
+int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);
+
/*
* Exports from logging.c.
*/
diff --git a/tools/plink/puttyps.h b/tools/plink/puttyps.h
index e7d280814..454f6055a 100644
--- a/tools/plink/puttyps.h
+++ b/tools/plink/puttyps.h
@@ -9,10 +9,6 @@
#include "winstuff.h"
-#elif defined(macintosh)
-
-#include "macstuff.h"
-
#elif defined(MACOSX)
#include "osx.h"
diff --git a/tools/plink/settings.c b/tools/plink/settings.c
index 31d4e1fff..bd6b97495 100644
--- a/tools/plink/settings.c
+++ b/tools/plink/settings.c
@@ -8,11 +8,6 @@
#include "putty.h"
#include "storage.h"
-/*
- * Tables of string <-> enum value mappings
- */
-struct keyval { char *s; int v; };
-
/* The cipher order given here is the default order. */
static const struct keyval ciphernames[] = {
{ "aes", CIPHER_AES },
@@ -84,9 +79,13 @@ int get_remote_username(Config *cfg, char *user, size_t len)
if (cfg->username_from_env) {
/* Use local username. */
char *luser = get_username();
- strncpy(user, luser, len);
- user[len-1] = '\0';
- sfree(luser);
+ if (luser) {
+ strncpy(user, luser, len);
+ user[len-1] = '\0';
+ sfree(luser);
+ } else {
+ *user = '\0';
+ }
} else {
*user = '\0';
}
@@ -352,6 +351,11 @@ void save_open_settings(void *sesskey, Config *cfg)
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);
+#ifndef NO_GSSAPI
+ wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs,
+ cfg->ssh_gsslist);
+ write_setting_filename(sesskey, "GSSCustom", cfg->ssh_gss_custom);
+#endif
write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell);
write_setting_i(sesskey, "SshProt", cfg->sshprot);
write_setting_s(sesskey, "LogHost", cfg->loghost);
@@ -473,6 +477,7 @@ void save_open_settings(void *sesskey, Config *cfg)
write_setting_i(sesskey, "BugIgnore1", 2-cfg->sshbug_ignore1);
write_setting_i(sesskey, "BugPlainPW1", 2-cfg->sshbug_plainpw1);
write_setting_i(sesskey, "BugRSA1", 2-cfg->sshbug_rsa1);
+ write_setting_i(sesskey, "BugIgnore2", 2-cfg->sshbug_ignore2);
write_setting_i(sesskey, "BugHMAC2", 2-cfg->sshbug_hmac2);
write_setting_i(sesskey, "BugDeriveKey2", 2-cfg->sshbug_derivekey2);
write_setting_i(sesskey, "BugRSAPad2", 2-cfg->sshbug_rsapad2);
@@ -640,6 +645,11 @@ void load_open_settings(void *sesskey, Config *cfg)
gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth);
gppi(sesskey, "AuthKI", 1, &cfg->try_ki_auth);
gppi(sesskey, "AuthGSSAPI", 1, &cfg->try_gssapi_auth);
+#ifndef NO_GSSAPI
+ gprefs(sesskey, "GSSLibs", "\0",
+ gsslibkeywords, ngsslibs, cfg->ssh_gsslist);
+ gppfile(sesskey, "GSSCustom", &cfg->ssh_gss_custom);
+#endif
gppi(sesskey, "SshNoShell", 0, &cfg->ssh_no_shell);
gppfile(sesskey, "PublicKeyFile", &cfg->keyfile);
gpps(sesskey, "RemoteCommand", "", cfg->remote_cmd,
@@ -813,6 +823,7 @@ void load_open_settings(void *sesskey, Config *cfg)
gppi(sesskey, "BugIgnore1", 0, &i); cfg->sshbug_ignore1 = 2-i;
gppi(sesskey, "BugPlainPW1", 0, &i); cfg->sshbug_plainpw1 = 2-i;
gppi(sesskey, "BugRSA1", 0, &i); cfg->sshbug_rsa1 = 2-i;
+ gppi(sesskey, "BugIgnore2", 0, &i); cfg->sshbug_ignore2 = 2-i;
{
int i;
gppi(sesskey, "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i;
diff --git a/tools/plink/ssh.c b/tools/plink/ssh.c
index 89c0433ca..7c9b929de 100644
--- a/tools/plink/ssh.c
+++ b/tools/plink/ssh.c
@@ -13,6 +13,7 @@
#include "tree234.h"
#include "ssh.h"
#ifndef NO_GSSAPI
+#include "sshgssc.h"
#include "sshgss.h"
#endif
@@ -194,6 +195,7 @@ static const char *const ssh2_disconnect_reasons[] = {
#define BUG_SSH2_REKEY 64
#define BUG_SSH2_PK_SESSIONID 128
#define BUG_SSH2_MAXPKT 256
+#define BUG_CHOKES_ON_SSH2_IGNORE 512
/*
* Codes for terminal modes.
@@ -588,6 +590,17 @@ struct ssh_channel {
* A channel is completely finished with when all four bits are set.
*/
int closes;
+
+ /*
+ * This flag indicates that a close is pending on the outgoing
+ * side of the channel: that is, wherever we're getting the data
+ * for this channel has sent us some data followed by EOF. We
+ * can't actually close the channel until we've finished sending
+ * the data, so we set this flag instead to remind us to
+ * initiate the closing process once our buffer is clear.
+ */
+ int pending_close;
+
/*
* True if this channel is causing the underlying connection to be
* throttled.
@@ -928,6 +941,13 @@ struct ssh_tag {
* Fully qualified host name, which we need if doing GSSAPI.
*/
char *fullhostname;
+
+#ifndef NO_GSSAPI
+ /*
+ * GSSAPI libraries for this session.
+ */
+ struct ssh_gss_liblist *gsslibs;
+#endif
};
#define logevent(s) logevent(ssh->frontend, s)
@@ -2011,7 +2031,8 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
{
int len;
if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
- ssh->deferred_len == 0 && !noignore) {
+ ssh->deferred_len == 0 && !noignore &&
+ !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
/*
* Interpose an SSH_MSG_IGNORE to ensure that user data don't
* get encrypted with a known IV.
@@ -2141,7 +2162,8 @@ static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt,
* unavailable, we don't do this trick at all, because we
* gain nothing by it.)
*/
- if (ssh->cscipher) {
+ if (ssh->cscipher &&
+ !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
int stringlen, i;
stringlen = (256 - ssh->deferred_len);
@@ -2508,6 +2530,15 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
ssh->remote_bugs |= BUG_SSH2_MAXPKT;
logevent("We believe remote version ignores SSH-2 maximum packet size");
}
+
+ if (ssh->cfg.sshbug_ignore2 == FORCE_ON) {
+ /*
+ * Servers that don't support SSH2_MSG_IGNORE. Currently,
+ * none detected automatically.
+ */
+ ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
+ logevent("We believe remote version has SSH-2 ignore bug");
+ }
}
/*
@@ -2853,6 +2884,8 @@ static int ssh_do_close(Ssh ssh, int notify_exit)
del234(ssh->portfwds, pf); /* moving next one to index 0 */
free_portfwd(pf);
}
+ freetree234(ssh->portfwds);
+ ssh->portfwds = NULL;
}
return ret;
@@ -4138,7 +4171,7 @@ void sshfwd_close(struct ssh_channel *c)
if (ssh->state == SSH_STATE_CLOSED)
return;
- if (c && !c->closes) {
+ if (!c->closes) {
/*
* If halfopen is true, we have sent
* CHANNEL_OPEN for this channel, but it hasn't even been
@@ -4150,14 +4183,42 @@ void sshfwd_close(struct ssh_channel *c)
if (ssh->version == 1) {
send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
PKT_END);
+ c->closes = 1; /* sent MSG_CLOSE */
} else {
- struct Packet *pktout;
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(pktout, c->remoteid);
- ssh2_pkt_send(ssh, pktout);
+ int bytes_to_send = bufchain_size(&c->v.v2.outbuffer);
+ if (bytes_to_send > 0) {
+ /*
+ * If we still have unsent data in our outgoing
+ * buffer for this channel, we can't actually
+ * initiate a close operation yet or that data
+ * will be lost. Instead, set the pending_close
+ * flag so that when we do clear the buffer
+ * we'll start closing the channel.
+ */
+ char logmsg[160] = {'\0'};
+ sprintf(
+ logmsg,
+ "Forwarded port pending to be closed : "
+ "%d bytes remaining",
+ bytes_to_send);
+ logevent(logmsg);
+
+ c->pending_close = TRUE;
+ } else {
+ /*
+ * No locally buffered data, so we can send the
+ * close message immediately.
+ */
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes = 1; /* sent MSG_CLOSE */
+ logevent("Nothing left to send, closing channel");
+ }
}
}
- c->closes = 1; /* sent MSG_CLOSE */
+
if (c->type == CHAN_X11) {
c->u.x11.s = NULL;
logevent("Forwarded X11 connection terminated");
@@ -4296,6 +4357,7 @@ static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx)
rpf = del234(ssh->rportfwds, pf);
assert(rpf == pf);
+ pf->pfrec->remote = NULL;
free_rportfwd(pf);
}
}
@@ -4427,12 +4489,19 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
epfrec = add234(ssh->portfwds, pfrec);
if (epfrec != pfrec) {
+ if (epfrec->status == DESTROY) {
+ /*
+ * We already have a port forwarding up and running
+ * with precisely these parameters. Hence, no need
+ * to do anything; simply re-tag the existing one
+ * as KEEP.
+ */
+ epfrec->status = KEEP;
+ }
/*
- * We already have a port forwarding with precisely
- * these parameters. Hence, no need to do anything;
- * simply tag the existing one as KEEP.
+ * Anything else indicates that there was a duplicate
+ * in our input, which we'll silently ignore.
*/
- epfrec->status = KEEP;
free_portfwd(pfrec);
} else {
pfrec->status = CREATE;
@@ -4465,6 +4534,8 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
logeventf(ssh, "Cancelling %s", message);
sfree(message);
+ /* epf->remote or epf->local may be NULL if setting up a
+ * forwarding failed. */
if (epf->remote) {
struct ssh_rportfwd *rpf = epf->remote;
struct Packet *pktout;
@@ -4674,6 +4745,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = 0;
c->type = CHAN_X11; /* identify channel type */
add234(ssh->channels, c);
@@ -4703,6 +4775,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = 0;
c->type = CHAN_AGENT; /* identify channel type */
c->u.a.lensofar = 0;
@@ -4757,6 +4830,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = 0;
c->type = CHAN_SOCKDATA; /* identify channel type */
add234(ssh->channels, c);
@@ -6323,7 +6397,7 @@ static int ssh2_try_send(struct ssh_channel *c)
return bufchain_size(&c->v.v2.outbuffer);
}
-static void ssh2_try_send_and_unthrottle(struct ssh_channel *c)
+static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
{
int bufsize;
if (c->closes)
@@ -6347,6 +6421,19 @@ static void ssh2_try_send_and_unthrottle(struct ssh_channel *c)
break;
}
}
+
+ /*
+ * If we've emptied the channel's output buffer and there's a
+ * pending close event, start the channel-closing procedure.
+ */
+ if (c->pending_close && bufchain_size(&c->v.v2.outbuffer) == 0) {
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes = 1;
+ c->pending_close = FALSE;
+ }
}
/*
@@ -6357,6 +6444,7 @@ static void ssh2_channel_init(struct ssh_channel *c)
Ssh ssh = c->ssh;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = FALSE;
c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
@@ -6540,7 +6628,7 @@ static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
return;
if (!c->closes) {
c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
- ssh2_try_send_and_unthrottle(c);
+ ssh2_try_send_and_unthrottle(ssh, c);
}
}
@@ -6661,11 +6749,13 @@ static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
* wrap up and close the channel ourselves.
*/
x11_close(c->u.x11.s);
+ c->u.x11.s = NULL;
sshfwd_close(c);
} else if (c->type == CHAN_AGENT) {
sshfwd_close(c);
} else if (c->type == CHAN_SOCKDATA) {
pfd_close(c->u.pfd.s);
+ c->u.pfd.s = NULL;
sshfwd_close(c);
}
}
@@ -7186,6 +7276,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
int num_env, env_left, env_ok;
struct Packet *pktout;
#ifndef NO_GSSAPI
+ struct ssh_gss_library *gsslib;
Ssh_gss_ctx gss_ctx;
Ssh_gss_buf gss_buf;
Ssh_gss_buf gss_rcvtok, gss_sndtok;
@@ -7454,6 +7545,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
}
while (1) {
+ char *methods = NULL;
+ int methlen = 0;
+
/*
* Wait for the result of the last authentication request.
*/
@@ -7503,8 +7597,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* helpfully try next.
*/
if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
- char *methods;
- int methlen;
ssh_pkt_getstring(pktin, &methods, &methlen);
if (!ssh2_pkt_getbool(pktin)) {
/*
@@ -7560,10 +7652,12 @@ 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
+#ifndef NO_GSSAPI
+ if (!ssh->gsslibs)
+ ssh->gsslibs = ssh_gss_setup(&ssh->cfg);
s->can_gssapi = ssh->cfg.try_gssapi_auth &&
- in_commasep_string("gssapi-with-mic", methods, methlen) &&
- ssh_gss_init();
+ in_commasep_string("gssapi-with-mic", methods, methlen) &&
+ ssh->gsslibs->nlibraries > 0;
#endif
}
@@ -7906,6 +8000,35 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
s->gotit = TRUE;
ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;
+ /*
+ * Pick the highest GSS library on the preference
+ * list.
+ */
+ {
+ int i, j;
+ s->gsslib = NULL;
+ for (i = 0; i < ngsslibs; i++) {
+ int want_id = ssh->cfg.ssh_gsslist[i];
+ for (j = 0; j < ssh->gsslibs->nlibraries; j++)
+ if (ssh->gsslibs->libraries[j].id == want_id) {
+ s->gsslib = &ssh->gsslibs->libraries[j];
+ goto got_gsslib; /* double break */
+ }
+ }
+ got_gsslib:
+ /*
+ * We always expect to have found something in
+ * the above loop: we only came here if there
+ * was at least one viable GSS library, and the
+ * preference list should always mention
+ * everything and only change the order.
+ */
+ assert(s->gsslib);
+ }
+
+ if (s->gsslib->gsslogmsg)
+ logevent(s->gsslib->gsslogmsg);
+
/* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
ssh2_pkt_addstring(s->pktout, s->username);
@@ -7913,7 +8036,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
ssh2_pkt_addstring(s->pktout, "gssapi-with-mic");
/* add mechanism info */
- ssh_gss_indicate_mech(&s->gss_buf);
+ s->gsslib->indicate_mech(s->gsslib, &s->gss_buf);
/* number of GSSAPI mechanisms */
ssh2_pkt_adduint32(s->pktout,1);
@@ -7949,8 +8072,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
}
/* now start running */
- s->gss_stat = ssh_gss_import_name(ssh->fullhostname,
- &s->gss_srv_name);
+ s->gss_stat = s->gsslib->import_name(s->gsslib,
+ 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");
@@ -7960,11 +8084,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
}
/* fetch TGT into GSS engine */
- s->gss_stat = ssh_gss_acquire_cred(&s->gss_ctx);
+ s->gss_stat = s->gsslib->acquire_cred(s->gsslib, &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);
+ s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
continue;
}
@@ -7974,17 +8098,20 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
/* 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);
+ s->gss_stat = s->gsslib->init_sec_context
+ (s->gsslib,
+ &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) {
+ if (s->gsslib->display_status(s->gsslib, s->gss_ctx,
+ &s->gss_buf) == SSH_GSS_OK) {
logevent(s->gss_buf.value);
sfree(s->gss_buf.value);
}
@@ -8001,7 +8128,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
ssh_pkt_addstring_start(s->pktout);
ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.value,s->gss_sndtok.length);
ssh2_pkt_send(ssh, s->pktout);
- ssh_gss_free_tok(&s->gss_sndtok);
+ s->gsslib->free_tok(s->gsslib, &s->gss_sndtok);
}
if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {
@@ -8018,8 +8145,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
} 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);
+ s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+ s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
continue;
}
logevent("GSSAPI authentication loop finished OK");
@@ -8038,17 +8165,17 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
s->gss_buf.value = (char *)s->pktout->data + micoffset;
s->gss_buf.length = s->pktout->length - micoffset;
- ssh_gss_get_mic(s->gss_ctx, &s->gss_buf, &mic);
+ s->gsslib->get_mic(s->gsslib, 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.value, mic.length);
ssh2_pkt_send(ssh, s->pktout);
- ssh_gss_free_mic(&mic);
+ s->gsslib->free_mic(s->gsslib, &mic);
s->gotit = FALSE;
- ssh_gss_release_name(&s->gss_srv_name);
- ssh_gss_release_cred(&s->gss_ctx);
+ s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+ s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
continue;
#endif
} else if (s->can_keyb_inter && !s->kbd_inter_refused) {
@@ -8434,11 +8561,16 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
sfree(s->password);
} else {
+ char *str = dupprintf("No supported authentication methods available"
+ " (server sent: %.*s)",
+ methlen, methods);
- ssh_disconnect(ssh, NULL,
+ ssh_disconnect(ssh, str,
"No supported authentication methods available",
SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
FALSE);
+ sfree(str);
+
crStopV;
}
@@ -8877,7 +9009,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* Try to send data on all channels if we can.
*/
for (i = 0; NULL != (c = index234(ssh->channels, i)); i++)
- ssh2_try_send_and_unthrottle(c);
+ ssh2_try_send_and_unthrottle(ssh, c);
}
}
@@ -9159,6 +9291,10 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);
ssh->kex_in_progress = FALSE;
+#ifndef NO_GSSAPI
+ ssh->gsslibs = NULL;
+#endif
+
p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
if (p != NULL)
return p;
@@ -9231,7 +9367,7 @@ static void ssh_free(void *handle)
if (ssh->rportfwds) {
while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)
- sfree(pf);
+ free_rportfwd(pf);
freetree234(ssh->rportfwds);
ssh->rportfwds = NULL;
}
@@ -9255,6 +9391,10 @@ static void ssh_free(void *handle)
if (ssh->pinger)
pinger_free(ssh->pinger);
bufchain_clear(&ssh->queued_incoming_data);
+#ifndef NO_GSSAPI
+ if (ssh->gsslibs)
+ ssh_gss_cleanup(ssh->gsslibs);
+#endif
sfree(ssh);
random_unref();
@@ -9415,8 +9555,10 @@ static const struct telnet_special *ssh_get_specials(void *handle)
static const struct telnet_special ssh1_ignore_special[] = {
{"IGNORE message", TS_NOP}
};
- static const struct telnet_special ssh2_transport_specials[] = {
+ static const struct telnet_special ssh2_ignore_special[] = {
{"IGNORE message", TS_NOP},
+ };
+ static const struct telnet_special ssh2_rekey_special[] = {
{"Repeat key exchange", TS_REKEY},
};
static const struct telnet_special ssh2_session_specials[] = {
@@ -9441,7 +9583,8 @@ static const struct telnet_special *ssh_get_specials(void *handle)
{NULL, TS_EXITMENU}
};
/* XXX review this length for any changes: */
- static struct telnet_special ssh_specials[lenof(ssh2_transport_specials) +
+ static struct telnet_special ssh_specials[lenof(ssh2_ignore_special) +
+ lenof(ssh2_rekey_special) +
lenof(ssh2_session_specials) +
lenof(specials_end)];
Ssh ssh = (Ssh) handle;
@@ -9460,7 +9603,10 @@ static const struct telnet_special *ssh_get_specials(void *handle)
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
ADD_SPECIALS(ssh1_ignore_special);
} else if (ssh->version == 2) {
- ADD_SPECIALS(ssh2_transport_specials);
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE))
+ ADD_SPECIALS(ssh2_ignore_special);
+ if (!(ssh->remote_bugs & BUG_SSH2_REKEY))
+ ADD_SPECIALS(ssh2_rekey_special);
if (ssh->mainchan)
ADD_SPECIALS(ssh2_session_specials);
} /* else we're not ready yet */
@@ -9510,9 +9656,11 @@ static void ssh_special(void *handle, Telnet_Special code)
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
} else {
- pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
- ssh2_pkt_addstring_start(pktout);
- ssh2_pkt_send_noqueue(ssh, pktout);
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
+ pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_addstring_start(pktout);
+ ssh2_pkt_send_noqueue(ssh, pktout);
+ }
}
} else if (code == TS_REKEY) {
if (!ssh->kex_in_progress && ssh->version == 2) {
diff --git a/tools/plink/ssh.h b/tools/plink/ssh.h
index 814939c2b..86c402965 100644
--- a/tools/plink/ssh.h
+++ b/tools/plink/ssh.h
@@ -71,8 +71,12 @@ unsigned char *rsa_public_blob(struct RSAKey *key, int *len);
int rsa_public_blob_len(void *data, int maxlen);
void freersakey(struct RSAKey *key);
-typedef unsigned int word32;
+#ifndef PUTTY_UINT32_DEFINED
+/* This makes assumptions about the int type. */
typedef unsigned int uint32;
+#define PUTTY_UINT32_DEFINED
+#endif
+typedef uint32 word32;
unsigned long crc32_compute(const void *s, size_t len);
unsigned long crc32_update(unsigned long crc_input, const void *s, size_t len);
@@ -290,6 +294,14 @@ extern const struct ssh_mac ssh_hmac_sha1_buggy;
extern const struct ssh_mac ssh_hmac_sha1_96;
extern const struct ssh_mac ssh_hmac_sha1_96_buggy;
+void *aes_make_context(void);
+void aes_free_context(void *handle);
+void aes128_key(void *handle, unsigned char *key);
+void aes192_key(void *handle, unsigned char *key);
+void aes256_key(void *handle, unsigned char *key);
+void aes_iv(void *handle, unsigned char *iv);
+void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len);
+void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len);
/*
* PuTTY version number formatted as an SSH version string.
diff --git a/tools/plink/sshaes.c b/tools/plink/sshaes.c
index 089e6c5d8..7684cd949 100644
--- a/tools/plink/sshaes.c
+++ b/tools/plink/sshaes.c
@@ -1097,35 +1097,35 @@ static void aes_sdctr(unsigned char *blk, int len, AESContext *ctx)
memcpy(ctx->iv, iv, sizeof(iv));
}
-static void *aes_make_context(void)
+void *aes_make_context(void)
{
return snew(AESContext);
}
-static void aes_free_context(void *handle)
+void aes_free_context(void *handle)
{
sfree(handle);
}
-static void aes128_key(void *handle, unsigned char *key)
+void aes128_key(void *handle, unsigned char *key)
{
AESContext *ctx = (AESContext *)handle;
aes_setup(ctx, 16, key, 16);
}
-static void aes192_key(void *handle, unsigned char *key)
+void aes192_key(void *handle, unsigned char *key)
{
AESContext *ctx = (AESContext *)handle;
aes_setup(ctx, 16, key, 24);
}
-static void aes256_key(void *handle, unsigned char *key)
+void aes256_key(void *handle, unsigned char *key)
{
AESContext *ctx = (AESContext *)handle;
aes_setup(ctx, 16, key, 32);
}
-static void aes_iv(void *handle, unsigned char *iv)
+void aes_iv(void *handle, unsigned char *iv)
{
AESContext *ctx = (AESContext *)handle;
int i;
@@ -1133,13 +1133,13 @@ static void aes_iv(void *handle, unsigned char *iv)
ctx->iv[i] = GET_32BIT_MSB_FIRST(iv + 4 * i);
}
-static void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)
+void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)
{
AESContext *ctx = (AESContext *)handle;
aes_encrypt_cbc(blk, len, ctx);
}
-static void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)
+void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)
{
AESContext *ctx = (AESContext *)handle;
aes_decrypt_cbc(blk, len, ctx);
diff --git a/tools/plink/sshgss.h b/tools/plink/sshgss.h
index 2115cb124..5d8fca1b7 100644
--- a/tools/plink/sshgss.h
+++ b/tools/plink/sshgss.h
@@ -1,106 +1,188 @@
-#include "puttyps.h"
-
-#define SSH2_GSS_OIDTYPE 0x06
-typedef void *Ssh_gss_ctx;
-
-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
-
-#define SSH_GSS_CLEAR_BUF(buf) do { \
- (*buf).length = 0; \
- (*buf).value = 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);
+#ifndef PUTTY_SSHGSS_H
+#define PUTTY_SSHGSS_H
+#include "putty.h"
+#include "pgssapi.h"
+
+#ifndef NO_GSSAPI
+
+#define SSH2_GSS_OIDTYPE 0x06
+typedef void *Ssh_gss_ctx;
+
+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
+
+#define SSH_GSS_CLEAR_BUF(buf) do { \
+ (*buf).length = 0; \
+ (*buf).value = NULL; \
+} while (0)
+
+typedef gss_buffer_desc Ssh_gss_buf;
+typedef gss_name_t Ssh_gss_name;
+
+/* Functions, provided by either wingss.c or sshgssc.c */
+
+struct ssh_gss_library;
+
+/*
+ * Prepare a collection of GSSAPI libraries for use in a single SSH
+ * connection. Returns a structure containing a list of libraries,
+ * with their ids (see struct ssh_gss_library below) filled in so
+ * that the client can go through them in the SSH user's preferred
+ * order.
+ *
+ * Must always return non-NULL. (Even if no libraries are available,
+ * it must return an empty structure.)
+ *
+ * The free function cleans up the structure, and its associated
+ * libraries (if any).
+ */
+struct ssh_gss_liblist {
+ struct ssh_gss_library *libraries;
+ int nlibraries;
+};
+struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg);
+void ssh_gss_cleanup(struct ssh_gss_liblist *list);
+
+/*
+ * Fills in buf with a string describing the GSSAPI mechanism in
+ * use. buf->data is not dynamically allocated.
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_indicate_mech)(struct ssh_gss_library *lib,
+ 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().
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_import_name)(struct ssh_gss_library *lib,
+ char *in, Ssh_gss_name *out);
+
+/*
+ * Frees the contents of an Ssh_gss_name structure filled in by
+ * ssh_gss_import_name().
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_release_name)(struct ssh_gss_library *lib,
+ Ssh_gss_name *name);
+
+/*
+ * The main GSSAPI security context setup function. The "out"
+ * parameter will need to be freed by ssh_gss_free_tok.
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_init_sec_context)
+ (struct ssh_gss_library *lib,
+ 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.
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_free_tok)(struct ssh_gss_library *lib,
+ Ssh_gss_buf *);
+
+/*
+ * Acquires the credentials to perform authentication in the first
+ * place. Needs to be freed by ssh_gss_release_cred().
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_acquire_cred)(struct ssh_gss_library *lib,
+ Ssh_gss_ctx *);
+
+/*
+ * Frees the contents of an Ssh_gss_ctx filled in by
+ * ssh_gss_acquire_cred().
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_release_cred)(struct ssh_gss_library *lib,
+ Ssh_gss_ctx *);
+
+/*
+ * Gets a MIC for some input data. "out" needs to be freed by
+ * ssh_gss_free_mic().
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_get_mic)(struct ssh_gss_library *lib,
+ 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.
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_free_mic)(struct ssh_gss_library *lib,
+ 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.
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_display_status)(struct ssh_gss_library *lib,
+ Ssh_gss_ctx, Ssh_gss_buf *buf);
+
+struct ssh_gss_library {
+ /*
+ * Identifying number in the enumeration used by the
+ * configuration code to specify a preference order.
+ */
+ int id;
+
+ /*
+ * Filled in at initialisation time, if there's anything
+ * interesting to say about how GSSAPI was initialised (e.g.
+ * which of a number of alternative libraries was used).
+ */
+ const char *gsslogmsg;
+
+ /*
+ * Function pointers implementing the SSH wrapper layer on top
+ * of GSSAPI. (Defined in sshgssc, typically, though Windows
+ * provides an alternative layer to sit on top of the annoyingly
+ * different SSPI.)
+ */
+ t_ssh_gss_indicate_mech indicate_mech;
+ t_ssh_gss_import_name import_name;
+ t_ssh_gss_release_name release_name;
+ t_ssh_gss_init_sec_context init_sec_context;
+ t_ssh_gss_free_tok free_tok;
+ t_ssh_gss_acquire_cred acquire_cred;
+ t_ssh_gss_release_cred release_cred;
+ t_ssh_gss_get_mic get_mic;
+ t_ssh_gss_free_mic free_mic;
+ t_ssh_gss_display_status display_status;
+
+ /*
+ * Additional data for the wrapper layers.
+ */
+ union {
+ struct gssapi_functions gssapi;
+ /*
+ * The SSPI wrappers don't need to store their Windows API
+ * function pointers in this structure, because there can't
+ * be more than one set of them available.
+ */
+ } u;
+
+ /*
+ * Wrapper layers will often also need to store a library handle
+ * of some sort for cleanup time.
+ */
+ void *handle;
+};
+
+#endif /* NO_GSSAPI */
+
+#endif /*PUTTY_SSHGSS_H*/
diff --git a/tools/plink/sshrand.c b/tools/plink/sshrand.c
index 57ccc1393..91d9b3772 100644
--- a/tools/plink/sshrand.c
+++ b/tools/plink/sshrand.c
@@ -4,6 +4,7 @@
#include "putty.h"
#include "ssh.h"
+#include <assert.h>
/* Collect environmental noise every 5 minutes */
#define NOISE_REGULAR_INTERVAL (5*60*TICKSPERSEC)
@@ -225,6 +226,10 @@ void random_ref(void)
void random_unref(void)
{
random_active--;
+ assert(random_active >= 0);
+ if (random_active) return;
+
+ expire_timer_context(&pool);
}
int random_byte(void)
diff --git a/tools/plink/sshsh512.c b/tools/plink/sshsh512.c
index c025ffd17..bdb126fcf 100644
--- a/tools/plink/sshsh512.c
+++ b/tools/plink/sshsh512.c
@@ -13,14 +13,14 @@
* overlap destination with one source, but the others can't.
*/
#define add(r,x,y) ( r.lo = y.lo + x.lo, \
- r.hi = y.hi + x.hi + (r.lo < y.lo) )
-#define rorB(r,x,y) ( r.lo = (x.hi >> ((y)-32)) | (x.lo << (64-(y))), \
- r.hi = (x.lo >> ((y)-32)) | (x.hi << (64-(y))) )
-#define rorL(r,x,y) ( r.lo = (x.lo >> (y)) | (x.hi << (32-(y))), \
- r.hi = (x.hi >> (y)) | (x.lo << (32-(y))) )
-#define shrB(r,x,y) ( r.lo = x.hi >> ((y)-32), r.hi = 0 )
-#define shrL(r,x,y) ( r.lo = (x.lo >> (y)) | (x.hi << (32-(y))), \
- r.hi = x.hi >> (y) )
+ r.hi = y.hi + x.hi + ((uint32)r.lo < (uint32)y.lo) )
+#define rorB(r,x,y) ( r.lo = ((uint32)x.hi >> ((y)-32)) | ((uint32)x.lo << (64-(y))), \
+ r.hi = ((uint32)x.lo >> ((y)-32)) | ((uint32)x.hi << (64-(y))) )
+#define rorL(r,x,y) ( r.lo = ((uint32)x.lo >> (y)) | ((uint32)x.hi << (32-(y))), \
+ r.hi = ((uint32)x.hi >> (y)) | ((uint32)x.lo << (32-(y))) )
+#define shrB(r,x,y) ( r.lo = (uint32)x.hi >> ((y)-32), r.hi = 0 )
+#define shrL(r,x,y) ( r.lo = ((uint32)x.lo >> (y)) | ((uint32)x.hi << (32-(y))), \
+ r.hi = (uint32)x.hi >> (y) )
#define and(r,x,y) ( r.lo = x.lo & y.lo, r.hi = x.hi & y.hi )
#define xor(r,x,y) ( r.lo = x.lo ^ y.lo, r.hi = x.hi ^ y.hi )
#define not(r,x) ( r.lo = ~x.lo, r.hi = ~x.hi )
diff --git a/tools/plink/version.c b/tools/plink/version.c
index c0d28b884..483495771 100644
--- a/tools/plink/version.c
+++ b/tools/plink/version.c
@@ -5,6 +5,8 @@
#define STR1(x) #x
#define STR(x) STR1(x)
+#define SVN_REV 9025
+
#if defined SNAPSHOT
#if defined SVN_REV
@@ -25,7 +27,7 @@ char sshver[] = "PuTTY-Release-" STR(RELEASE);
#elif defined SVN_REV
-char ver[] = "Custom build r" STR(SVN_REV);
+char ver[] = "Custom build r" STR(SVN_REV) ", " __DATE__ " " __TIME__;
char sshver[] = "PuTTY-Custom-r" STR(SVN_REV);
#else
diff --git a/tools/plink/wingss.c b/tools/plink/wingss.c
index 742106e88..4efad5d58 100644
--- a/tools/plink/wingss.c
+++ b/tools/plink/wingss.c
@@ -1,322 +1,478 @@
-#ifndef NO_GSSAPI
-
-#include "putty.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);
- memset(winctx, 0, sizeof(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->length,SECBUFFER_TOKEN,send_tok->value};
- SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value};
- 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->value = wsend_tok.pvBuffer;
- send_tok->length = 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->value);
- SSH_GSS_CLEAR_BUF(send_tok);
-
- 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->value = dupstr(msg);
- buf->length = strlen(buf->value);
-
- 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->length;
- InputSecurityToken[0].pvBuffer = buf->value;
- 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->length = InputSecurityToken[1].cbBuffer;
- hash->value = InputSecurityToken[1].pvBuffer;
- }
-
- return winctx->maj_stat;
-}
-
-Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash)
-{
- sfree(hash->value);
- 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
+#ifndef NO_GSSAPI
+
+#include "putty.h"
+
+#include <security.h>
+
+#include "pgssapi.h"
+#include "sshgss.h"
+#include "sshgssc.h"
+
+#include "misc.h"
+
+/* Windows code to set up the GSSAPI library list. */
+
+const int ngsslibs = 3;
+const char *const gsslibnames[3] = {
+ "MIT Kerberos GSSAPI32.DLL",
+ "Microsoft SSPI SECUR32.DLL",
+ "User-specified GSSAPI DLL",
+};
+const struct keyval gsslibkeywords[] = {
+ { "gssapi32", 0 },
+ { "sspi", 1 },
+ { "custom", 2 },
+};
+
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
+ AcquireCredentialsHandleA,
+ (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
+ PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp));
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
+ InitializeSecurityContextA,
+ (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG,
+ ULONG, PSecBufferDesc, ULONG, PCtxtHandle,
+ PSecBufferDesc, PULONG, PTimeStamp));
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
+ FreeContextBuffer,
+ (PVOID));
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
+ FreeCredentialsHandle,
+ (PCredHandle));
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
+ DeleteSecurityContext,
+ (PCtxtHandle));
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
+ QueryContextAttributesA,
+ (PCtxtHandle, ULONG, PVOID));
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
+ MakeSignature,
+ (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
+
+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"};
+
+const char *gsslogmsg = NULL;
+
+static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
+
+struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
+{
+ HMODULE module;
+ HKEY regkey;
+ struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);
+
+ list->libraries = snewn(3, struct ssh_gss_library);
+ list->nlibraries = 0;
+
+ /* MIT Kerberos GSSAPI implementation */
+ /* TODO: For 64-bit builds, check for gssapi64.dll */
+ module = NULL;
+ if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", &regkey)
+ == ERROR_SUCCESS) {
+ DWORD type, size;
+ LONG ret;
+ char *buffer;
+
+ /* Find out the string length */
+ ret = RegQueryValueEx(regkey, "InstallDir", NULL, &type, NULL, &size);
+
+ if (ret == ERROR_SUCCESS && type == REG_SZ) {
+ buffer = snewn(size + 20, char);
+ ret = RegQueryValueEx(regkey, "InstallDir", NULL,
+ &type, buffer, &size);
+ if (ret == ERROR_SUCCESS && type == REG_SZ) {
+ strcat(buffer, "\\bin\\gssapi32.dll");
+ module = LoadLibrary(buffer);
+ }
+ sfree(buffer);
+ }
+ RegCloseKey(regkey);
+ }
+ if (module) {
+ struct ssh_gss_library *lib =
+ &list->libraries[list->nlibraries++];
+
+ lib->id = 0;
+ lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL";
+ lib->handle = (void *)module;
+
+#define BIND_GSS_FN(name) \
+ lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
+
+ BIND_GSS_FN(delete_sec_context);
+ BIND_GSS_FN(display_status);
+ BIND_GSS_FN(get_mic);
+ BIND_GSS_FN(import_name);
+ BIND_GSS_FN(init_sec_context);
+ BIND_GSS_FN(release_buffer);
+ BIND_GSS_FN(release_cred);
+ BIND_GSS_FN(release_name);
+
+#undef BIND_GSS_FN
+
+ ssh_gssapi_bind_fns(lib);
+ }
+
+ /* Microsoft SSPI Implementation */
+ module = load_system32_dll("secur32.dll");
+ if (module) {
+ struct ssh_gss_library *lib =
+ &list->libraries[list->nlibraries++];
+
+ lib->id = 1;
+ lib->gsslogmsg = "Using SSPI from SECUR32.DLL";
+ lib->handle = (void *)module;
+
+ GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA);
+ GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA);
+ GET_WINDOWS_FUNCTION(module, FreeContextBuffer);
+ GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle);
+ GET_WINDOWS_FUNCTION(module, DeleteSecurityContext);
+ GET_WINDOWS_FUNCTION(module, QueryContextAttributesA);
+ GET_WINDOWS_FUNCTION(module, MakeSignature);
+
+ ssh_sspi_bind_fns(lib);
+ }
+
+ /*
+ * Custom GSSAPI DLL.
+ */
+ module = NULL;
+ if (cfg->ssh_gss_custom.path[0]) {
+ module = LoadLibrary(cfg->ssh_gss_custom.path);
+ }
+ if (module) {
+ struct ssh_gss_library *lib =
+ &list->libraries[list->nlibraries++];
+
+ lib->id = 2;
+ lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified"
+ " library '%s'", cfg->ssh_gss_custom.path);
+ lib->handle = (void *)module;
+
+#define BIND_GSS_FN(name) \
+ lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
+
+ BIND_GSS_FN(delete_sec_context);
+ BIND_GSS_FN(display_status);
+ BIND_GSS_FN(get_mic);
+ BIND_GSS_FN(import_name);
+ BIND_GSS_FN(init_sec_context);
+ BIND_GSS_FN(release_buffer);
+ BIND_GSS_FN(release_cred);
+ BIND_GSS_FN(release_name);
+
+#undef BIND_GSS_FN
+
+ ssh_gssapi_bind_fns(lib);
+ }
+
+
+ return list;
+}
+
+void ssh_gss_cleanup(struct ssh_gss_liblist *list)
+{
+ int i;
+
+ /*
+ * LoadLibrary and FreeLibrary are defined to employ reference
+ * counting in the case where the same library is repeatedly
+ * loaded, so even in a multiple-sessions-per-process context
+ * (not that we currently expect ever to have such a thing on
+ * Windows) it's safe to naively FreeLibrary everything here
+ * without worrying about destroying it under the feet of
+ * another SSH instance still using it.
+ */
+ for (i = 0; i < list->nlibraries; i++) {
+ FreeLibrary((HMODULE)list->libraries[i].handle);
+ if (list->libraries[i].id == 2) {
+ /* The 'custom' id involves a dynamically allocated message.
+ * Note that we must cast away the 'const' to free it. */
+ sfree((char *)list->libraries[i].gsslogmsg);
+ }
+ }
+ sfree(list->libraries);
+ sfree(list);
+}
+
+static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib,
+ Ssh_gss_buf *mech)
+{
+ *mech = gss_mech_krb5;
+ return SSH_GSS_OK;
+}
+
+
+static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
+ 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;
+}
+
+static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
+ Ssh_gss_ctx *ctx)
+{
+ winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
+ memset(winctx, 0, sizeof(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;
+}
+
+
+static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
+ 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->length,SECBUFFER_TOKEN,send_tok->value};
+ SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value};
+ 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->value = wsend_tok.pvBuffer;
+ send_tok->length = 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;
+}
+
+static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib,
+ Ssh_gss_buf *send_tok)
+{
+ /* check input */
+ if (send_tok == NULL) return SSH_GSS_FAILURE;
+
+ /* free Windows buffer */
+ p_FreeContextBuffer(send_tok->value);
+ SSH_GSS_CLEAR_BUF(send_tok);
+
+ return SSH_GSS_OK;
+}
+
+static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib,
+ 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;
+}
+
+
+static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib,
+ 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;
+}
+
+static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib,
+ 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->value = dupstr(msg);
+ buf->length = strlen(buf->value);
+
+ return SSH_GSS_OK;
+}
+
+static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib,
+ 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->length;
+ InputSecurityToken[0].pvBuffer = buf->value;
+ 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->length = InputSecurityToken[1].cbBuffer;
+ hash->value = InputSecurityToken[1].pvBuffer;
+ }
+
+ return winctx->maj_stat;
+}
+
+static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib,
+ Ssh_gss_buf *hash)
+{
+ sfree(hash->value);
+ return SSH_GSS_OK;
+}
+
+static void ssh_sspi_bind_fns(struct ssh_gss_library *lib)
+{
+ lib->indicate_mech = ssh_sspi_indicate_mech;
+ lib->import_name = ssh_sspi_import_name;
+ lib->release_name = ssh_sspi_release_name;
+ lib->init_sec_context = ssh_sspi_init_sec_context;
+ lib->free_tok = ssh_sspi_free_tok;
+ lib->acquire_cred = ssh_sspi_acquire_cred;
+ lib->release_cred = ssh_sspi_release_cred;
+ lib->get_mic = ssh_sspi_get_mic;
+ lib->free_mic = ssh_sspi_free_mic;
+ lib->display_status = ssh_sspi_display_status;
+}
+
+#else
+
+/* Dummy function so this source file defines something if NO_GSSAPI
+ is defined. */
+
+void ssh_gss_init(void)
+{
+}
+
+#endif
diff --git a/tools/plink/winhelp.h b/tools/plink/winhelp.h
index 95cfaba15..9ee140fef 100644
--- a/tools/plink/winhelp.h
+++ b/tools/plink/winhelp.h
@@ -108,6 +108,9 @@
#define WINHELP_CTX_ssh_auth_pageant "ssh.auth.pageant:config-ssh-tryagent"
#define WINHELP_CTX_ssh_auth_tis "ssh.auth.tis:config-ssh-tis"
#define WINHELP_CTX_ssh_auth_ki "ssh.auth.ki:config-ssh-ki"
+#define WINHELP_CTX_ssh_gssapi "ssh.auth.gssapi:config-ssh-auth-gssapi"
+#define WINHELP_CTX_ssh_gssapi_delegation "ssh.auth.gssapi.delegation:config-ssh-auth-gssapi-delegation"
+#define WINHELP_CTX_ssh_gssapi_libraries "ssh.auth.gssapi.libraries:config-ssh-auth-gssapi-libraries"
#define WINHELP_CTX_selection_buttons "selection.buttons:config-mouse"
#define WINHELP_CTX_selection_shiftdrag "selection.shiftdrag:config-mouseshift"
#define WINHELP_CTX_selection_rect "selection.rect:config-rectselect"
@@ -133,6 +136,7 @@
#define WINHELP_CTX_ssh_bugs_ignore1 "ssh.bugs.ignore1:config-ssh-bug-ignore1"
#define WINHELP_CTX_ssh_bugs_plainpw1 "ssh.bugs.plainpw1:config-ssh-bug-plainpw1"
#define WINHELP_CTX_ssh_bugs_rsa1 "ssh.bugs.rsa1:config-ssh-bug-rsa1"
+#define WINHELP_CTX_ssh_bugs_ignore2 "ssh.bugs.ignore2:config-ssh-bug-ignore2"
#define WINHELP_CTX_ssh_bugs_hmac2 "ssh.bugs.hmac2:config-ssh-bug-hmac2"
#define WINHELP_CTX_ssh_bugs_derivekey2 "ssh.bugs.derivekey2:config-ssh-bug-derivekey2"
#define WINHELP_CTX_ssh_bugs_rsapad2 "ssh.bugs.rsapad2:config-ssh-bug-sig"
diff --git a/tools/plink/winmisc.c b/tools/plink/winmisc.c
index 8ffbe77a1..e70e77efa 100644
--- a/tools/plink/winmisc.c
+++ b/tools/plink/winmisc.c
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include "putty.h"
+#include <security.h>
OSVERSIONINFO osVersion;
@@ -40,21 +41,61 @@ char *get_username(void)
{
DWORD namelen;
char *user;
+ int got_username = FALSE;
+ DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
+ (EXTENDED_NAME_FORMAT, LPSTR, PULONG));
+
+ {
+ static int tried_usernameex = FALSE;
+ if (!tried_usernameex) {
+ /* Not available on Win9x, so load dynamically */
+ HMODULE secur32 = load_system32_dll("secur32.dll");
+ GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
+ tried_usernameex = TRUE;
+ }
+ }
- namelen = 0;
- if (GetUserName(NULL, &namelen) == FALSE) {
+ if (p_GetUserNameExA) {
/*
- * Apparently this doesn't work at least on Windows XP SP2.
- * Thus assume a maximum of 256. It will fail again if it
- * doesn't fit.
+ * If available, use the principal -- this avoids the problem
+ * that the local username is case-insensitive but Kerberos
+ * usernames are case-sensitive.
*/
- namelen = 256;
+
+ /* Get the length */
+ namelen = 0;
+ (void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
+
+ user = snewn(namelen, char);
+ got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
+ if (got_username) {
+ char *p = strchr(user, '@');
+ if (p) *p = 0;
+ } else {
+ sfree(user);
+ }
}
- user = snewn(namelen, char);
- GetUserName(user, &namelen);
+ if (!got_username) {
+ /* Fall back to local user name */
+ namelen = 0;
+ if (GetUserName(NULL, &namelen) == FALSE) {
+ /*
+ * Apparently this doesn't work at least on Windows XP SP2.
+ * Thus assume a maximum of 256. It will fail again if it
+ * doesn't fit.
+ */
+ namelen = 256;
+ }
+
+ user = snewn(namelen, char);
+ got_username = GetUserName(user, &namelen);
+ if (!got_username) {
+ sfree(user);
+ }
+ }
- return user;
+ return got_username ? user : NULL;
}
BOOL init_winver(void)
@@ -64,6 +105,33 @@ BOOL init_winver(void)
return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
}
+HMODULE load_system32_dll(const char *libname)
+{
+ /*
+ * Wrapper function to load a DLL out of c:\windows\system32
+ * without going through the full DLL search path. (Hence no
+ * attack is possible by placing a substitute DLL earlier on that
+ * path.)
+ */
+ static char *sysdir = NULL;
+ char *fullpath;
+ HMODULE ret;
+
+ if (!sysdir) {
+ int size = 0, len;
+ do {
+ size = 3*size/2 + 512;
+ sysdir = sresize(sysdir, size, char);
+ len = GetSystemDirectory(sysdir, size);
+ } while (len >= size);
+ }
+
+ fullpath = dupcat(sysdir, "\\", libname, NULL);
+ ret = LoadLibrary(fullpath);
+ sfree(fullpath);
+ return ret;
+}
+
#ifdef DEBUG
static FILE *debug_fp = NULL;
static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
diff --git a/tools/plink/winnet.c b/tools/plink/winnet.c
index 59f3774e0..8da79dbe7 100644
--- a/tools/plink/winnet.c
+++ b/tools/plink/winnet.c
@@ -144,67 +144,51 @@ static int cmpforsearch(void *av, void *bv)
return 0;
}
-#define NOTHING
-#define DECL_WINSOCK_FUNCTION(linkage, rettype, name, params) \
- typedef rettype (WINAPI *t_##name) params; \
- linkage t_##name p_##name
-#define GET_WINSOCK_FUNCTION(module, name) \
- p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL
-
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAAsyncSelect,
- (SOCKET, HWND, u_int, long));
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAEventSelect, (SOCKET, WSAEVENT, long));
-DECL_WINSOCK_FUNCTION(NOTHING, int, select,
- (int, fd_set FAR *, fd_set FAR *,
- fd_set FAR *, const struct timeval FAR *));
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAGetLastError, (void));
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAEnumNetworkEvents,
- (SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
-DECL_WINSOCK_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA));
-DECL_WINSOCK_FUNCTION(static, int, WSACleanup, (void));
-DECL_WINSOCK_FUNCTION(static, int, closesocket, (SOCKET));
-DECL_WINSOCK_FUNCTION(static, u_long, ntohl, (u_long));
-DECL_WINSOCK_FUNCTION(static, u_long, htonl, (u_long));
-DECL_WINSOCK_FUNCTION(static, u_short, htons, (u_short));
-DECL_WINSOCK_FUNCTION(static, u_short, ntohs, (u_short));
-DECL_WINSOCK_FUNCTION(static, int, gethostname, (char *, int));
-DECL_WINSOCK_FUNCTION(static, struct hostent FAR *, gethostbyname,
+DECL_WINDOWS_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA));
+DECL_WINDOWS_FUNCTION(static, int, WSACleanup, (void));
+DECL_WINDOWS_FUNCTION(static, int, closesocket, (SOCKET));
+DECL_WINDOWS_FUNCTION(static, u_long, ntohl, (u_long));
+DECL_WINDOWS_FUNCTION(static, u_long, htonl, (u_long));
+DECL_WINDOWS_FUNCTION(static, u_short, htons, (u_short));
+DECL_WINDOWS_FUNCTION(static, u_short, ntohs, (u_short));
+DECL_WINDOWS_FUNCTION(static, int, gethostname, (char *, int));
+DECL_WINDOWS_FUNCTION(static, struct hostent FAR *, gethostbyname,
(const char FAR *));
-DECL_WINSOCK_FUNCTION(static, struct servent FAR *, getservbyname,
+DECL_WINDOWS_FUNCTION(static, struct servent FAR *, getservbyname,
(const char FAR *, const char FAR *));
-DECL_WINSOCK_FUNCTION(static, unsigned long, inet_addr, (const char FAR *));
-DECL_WINSOCK_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr));
-DECL_WINSOCK_FUNCTION(static, int, connect,
+DECL_WINDOWS_FUNCTION(static, unsigned long, inet_addr, (const char FAR *));
+DECL_WINDOWS_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr));
+DECL_WINDOWS_FUNCTION(static, int, connect,
(SOCKET, const struct sockaddr FAR *, int));
-DECL_WINSOCK_FUNCTION(static, int, bind,
+DECL_WINDOWS_FUNCTION(static, int, bind,
(SOCKET, const struct sockaddr FAR *, int));
-DECL_WINSOCK_FUNCTION(static, int, setsockopt,
+DECL_WINDOWS_FUNCTION(static, int, setsockopt,
(SOCKET, int, int, const char FAR *, int));
-DECL_WINSOCK_FUNCTION(static, SOCKET, socket, (int, int, int));
-DECL_WINSOCK_FUNCTION(static, int, listen, (SOCKET, int));
-DECL_WINSOCK_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int));
-DECL_WINSOCK_FUNCTION(static, int, ioctlsocket,
+DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int));
+DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int));
+DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int));
+DECL_WINDOWS_FUNCTION(static, int, ioctlsocket,
(SOCKET, long, u_long FAR *));
-DECL_WINSOCK_FUNCTION(static, SOCKET, accept,
+DECL_WINDOWS_FUNCTION(static, SOCKET, accept,
(SOCKET, struct sockaddr FAR *, int FAR *));
-DECL_WINSOCK_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int));
-DECL_WINSOCK_FUNCTION(static, int, WSAIoctl,
+DECL_WINDOWS_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int));
+DECL_WINDOWS_FUNCTION(static, int, WSAIoctl,
(SOCKET, DWORD, LPVOID, DWORD, LPVOID, DWORD,
LPDWORD, LPWSAOVERLAPPED,
LPWSAOVERLAPPED_COMPLETION_ROUTINE));
#ifndef NO_IPV6
-DECL_WINSOCK_FUNCTION(static, int, getaddrinfo,
+DECL_WINDOWS_FUNCTION(static, int, getaddrinfo,
(const char *nodename, const char *servname,
const struct addrinfo *hints, struct addrinfo **res));
-DECL_WINSOCK_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res));
-DECL_WINSOCK_FUNCTION(static, int, getnameinfo,
+DECL_WINDOWS_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res));
+DECL_WINDOWS_FUNCTION(static, int, getnameinfo,
(const struct sockaddr FAR * sa, socklen_t salen,
char FAR * host, size_t hostlen, char FAR * serv,
size_t servlen, int flags));
-DECL_WINSOCK_FUNCTION(static, char *, gai_strerror, (int ecode));
-DECL_WINSOCK_FUNCTION(static, int, WSAAddressToStringA,
+DECL_WINDOWS_FUNCTION(static, char *, gai_strerror, (int ecode));
+DECL_WINDOWS_FUNCTION(static, int, WSAAddressToStringA,
(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO,
- LPTSTR, LPDWORD));
+ LPSTR, LPDWORD));
#endif
static HMODULE winsock_module = NULL;
@@ -243,9 +227,9 @@ void sk_init(void)
#ifndef NO_IPV6
winsock2_module =
#endif
- winsock_module = LoadLibrary("WS2_32.DLL");
+ winsock_module = load_system32_dll("ws2_32.dll");
if (!winsock_module) {
- winsock_module = LoadLibrary("WSOCK32.DLL");
+ winsock_module = load_system32_dll("wsock32.dll");
}
if (!winsock_module)
fatalbox("Unable to load any WinSock library");
@@ -256,61 +240,61 @@ void sk_init(void)
#ifdef NET_SETUP_DIAGNOSTICS
logevent(NULL, "Native WinSock IPv6 support detected");
#endif
- GET_WINSOCK_FUNCTION(winsock_module, getaddrinfo);
- GET_WINSOCK_FUNCTION(winsock_module, freeaddrinfo);
- GET_WINSOCK_FUNCTION(winsock_module, getnameinfo);
- GET_WINSOCK_FUNCTION(winsock_module, gai_strerror);
+ GET_WINDOWS_FUNCTION(winsock_module, getaddrinfo);
+ GET_WINDOWS_FUNCTION(winsock_module, freeaddrinfo);
+ GET_WINDOWS_FUNCTION(winsock_module, getnameinfo);
+ GET_WINDOWS_FUNCTION(winsock_module, gai_strerror);
} else {
/* Fall back to wship6.dll for Windows 2000 */
- wship6_module = LoadLibrary("wship6.dll");
+ wship6_module = load_system32_dll("wship6.dll");
if (wship6_module) {
#ifdef NET_SETUP_DIAGNOSTICS
logevent(NULL, "WSH IPv6 support detected");
#endif
- GET_WINSOCK_FUNCTION(wship6_module, getaddrinfo);
- GET_WINSOCK_FUNCTION(wship6_module, freeaddrinfo);
- GET_WINSOCK_FUNCTION(wship6_module, getnameinfo);
- GET_WINSOCK_FUNCTION(wship6_module, gai_strerror);
+ GET_WINDOWS_FUNCTION(wship6_module, getaddrinfo);
+ GET_WINDOWS_FUNCTION(wship6_module, freeaddrinfo);
+ GET_WINDOWS_FUNCTION(wship6_module, getnameinfo);
+ GET_WINDOWS_FUNCTION(wship6_module, gai_strerror);
} else {
#ifdef NET_SETUP_DIAGNOSTICS
logevent(NULL, "No IPv6 support detected");
#endif
}
}
- GET_WINSOCK_FUNCTION(winsock2_module, WSAAddressToStringA);
+ GET_WINDOWS_FUNCTION(winsock2_module, WSAAddressToStringA);
#else
#ifdef NET_SETUP_DIAGNOSTICS
logevent(NULL, "PuTTY was built without IPv6 support");
#endif
#endif
- GET_WINSOCK_FUNCTION(winsock_module, WSAAsyncSelect);
- GET_WINSOCK_FUNCTION(winsock_module, WSAEventSelect);
- GET_WINSOCK_FUNCTION(winsock_module, select);
- GET_WINSOCK_FUNCTION(winsock_module, WSAGetLastError);
- GET_WINSOCK_FUNCTION(winsock_module, WSAEnumNetworkEvents);
- GET_WINSOCK_FUNCTION(winsock_module, WSAStartup);
- GET_WINSOCK_FUNCTION(winsock_module, WSACleanup);
- GET_WINSOCK_FUNCTION(winsock_module, closesocket);
- GET_WINSOCK_FUNCTION(winsock_module, ntohl);
- GET_WINSOCK_FUNCTION(winsock_module, htonl);
- GET_WINSOCK_FUNCTION(winsock_module, htons);
- GET_WINSOCK_FUNCTION(winsock_module, ntohs);
- GET_WINSOCK_FUNCTION(winsock_module, gethostname);
- GET_WINSOCK_FUNCTION(winsock_module, gethostbyname);
- GET_WINSOCK_FUNCTION(winsock_module, getservbyname);
- GET_WINSOCK_FUNCTION(winsock_module, inet_addr);
- GET_WINSOCK_FUNCTION(winsock_module, inet_ntoa);
- GET_WINSOCK_FUNCTION(winsock_module, connect);
- GET_WINSOCK_FUNCTION(winsock_module, bind);
- GET_WINSOCK_FUNCTION(winsock_module, setsockopt);
- GET_WINSOCK_FUNCTION(winsock_module, socket);
- GET_WINSOCK_FUNCTION(winsock_module, listen);
- GET_WINSOCK_FUNCTION(winsock_module, send);
- GET_WINSOCK_FUNCTION(winsock_module, ioctlsocket);
- GET_WINSOCK_FUNCTION(winsock_module, accept);
- GET_WINSOCK_FUNCTION(winsock_module, recv);
- GET_WINSOCK_FUNCTION(winsock_module, WSAIoctl);
+ GET_WINDOWS_FUNCTION(winsock_module, WSAAsyncSelect);
+ GET_WINDOWS_FUNCTION(winsock_module, WSAEventSelect);
+ GET_WINDOWS_FUNCTION(winsock_module, select);
+ GET_WINDOWS_FUNCTION(winsock_module, WSAGetLastError);
+ GET_WINDOWS_FUNCTION(winsock_module, WSAEnumNetworkEvents);
+ GET_WINDOWS_FUNCTION(winsock_module, WSAStartup);
+ GET_WINDOWS_FUNCTION(winsock_module, WSACleanup);
+ GET_WINDOWS_FUNCTION(winsock_module, closesocket);
+ GET_WINDOWS_FUNCTION(winsock_module, ntohl);
+ GET_WINDOWS_FUNCTION(winsock_module, htonl);
+ GET_WINDOWS_FUNCTION(winsock_module, htons);
+ GET_WINDOWS_FUNCTION(winsock_module, ntohs);
+ GET_WINDOWS_FUNCTION(winsock_module, gethostname);
+ GET_WINDOWS_FUNCTION(winsock_module, gethostbyname);
+ GET_WINDOWS_FUNCTION(winsock_module, getservbyname);
+ GET_WINDOWS_FUNCTION(winsock_module, inet_addr);
+ GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa);
+ GET_WINDOWS_FUNCTION(winsock_module, connect);
+ GET_WINDOWS_FUNCTION(winsock_module, bind);
+ GET_WINDOWS_FUNCTION(winsock_module, setsockopt);
+ GET_WINDOWS_FUNCTION(winsock_module, socket);
+ GET_WINDOWS_FUNCTION(winsock_module, listen);
+ GET_WINDOWS_FUNCTION(winsock_module, send);
+ GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket);
+ GET_WINDOWS_FUNCTION(winsock_module, accept);
+ GET_WINDOWS_FUNCTION(winsock_module, recv);
+ GET_WINDOWS_FUNCTION(winsock_module, WSAIoctl);
/* Try to get the best WinSock version we can get */
if (!sk_startup(2,2) &&
@@ -335,7 +319,8 @@ void sk_cleanup(void)
sktree = NULL;
}
- p_WSACleanup();
+ if (p_WSACleanup)
+ p_WSACleanup();
if (winsock_module)
FreeLibrary(winsock_module);
#ifndef NO_IPV6
@@ -428,10 +413,8 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
{
SockAddr ret = snew(struct SockAddr_tag);
unsigned long a;
- struct hostent *h = NULL;
char realhost[8192];
int hint_family;
- int err;
/* Default to IPv4. */
hint_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
@@ -451,6 +434,8 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
*realhost = '\0';
if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) {
+ struct hostent *h = NULL;
+ int err;
#ifndef NO_IPV6
/*
* Use getaddrinfo when it's available
@@ -583,11 +568,19 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen)
#ifndef NO_IPV6
if (step.ai) {
+ int err = 0;
if (p_WSAAddressToStringA) {
- p_WSAAddressToStringA(step.ai->ai_addr, step.ai->ai_addrlen,
- NULL, buf, &buflen);
+ DWORD dwbuflen = buflen;
+ err = p_WSAAddressToStringA(step.ai->ai_addr, step.ai->ai_addrlen,
+ NULL, buf, &dwbuflen);
} else
- strncpy(buf, "IPv6", buflen);
+ err = -1;
+ if (err) {
+ strncpy(buf, addr->hostname, buflen);
+ if (!buf[0])
+ strncpy(buf, "<unknown>", buflen);
+ buf[buflen-1] = '\0';
+ }
} else
#endif
if (SOCKADDR_FAMILY(addr, step) == AF_INET) {
diff --git a/tools/plink/winplink.c b/tools/plink/winplink.c
index 525b1a212..7af9e1b4f 100644
--- a/tools/plink/winplink.c
+++ b/tools/plink/winplink.c
@@ -166,7 +166,7 @@ static void usage(void)
printf(" -pgpfp print PGP key fingerprints and exit\n");
printf(" -v show verbose messages\n");
printf(" -load sessname Load settings from saved session\n");
- printf(" -ssh -telnet -rlogin -raw\n");
+ printf(" -ssh -telnet -rlogin -raw -serial\n");
printf(" force use of a particular protocol\n");
printf(" -P port connect to specified port\n");
printf(" -l user connect with specified username\n");
@@ -193,6 +193,8 @@ static void usage(void)
printf(" -N don't start a shell/command (SSH-2 only)\n");
printf(" -nc host:port\n");
printf(" open tunnel in place of session (SSH-2 only)\n");
+ printf(" -sercfg configuration-string (e.g. 19200,8,n,1,X)\n");
+ printf(" Specify the serial configuration (serial only)\n");
exit(1);
}
@@ -279,6 +281,7 @@ int main(int argc, char **argv)
int skcount, sksize;
int exitcode;
int errors;
+ int got_host = FALSE;
int use_subsystem = 0;
long now, next;
@@ -341,7 +344,7 @@ int main(int argc, char **argv)
errors = 1;
}
} else if (*p) {
- if (!cfg_launchable(&cfg)) {
+ if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
char *q = p;
/*
* If the hostname starts with "telnet:", set the
@@ -367,6 +370,7 @@ int main(int argc, char **argv)
cfg.port = -1;
strncpy(cfg.host, q, sizeof(cfg.host) - 1);
cfg.host[sizeof(cfg.host) - 1] = '\0';
+ got_host = TRUE;
} else {
char *r, *user, *host;
/*
@@ -415,8 +419,10 @@ int main(int argc, char **argv)
strncpy(cfg.host, host, sizeof(cfg.host) - 1);
cfg.host[sizeof(cfg.host) - 1] = '\0';
cfg.port = default_port;
+ got_host = TRUE;
} else {
cfg = cfg2;
+ loaded_session = TRUE;
}
}
@@ -463,7 +469,7 @@ int main(int argc, char **argv)
if (errors)
return 1;
- if (!cfg_launchable(&cfg)) {
+ if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
usage();
}
diff --git a/tools/plink/winproxy.c b/tools/plink/winproxy.c
index 45998e400..4da4d2e01 100644
--- a/tools/plink/winproxy.c
+++ b/tools/plink/winproxy.c
@@ -199,6 +199,8 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
NULL, NULL, &si, &pi);
+ sfree(cmd);
+
CloseHandle(cmd_from_us);
CloseHandle(cmd_to_us);
diff --git a/tools/plink/winstore.c b/tools/plink/winstore.c
index b1b3d3a66..f7dd98965 100644
--- a/tools/plink/winstore.c
+++ b/tools/plink/winstore.c
@@ -23,9 +23,8 @@ static const char hex[16] = "0123456789ABCDEF";
static int tried_shgetfolderpath = FALSE;
static HMODULE shell32_module = NULL;
-typedef HRESULT (WINAPI *p_SHGetFolderPath_t)
- (HWND, int, HANDLE, DWORD, LPTSTR);
-static p_SHGetFolderPath_t p_SHGetFolderPath = NULL;
+DECL_WINDOWS_FUNCTION(static, HRESULT, SHGetFolderPathA,
+ (HWND, int, HANDLE, DWORD, LPSTR));
static void mungestr(const char *in, char *out)
{
@@ -498,22 +497,20 @@ static HANDLE access_random_seed(int action)
* on older versions of Windows if we cared enough.
* However, the invocation below requires IE5+ anyway,
* so stuff that. */
- shell32_module = LoadLibrary("SHELL32.DLL");
- if (shell32_module) {
- p_SHGetFolderPath = (p_SHGetFolderPath_t)
- GetProcAddress(shell32_module, "SHGetFolderPathA");
- }
+ shell32_module = load_system32_dll("shell32.dll");
+ GET_WINDOWS_FUNCTION(shell32_module, SHGetFolderPathA);
+ tried_shgetfolderpath = TRUE;
}
- if (p_SHGetFolderPath) {
- if (SUCCEEDED(p_SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA,
- NULL, SHGFP_TYPE_CURRENT, seedpath))) {
+ if (p_SHGetFolderPathA) {
+ if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA,
+ NULL, SHGFP_TYPE_CURRENT, seedpath))) {
strcat(seedpath, "\\PUTTY.RND");
if (try_random_seed(seedpath, action, &rethandle))
return rethandle;
}
- if (SUCCEEDED(p_SHGetFolderPath(NULL, CSIDL_APPDATA,
- NULL, SHGFP_TYPE_CURRENT, seedpath))) {
+ if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA,
+ NULL, SHGFP_TYPE_CURRENT, seedpath))) {
strcat(seedpath, "\\PUTTY.RND");
if (try_random_seed(seedpath, action, &rethandle))
return rethandle;
diff --git a/tools/plink/winstuff.h b/tools/plink/winstuff.h
index 09a515a62..eeceea17a 100644
--- a/tools/plink/winstuff.h
+++ b/tools/plink/winstuff.h
@@ -74,6 +74,33 @@ struct FontSpec {
#define DF_END 0x0001
/*
+ * Dynamically linked functions. These come in two flavours:
+ *
+ * - GET_WINDOWS_FUNCTION does not expose "name" to the preprocessor,
+ * so will always dynamically link against exactly what is specified
+ * in "name". If you're not sure, use this one.
+ *
+ * - GET_WINDOWS_FUNCTION_PP allows "name" to be redirected via
+ * preprocessor definitions like "#define foo bar"; this is principally
+ * intended for the ANSI/Unicode DoSomething/DoSomethingA/DoSomethingW.
+ * If your function has an argument of type "LPTSTR" or similar, this
+ * is the variant to use.
+ * (However, it can't always be used, as it trips over more complicated
+ * macro trickery such as the WspiapiGetAddrInfo wrapper for getaddrinfo.)
+ *
+ * (DECL_WINDOWS_FUNCTION works with both these variants.)
+ */
+#define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \
+ typedef rettype (WINAPI *t_##name) params; \
+ linkage t_##name p_##name
+#define STR1(x) #x
+#define STR(x) STR1(x)
+#define GET_WINDOWS_FUNCTION_PP(module, name) \
+ p_##name = module ? (t_##name) GetProcAddress(module, STR(name)) : NULL
+#define GET_WINDOWS_FUNCTION(module, name) \
+ p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL
+
+/*
* Global variables. Most modules declare these `extern', but
* window.c will do `#define PUTTY_DO_GLOBALS' before including this
* module, and so will get them properly defined.
@@ -111,17 +138,23 @@ typedef struct terminal_tag Terminal;
typedef HDC Context;
+typedef unsigned int uint32; /* int is 32-bits on Win32 and Win64. */
+#define PUTTY_UINT32_DEFINED
+
#ifndef NO_GSSAPI
/*
* GSS-API stuff
*/
+#define GSS_CC CALLBACK
+/*
typedef struct Ssh_gss_buf {
- int length;
+ size_t length;
char *value;
} Ssh_gss_buf;
#define SSH_GSS_EMPTY_BUF (Ssh_gss_buf) {0,NULL}
typedef void *Ssh_gss_name;
+*/
#endif
/*
@@ -189,6 +222,8 @@ GLOBAL void *logctx;
"All Files (*.*)\0*\0\0\0")
#define FILTER_WAVE_FILES ("Wave Files (*.wav)\0*.WAV\0" \
"All Files (*.*)\0*\0\0\0")
+#define FILTER_DYNLIB_FILES ("Dynamic Library Files (*.dll)\0*.dll\0" \
+ "All Files (*.*)\0*\0\0\0")
/*
* On some versions of Windows, it has been known for WM_TIMER to
@@ -205,16 +240,16 @@ GLOBAL void *logctx;
* that module must be exported from it as function pointers. So
* here they are.
*/
-extern int (WINAPI *p_WSAAsyncSelect)
- (SOCKET s, HWND hWnd, u_int wMsg, long lEvent);
-extern int (WINAPI *p_WSAEventSelect)
- (SOCKET s, WSAEVENT hEventObject, long lNetworkEvents);
-extern int (WINAPI *p_select)
- (int nfds, fd_set FAR * readfds, fd_set FAR * writefds,
- fd_set FAR *exceptfds, const struct timeval FAR * timeout);
-extern int (WINAPI *p_WSAGetLastError)(void);
-extern int (WINAPI *p_WSAEnumNetworkEvents)
- (SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAAsyncSelect,
+ (SOCKET, HWND, u_int, long));
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAEventSelect,
+ (SOCKET, WSAEVENT, long));
+DECL_WINDOWS_FUNCTION(GLOBAL, int, select,
+ (int, fd_set FAR *, fd_set FAR *,
+ fd_set FAR *, const struct timeval FAR *));
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAGetLastError, (void));
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAEnumNetworkEvents,
+ (SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
extern int socket_writable(SOCKET skt);
@@ -413,6 +448,7 @@ void show_help(HWND hwnd);
*/
extern OSVERSIONINFO osVersion;
BOOL init_winver(void);
+HMODULE load_system32_dll(const char *libname);
/*
* Exports from sizetip.c.