diff options
Diffstat (limited to 'tools/plink')
-rw-r--r-- | tools/plink/cmdline.c | 15 | ||||
-rw-r--r-- | tools/plink/misc.c | 148 | ||||
-rwxr-xr-x | tools/plink/misc.h | 3 | ||||
-rw-r--r-- | tools/plink/putty.h | 6 | ||||
-rw-r--r-- | tools/plink/settings.c | 31 | ||||
-rwxr-xr-x | tools/plink/ssh.c | 166 | ||||
-rw-r--r-- | tools/plink/sshpubk.c | 48 | ||||
-rw-r--r-- | tools/plink/sshshare.c | 5 | ||||
-rw-r--r-- | tools/plink/winhelp.h | 1 | ||||
-rwxr-xr-x | tools/plink/winplink.c | 8 | ||||
-rw-r--r-- | tools/plink/winstore.c | 9 |
11 files changed, 325 insertions, 115 deletions
diff --git a/tools/plink/cmdline.c b/tools/plink/cmdline.c index 0c7ef4c91..327a585a4 100644 --- a/tools/plink/cmdline.c +++ b/tools/plink/cmdline.c @@ -236,6 +236,21 @@ int cmdline_process_param(char *p, char *value, int need_save, Conf *conf) SAVEABLE(0); conf_set_str(conf, CONF_loghost, value); } + if (!strcmp(p, "-hostkey")) { + char *dup; + RETURN(2); + UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); + SAVEABLE(0); + dup = dupstr(value); + if (!validate_manual_hostkey(dup)) { + cmdline_error("'%s' is not a valid format for a manual host " + "key specification", value); + sfree(dup); + return ret; + } + conf_set_str_str(conf, CONF_ssh_manual_hostkeys, dup, ""); + sfree(dup); + } if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) { char type, *q, *qq, *key, *val; RETURN(2); diff --git a/tools/plink/misc.c b/tools/plink/misc.c index d7c32c49d..24e42ebac 100644 --- a/tools/plink/misc.c +++ b/tools/plink/misc.c @@ -473,8 +473,7 @@ char *fgetline(FILE *fp) } /* ---------------------------------------------------------------------- - * Base64 encoding routine. This is required in public-key writing - * but also in HTTP proxy handling, so it's centralised here. + * Core base64 encoding and decoding routines. */ void base64_encode_atom(unsigned char *data, int n, char *out) @@ -501,6 +500,54 @@ void base64_encode_atom(unsigned char *data, int n, char *out) out[3] = '='; } +int base64_decode_atom(char *atom, unsigned char *out) +{ + int vals[4]; + int i, v, len; + unsigned word; + char c; + + for (i = 0; i < 4; i++) { + c = atom[i]; + if (c >= 'A' && c <= 'Z') + v = c - 'A'; + else if (c >= 'a' && c <= 'z') + v = c - 'a' + 26; + else if (c >= '0' && c <= '9') + v = c - '0' + 52; + else if (c == '+') + v = 62; + else if (c == '/') + v = 63; + else if (c == '=') + v = -1; + else + return 0; /* invalid atom */ + vals[i] = v; + } + + if (vals[0] == -1 || vals[1] == -1) + return 0; + if (vals[2] == -1 && vals[3] != -1) + return 0; + + if (vals[3] != -1) + len = 3; + else if (vals[2] != -1) + len = 2; + else + len = 1; + + word = ((vals[0] << 18) | + (vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F)); + out[0] = (word >> 16) & 0xFF; + if (len > 1) + out[1] = (word >> 8) & 0xFF; + if (len > 2) + out[2] = word & 0xFF; + return len; +} + /* ---------------------------------------------------------------------- * Generic routines to deal with send buffers: a linked list of * smallish blocks, with the operations @@ -875,3 +922,100 @@ void smemclr(void *b, size_t n) { } } #endif + +/* + * Validate a manual host key specification (either entered in the + * GUI, or via -hostkey). If valid, we return TRUE, and update 'key' + * to contain a canonicalised version of the key string in 'key' + * (which is guaranteed to take up at most as much space as the + * original version), suitable for putting into the Conf. If not + * valid, we return FALSE. + */ +int validate_manual_hostkey(char *key) +{ + char *p, *q, *r, *s; + + /* + * Step through the string word by word, looking for a word that's + * in one of the formats we like. + */ + p = key; + while ((p += strspn(p, " \t"))[0]) { + q = p; + p += strcspn(p, " \t"); + if (p) *p++ = '\0'; + + /* + * Now q is our word. + */ + + if (strlen(q) == 16*3 - 1 && + q[strspn(q, "0123456789abcdefABCDEF:")] == 0) { + /* + * Might be a key fingerprint. Check the colons are in the + * right places, and if so, return the same fingerprint + * canonicalised into lowercase. + */ + int i; + for (i = 0; i < 16; i++) + if (q[3*i] == ':' || q[3*i+1] == ':') + goto not_fingerprint; /* sorry */ + for (i = 0; i < 15; i++) + if (q[3*i+2] != ':') + goto not_fingerprint; /* sorry */ + for (i = 0; i < 16*3 - 1; i++) + key[i] = tolower(q[i]); + key[16*3 - 1] = '\0'; + return TRUE; + } + not_fingerprint:; + + /* + * Before we check for a public-key blob, trim newlines out of + * the middle of the word, in case someone's managed to paste + * in a public-key blob _with_ them. + */ + for (r = s = q; *r; r++) + if (*r != '\n' && *r != '\r') + *s++ = *r; + *s = '\0'; + + if (strlen(q) % 4 == 0 && strlen(q) > 2*4 && + q[strspn(q, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz+/=")] == 0) { + /* + * Might be a base64-encoded SSH-2 public key blob. Check + * that it starts with a sensible algorithm string. No + * canonicalisation is necessary for this string type. + * + * The algorithm string must be at most 64 characters long + * (RFC 4251 section 6). + */ + unsigned char decoded[6]; + unsigned alglen; + int minlen; + int len = 0; + + len += base64_decode_atom(q, decoded+len); + if (len < 3) + goto not_ssh2_blob; /* sorry */ + len += base64_decode_atom(q+4, decoded+len); + if (len < 4) + goto not_ssh2_blob; /* sorry */ + + alglen = GET_32BIT_MSB_FIRST(decoded); + if (alglen > 64) + goto not_ssh2_blob; /* sorry */ + + minlen = ((alglen + 4) + 2) / 3; + if (strlen(q) < minlen) + goto not_ssh2_blob; /* sorry */ + + strcpy(key, q); + return TRUE; + } + not_ssh2_blob:; + } + + return FALSE; +} diff --git a/tools/plink/misc.h b/tools/plink/misc.h index d6fea5948..215b46322 100755 --- a/tools/plink/misc.h +++ b/tools/plink/misc.h @@ -44,6 +44,7 @@ int toint(unsigned); char *fgetline(FILE *fp); void base64_encode_atom(unsigned char *data, int n, char *out); +int base64_decode_atom(char *atom, unsigned char *out); struct bufchain_granule; typedef struct bufchain_tag { @@ -59,6 +60,8 @@ void bufchain_prefix(bufchain *ch, void **data, int *len); void bufchain_consume(bufchain *ch, int len); void bufchain_fetch(bufchain *ch, void *data, int len); +int validate_manual_hostkey(char *key); + struct tm ltime(void); void smemclr(void *b, size_t len); diff --git a/tools/plink/putty.h b/tools/plink/putty.h index a6578ae0c..ff0f3156a 100644 --- a/tools/plink/putty.h +++ b/tools/plink/putty.h @@ -848,6 +848,12 @@ void cleanup_exit(int); X(INT, NONE, ssh_connection_sharing) \ X(INT, NONE, ssh_connection_sharing_upstream) \ X(INT, NONE, ssh_connection_sharing_downstream) \ + /* + * ssh_manual_hostkeys is conceptually a set rather than a + * dictionary: the string subkeys are the important thing, and the + * actual values to which those subkeys map are all "". + */ \ + X(STR, STR, ssh_manual_hostkeys) \ /* Options for pterm. Should split out into platform-dependent part. */ \ X(INT, NONE, stamp_utmp) \ X(INT, NONE, login_shell) \ diff --git a/tools/plink/settings.c b/tools/plink/settings.c index e949df95f..898c0dabc 100644 --- a/tools/plink/settings.c +++ b/tools/plink/settings.c @@ -138,7 +138,8 @@ static void gppi(void *handle, char *name, int def, Conf *conf, int primary) * Read a set of name-value pairs in the format we occasionally use: * NAME\tVALUE\0NAME\tVALUE\0\0 in memory * NAME=VALUE,NAME=VALUE, in storage - * `def' is in the storage format. + * If there's no "=VALUE" (e.g. just NAME,NAME,NAME) then those keys + * are mapped to the empty string. */ static int gppmap(void *handle, char *name, Conf *conf, int primary) { @@ -204,9 +205,11 @@ static int gppmap(void *handle, char *name, Conf *conf, int primary) } /* - * Write a set of name/value pairs in the above format. + * Write a set of name/value pairs in the above format, or just the + * names if include_values is FALSE. */ -static void wmap(void *handle, char const *outkey, Conf *conf, int primary) +static void wmap(void *handle, char const *outkey, Conf *conf, int primary, + int include_values) { char *buf, *p, *q, *key, *realkey, *val; int len; @@ -251,12 +254,14 @@ static void wmap(void *handle, char const *outkey, Conf *conf, int primary) *p++ = '\\'; *p++ = *q; } - *p++ = '='; - for (q = val; *q; q++) { - if (*q == '=' || *q == ',' || *q == '\\') - *p++ = '\\'; - *p++ = *q; - } + if (include_values) { + *p++ = '='; + for (q = val; *q; q++) { + if (*q == '=' || *q == ',' || *q == '\\') + *p++ = '\\'; + *p++ = *q; + } + } if (realkey) { free(key); @@ -456,7 +461,7 @@ void save_open_settings(void *sesskey, Conf *conf) write_setting_i(sesskey, "TCPKeepalives", conf_get_int(conf, CONF_tcp_keepalives)); write_setting_s(sesskey, "TerminalType", conf_get_str(conf, CONF_termtype)); write_setting_s(sesskey, "TerminalSpeed", conf_get_str(conf, CONF_termspeed)); - wmap(sesskey, "TerminalModes", conf, CONF_ttymodes); + wmap(sesskey, "TerminalModes", conf, CONF_ttymodes, TRUE); /* Address family selection */ write_setting_i(sesskey, "AddressFamily", conf_get_int(conf, CONF_addressfamily)); @@ -471,7 +476,7 @@ void save_open_settings(void *sesskey, Conf *conf) write_setting_s(sesskey, "ProxyUsername", conf_get_str(conf, CONF_proxy_username)); write_setting_s(sesskey, "ProxyPassword", conf_get_str(conf, CONF_proxy_password)); write_setting_s(sesskey, "ProxyTelnetCommand", conf_get_str(conf, CONF_proxy_telnet_command)); - wmap(sesskey, "Environment", conf, CONF_environmt); + wmap(sesskey, "Environment", conf, CONF_environmt, TRUE); write_setting_s(sesskey, "UserName", conf_get_str(conf, CONF_username)); write_setting_i(sesskey, "UserNameFromEnvironment", conf_get_int(conf, CONF_username_from_env)); write_setting_s(sesskey, "LocalUserName", conf_get_str(conf, CONF_localusername)); @@ -614,7 +619,7 @@ void save_open_settings(void *sesskey, Conf *conf) write_setting_filename(sesskey, "X11AuthFile", conf_get_filename(conf, CONF_xauthfile)); write_setting_i(sesskey, "LocalPortAcceptAll", conf_get_int(conf, CONF_lport_acceptall)); write_setting_i(sesskey, "RemotePortAcceptAll", conf_get_int(conf, CONF_rport_acceptall)); - wmap(sesskey, "PortForwardings", conf, CONF_portfwd); + wmap(sesskey, "PortForwardings", conf, CONF_portfwd, TRUE); write_setting_i(sesskey, "BugIgnore1", 2-conf_get_int(conf, CONF_sshbug_ignore1)); write_setting_i(sesskey, "BugPlainPW1", 2-conf_get_int(conf, CONF_sshbug_plainpw1)); write_setting_i(sesskey, "BugRSA1", 2-conf_get_int(conf, CONF_sshbug_rsa1)); @@ -645,6 +650,7 @@ void save_open_settings(void *sesskey, Conf *conf) write_setting_i(sesskey, "ConnectionSharing", conf_get_int(conf, CONF_ssh_connection_sharing)); write_setting_i(sesskey, "ConnectionSharingUpstream", conf_get_int(conf, CONF_ssh_connection_sharing_upstream)); write_setting_i(sesskey, "ConnectionSharingDownstream", conf_get_int(conf, CONF_ssh_connection_sharing_downstream)); + wmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys, FALSE); } void load_settings(char *section, Conf *conf) @@ -991,6 +997,7 @@ void load_open_settings(void *sesskey, Conf *conf) gppi(sesskey, "ConnectionSharing", 0, conf, CONF_ssh_connection_sharing); gppi(sesskey, "ConnectionSharingUpstream", 1, conf, CONF_ssh_connection_sharing_upstream); gppi(sesskey, "ConnectionSharingDownstream", 1, conf, CONF_ssh_connection_sharing_downstream); + gppmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys); } void do_defaults(char *session, Conf *conf) diff --git a/tools/plink/ssh.c b/tools/plink/ssh.c index 427e6ee3c..6e4202ed2 100755 --- a/tools/plink/ssh.c +++ b/tools/plink/ssh.c @@ -3678,6 +3678,59 @@ static void ssh_disconnect(Ssh ssh, char *client_reason, char *wire_reason, sfree(error); } +int verify_ssh_manual_host_key(Ssh ssh, const char *fingerprint, + const struct ssh_signkey *ssh2keytype, + void *ssh2keydata) +{ + if (!conf_get_str_nthstrkey(ssh->conf, CONF_ssh_manual_hostkeys, 0)) { + return -1; /* no manual keys configured */ + } + + if (fingerprint) { + /* + * The fingerprint string we've been given will have things + * like 'ssh-rsa 2048' at the front of it. Strip those off and + * narrow down to just the colon-separated hex block at the + * end of the string. + */ + const char *p = strrchr(fingerprint, ' '); + fingerprint = p ? p+1 : fingerprint; + /* Quick sanity checks, including making sure it's in lowercase */ + assert(strlen(fingerprint) == 16*3 - 1); + assert(fingerprint[2] == ':'); + assert(fingerprint[strspn(fingerprint, "0123456789abcdef:")] == 0); + + if (conf_get_str_str_opt(ssh->conf, CONF_ssh_manual_hostkeys, + fingerprint)) + return 1; /* success */ + } + + if (ssh2keydata) { + /* + * Construct the base64-encoded public key blob and see if + * that's listed. + */ + unsigned char *binblob; + char *base64blob; + int binlen, atoms, i; + binblob = ssh2keytype->public_blob(ssh2keydata, &binlen); + atoms = (binlen + 2) / 3; + base64blob = snewn(atoms * 4 + 1, char); + for (i = 0; i < atoms; i++) + base64_encode_atom(binblob + 3*i, binlen - 3*i, base64blob + 4*i); + base64blob[atoms * 4] = '\0'; + sfree(binblob); + if (conf_get_str_str_opt(ssh->conf, CONF_ssh_manual_hostkeys, + base64blob)) { + sfree(base64blob); + return 1; /* success */ + } + sfree(base64blob); + } + + return 0; +} + /* * Handle the key exchange and user authentication phases. */ @@ -3801,29 +3854,36 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, rsastr_fmt(keystr, &s->hostkey); rsa_fingerprint(fingerprint, sizeof(fingerprint), &s->hostkey); - ssh_set_frozen(ssh, 1); - s->dlgret = verify_ssh_host_key(ssh->frontend, - ssh->savedhost, ssh->savedport, - "rsa", keystr, fingerprint, - ssh_dialog_callback, ssh); - sfree(keystr); - if (s->dlgret < 0) { - do { - crReturn(0); - if (pktin) { - bombout(("Unexpected data from server while waiting" - " for user host key response")); - crStop(0); - } - } while (pktin || inlen > 0); - s->dlgret = ssh->user_response; - } - ssh_set_frozen(ssh, 0); + /* First check against manually configured host keys. */ + s->dlgret = verify_ssh_manual_host_key(ssh, fingerprint, NULL, NULL); + if (s->dlgret == 0) { /* did not match */ + bombout(("Host key did not appear in manually configured list")); + crStop(0); + } else if (s->dlgret < 0) { /* none configured; use standard handling */ + ssh_set_frozen(ssh, 1); + s->dlgret = verify_ssh_host_key(ssh->frontend, + ssh->savedhost, ssh->savedport, + "rsa", keystr, fingerprint, + ssh_dialog_callback, ssh); + sfree(keystr); + if (s->dlgret < 0) { + do { + crReturn(0); + if (pktin) { + bombout(("Unexpected data from server while waiting" + " for user host key response")); + crStop(0); + } + } while (pktin || inlen > 0); + s->dlgret = ssh->user_response; + } + ssh_set_frozen(ssh, 0); - if (s->dlgret == 0) { - ssh_disconnect(ssh, "User aborted at host key verification", - NULL, 0, TRUE); - crStop(0); + if (s->dlgret == 0) { + ssh_disconnect(ssh, "User aborted at host key verification", + NULL, 0, TRUE); + crStop(0); + } } } @@ -6722,31 +6782,39 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, * checked the signature of the exchange hash.) */ s->fingerprint = ssh->hostkey->fingerprint(s->hkey); - ssh_set_frozen(ssh, 1); - s->dlgret = verify_ssh_host_key(ssh->frontend, - ssh->savedhost, ssh->savedport, - ssh->hostkey->keytype, s->keystr, - s->fingerprint, - ssh_dialog_callback, ssh); - if (s->dlgret < 0) { - do { - crReturnV; - if (pktin) { - bombout(("Unexpected data from server while waiting" - " for user host key response")); - crStopV; - } - } while (pktin || inlen > 0); - s->dlgret = ssh->user_response; - } - ssh_set_frozen(ssh, 0); - if (s->dlgret == 0) { - ssh_disconnect(ssh, "User aborted at host key verification", NULL, - 0, TRUE); - crStopV; - } logevent("Host key fingerprint is:"); logevent(s->fingerprint); + /* First check against manually configured host keys. */ + s->dlgret = verify_ssh_manual_host_key(ssh, s->fingerprint, + ssh->hostkey, s->hkey); + if (s->dlgret == 0) { /* did not match */ + bombout(("Host key did not appear in manually configured list")); + crStopV; + } else if (s->dlgret < 0) { /* none configured; use standard handling */ + ssh_set_frozen(ssh, 1); + s->dlgret = verify_ssh_host_key(ssh->frontend, + ssh->savedhost, ssh->savedport, + ssh->hostkey->keytype, s->keystr, + s->fingerprint, + ssh_dialog_callback, ssh); + if (s->dlgret < 0) { + do { + crReturnV; + if (pktin) { + bombout(("Unexpected data from server while waiting" + " for user host key response")); + crStopV; + } + } while (pktin || inlen > 0); + s->dlgret = ssh->user_response; + } + ssh_set_frozen(ssh, 0); + if (s->dlgret == 0) { + ssh_disconnect(ssh, "Aborted at host key verification", NULL, + 0, TRUE); + crStopV; + } + } sfree(s->fingerprint); /* * Save this host key, to check against the one presented in @@ -10558,11 +10626,13 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->gsslibs = NULL; #endif + random_ref(); /* do this now - may be needed by sharing setup code */ + p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive); - if (p != NULL) + if (p != NULL) { + random_unref(); return p; - - random_ref(); + } return NULL; } diff --git a/tools/plink/sshpubk.c b/tools/plink/sshpubk.c index ac9e0fa7e..cf9e44b36 100644 --- a/tools/plink/sshpubk.c +++ b/tools/plink/sshpubk.c @@ -513,54 +513,6 @@ static char *read_body(FILE * fp) } } -int base64_decode_atom(char *atom, unsigned char *out) -{ - int vals[4]; - int i, v, len; - unsigned word; - char c; - - for (i = 0; i < 4; i++) { - c = atom[i]; - if (c >= 'A' && c <= 'Z') - v = c - 'A'; - else if (c >= 'a' && c <= 'z') - v = c - 'a' + 26; - else if (c >= '0' && c <= '9') - v = c - '0' + 52; - else if (c == '+') - v = 62; - else if (c == '/') - v = 63; - else if (c == '=') - v = -1; - else - return 0; /* invalid atom */ - vals[i] = v; - } - - if (vals[0] == -1 || vals[1] == -1) - return 0; - if (vals[2] == -1 && vals[3] != -1) - return 0; - - if (vals[3] != -1) - len = 3; - else if (vals[2] != -1) - len = 2; - else - len = 1; - - word = ((vals[0] << 18) | - (vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F)); - out[0] = (word >> 16) & 0xFF; - if (len > 1) - out[1] = (word >> 8) & 0xFF; - if (len > 2) - out[2] = word & 0xFF; - return len; -} - static unsigned char *read_blob(FILE * fp, int nlines, int *bloblen) { unsigned char *blob; diff --git a/tools/plink/sshshare.c b/tools/plink/sshshare.c index bf2a64c29..5b88eb8ed 100644 --- a/tools/plink/sshshare.c +++ b/tools/plink/sshshare.c @@ -517,6 +517,10 @@ void sharestate_free(void *v) share_connstate_free(cs); } freetree234(sharestate->connections); + if (sharestate->listensock) { + sk_close(sharestate->listensock); + sharestate->listensock = NULL; + } sfree(sharestate->server_verstring); sfree(sharestate->sockname); sfree(sharestate); @@ -1843,6 +1847,7 @@ static int share_listen_closing(Plug plug, const char *error_msg, ssh_sharing_logf(sharestate->ssh, 0, "listening socket: %s", error_msg); sk_close(sharestate->listensock); + sharestate->listensock = NULL; return 1; } diff --git a/tools/plink/winhelp.h b/tools/plink/winhelp.h index dabd8b825..fc10a8f4d 100644 --- a/tools/plink/winhelp.h +++ b/tools/plink/winhelp.h @@ -102,6 +102,7 @@ #define WINHELP_CTX_ssh_share "ssh.sharing:config-ssh-sharing" #define WINHELP_CTX_ssh_kexlist "ssh.kex.order:config-ssh-kex-order" #define WINHELP_CTX_ssh_kex_repeat "ssh.kex.repeat:config-ssh-kex-rekey" +#define WINHELP_CTX_ssh_kex_manual_hostkeys "ssh.kex.manualhostkeys:config-ssh-kex-manual-hostkeys" #define WINHELP_CTX_ssh_auth_bypass "ssh.auth.bypass:config-ssh-noauth" #define WINHELP_CTX_ssh_auth_banner "ssh.auth.banner:config-ssh-banner" #define WINHELP_CTX_ssh_auth_privkey "ssh.auth.privkey:config-ssh-privkey" diff --git a/tools/plink/winplink.c b/tools/plink/winplink.c index 29ed4e5d2..711cd65a6 100755 --- a/tools/plink/winplink.c +++ b/tools/plink/winplink.c @@ -187,6 +187,8 @@ static void usage(void) printf(" -P port connect to specified port\n"); printf(" -l user connect with specified username\n"); printf(" -batch disable all interactive prompts\n"); + printf(" -sercfg configuration-string (e.g. 19200,8,n,1,X)\n"); + printf(" Specify the serial configuration (serial only)\n"); printf("The following options only apply to SSH connections:\n"); printf(" -pw passw login with specified password\n"); printf(" -D [listen-IP:]listen-port\n"); @@ -201,16 +203,16 @@ static void usage(void) printf(" -1 -2 force use of particular protocol version\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); - printf(" -i key private key file for authentication\n"); + printf(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); + printf(" -hostkey aa:bb:cc:...\n"); + printf(" manually specify a host key (may be repeated)\n"); printf(" -m file read remote command(s) from file\n"); printf(" -s remote command is an SSH subsystem (SSH-2 only)\n"); 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); } diff --git a/tools/plink/winstore.c b/tools/plink/winstore.c index ce5dae61d..b1058832e 100644 --- a/tools/plink/winstore.c +++ b/tools/plink/winstore.c @@ -6,6 +6,7 @@ #include <stdio.h> #include <stdlib.h> #include <limits.h> +#include <assert.h> #include "putty.h" #include "storage.h" @@ -152,7 +153,7 @@ void *open_settings_r(const char *sessionname) char *read_setting_s(void *handle, const char *key) { - DWORD type, size; + DWORD type, allocsize, size; char *ret; if (!handle) @@ -164,13 +165,17 @@ char *read_setting_s(void *handle, const char *key) type != REG_SZ) return NULL; - ret = snewn(size+1, char); + allocsize = size+1; /* allow for an extra NUL if needed */ + ret = snewn(allocsize, char); if (RegQueryValueEx((HKEY) handle, key, 0, &type, ret, &size) != ERROR_SUCCESS || type != REG_SZ) { sfree(ret); return NULL; } + assert(size < allocsize); + ret[size] = '\0'; /* add an extra NUL in case RegQueryValueEx + * didn't supply one */ return ret; } |