1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-08 08:58:00 +00:00

Merge the 0.74 release branch back to master.

Two minor memory-leak fixes on 0.74 seem not to be needed on master:
the fix in an early exit path of pageant_add_keyfile is done already
on master in a different way, and the missing sfree(fdlist) in
uxsftp.c is in code that's been completely rewritten in the uxcliloop
refactoring.

Other minor conflicts: the rework in commit b52641644905 of
ssh1login.c collided with the change from FLAG_VERBOSE to
seat_verbose(), and master and 0.74 each added an unrelated extra
field to the end of struct SshServerConfig.
This commit is contained in:
Simon Tatham 2020-06-27 08:09:24 +01:00
commit 2762a2025f
24 changed files with 389 additions and 238 deletions

View File

@ -35,7 +35,7 @@ module putty
ifeq "$(RELEASE)" "" set Ndate $(!builddate)
ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -pe 's/(....)(..)(..)/$$1-$$2-$$3/' > date
ifneq "$(Ndate)" "" read Date date
set Epoch 17161 # update this at every release
set Epoch 17433 # update this at every release
ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -ne 'use Time::Local; /(....)(..)(..)/ and print timegm(0,0,0,$$3,$$2-1,$$1) / 86400 - $(Epoch)' > days
ifneq "$(Ndate)" "" read Days days

View File

@ -1 +1 @@
0.73
0.74

View File

@ -1430,7 +1430,7 @@ static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg,
#endif
) {
#ifdef NAMED_CLIPBOARDS
const char *sval = dlg_editbox_get(ctrl, dlg);
char *sval = dlg_editbox_get(ctrl, dlg);
int i;
for (i = 0; i < lenof(options); i++)
@ -1445,6 +1445,8 @@ static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg,
sval++;
conf_set_str(conf, strsetting, sval);
}
sfree(sval);
#else
int index = dlg_listbox_index(ctrl, dlg);
if (index >= 0) {
@ -2570,6 +2572,10 @@ void setup_config_box(struct controlbox *b, bool midsession,
HELPCTX(ssh_hklist),
hklist_handler, P(NULL));
c->listbox.height = 5;
ctrl_checkbox(s, "Prefer algorithms for which a host key is known",
'p', HELPCTX(ssh_hk_known), conf_checkbox_handler,
I(CONF_ssh_prefer_known_hostkeys));
}
/*

View File

@ -2602,6 +2602,27 @@ If the first key type PuTTY finds is below the \q{warn below here}
line, you will see a warning box when you make the connection, similar
to that for cipher selection (see \k{config-ssh-encryption}).
\S{config-ssh-prefer-known-hostkeys} Preferring known host keys
By default, PuTTY will adjust the preference order for host key
algorithms so that any host keys it already knows are moved to the top
of the list.
This prevents you from having to check and confirm a new host key for
a server you already had one for (e.g. because the server has
generated an alternative key of a type higher in PuTTY's preference
order, or because you changed the preference order itself).
However, on the other hand, it can leak information to a listener in
the network about \e{whether} you already know a host key for this
server.
For this reason, this policy is configurable. By turning this checkbox
off, you can reset PuTTY to always use the exact order of host key
algorithms configured in the preference list described in
\k{config-ssh-hostkey-order}, so that a listener will find out nothing
about what keys you had stored.
\S{config-ssh-kex-manual-hostkeys} \ii{Manually configuring host keys}
In some situations, if PuTTY's automated host key management is not

View File

@ -39,9 +39,9 @@ Once you've got a console window to type into, you can just type
version of Plink you're using, and gives you a brief summary of how to
use Plink:
\c Z:\sysosd>plink
\c C:\>plink
\c Plink: command-line connection utility
\c Release 0.73
\c Release 0.74
\c Usage: plink [options] [user@]host [command]
\c ("host" can also be a PuTTY saved session name)
\c Options:
@ -100,7 +100,7 @@ Once this works, you are ready to use Plink.
To make a simple interactive connection to a remote server, just
type \c{plink} and then the host name:
\c Z:\sysosd>plink login.example.com
\c C:\>plink login.example.com
\c
\c Debian GNU/Linux 2.2 flunky.example.com
\c flunky login:
@ -117,7 +117,7 @@ In order to connect with a different protocol, you can give the
command line options \c{-ssh}, \c{-telnet}, \c{-rlogin} or \c{-raw}.
To make an SSH connection, for example:
\c Z:\sysosd>plink -ssh login.example.com
\c C:\>plink -ssh login.example.com
\c login as:
If you have already set up a PuTTY saved session, then instead of
@ -125,7 +125,7 @@ supplying a host name, you can give the saved session name. This
allows you to use public-key authentication, specify a user name,
and use most of the other features of PuTTY:
\c Z:\sysosd>plink my-ssh-session
\c C:\>plink my-ssh-session
\c Sent username "fred"
\c Authenticating with public key "fred@winbox"
\c Last login: Thu Dec 6 19:25:33 2001 from :0.0
@ -196,18 +196,18 @@ Once you have done all this, you should be able to run a remote
command on the SSH server machine and have it execute automatically
with no prompting:
\c Z:\sysosd>plink login.example.com -l fred echo hello, world
\c C:\>plink login.example.com -l fred echo hello, world
\c hello, world
\c
\c Z:\sysosd>
\c C:\>
Or, if you have set up a saved session with all the connection
details:
\c Z:\sysosd>plink mysession echo hello, world
\c C:\>plink mysession echo hello, world
\c hello, world
\c
\c Z:\sysosd>
\c C:\>
Then you can set up other programs to run this Plink command and
talk to it as if it were a process on the server machine.

View File

@ -37,9 +37,9 @@ Once you've got a console window to type into, you can just type
version of PSCP you're using, and gives you a brief summary of how to
use PSCP:
\c Z:\owendadmin>pscp
\c C:\>pscp
\c PuTTY Secure Copy client
\c Release 0.73
\c Release 0.74
\c Usage: pscp [options] [user@]host:source target
\c pscp [options] source [source...] [user@]host:target
\c pscp [options] -ls [user@]host:filespec

View File

@ -572,8 +572,10 @@ static ssh2_userkey *openssh_pem_read(
strbuf *blob = strbuf_new_nm();
int privptr = 0, publen;
if (!key)
if (!key) {
strbuf_free(blob);
return NULL;
}
if (key->encrypted) {
unsigned char keybuf[32];

View File

@ -1256,6 +1256,7 @@ NORETURN void cleanup_exit(int);
X(BOOL, NONE, compression) \
X(INT, INT, ssh_kexlist) \
X(INT, INT, ssh_hklist) \
X(BOOL, NONE, ssh_prefer_known_hostkeys) \
X(INT, NONE, ssh_rekey_time) /* in minutes */ \
X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \
X(BOOL, NONE, tryagent) \

View File

@ -640,10 +640,10 @@ static void sesschan_notify_remote_exit(Seat *seat)
sshfwd_send_exit_signal(
sess->c, signame, false, ptrlen_from_asciz(sigmsg));
sfree(sigmsg);
got_signal = true;
}
sfree(sigmsg);
} else {
int signum = pty_backend_exit_signum(sess->backend);

View File

@ -602,6 +602,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist);
wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist);
wprefs(sesskey, "HostKey", hknames, HK_MAX, conf, CONF_ssh_hklist);
write_setting_b(sesskey, "PreferKnownHostKeys", conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys));
write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time));
#ifndef NO_GSSAPI
write_setting_i(sesskey, "GssapiRekey", conf_get_int(conf, CONF_gssapirekey));
@ -1006,6 +1007,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
}
gprefs(sesskey, "HostKey", "ed25519,ecdsa,rsa,dsa,WARN",
hknames, HK_MAX, conf, CONF_ssh_hklist);
gppb(sesskey, "PreferKnownHostKeys", true, conf, CONF_ssh_prefer_known_hostkeys);
gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time);
#ifndef NO_GSSAPI
gppi(sesskey, "GssapiRekey", GSS_DEF_REKEY_MINS, conf, CONF_gssapirekey);

View File

@ -298,18 +298,34 @@ static void ssh1_login_server_process_queue(PacketProtocolLayer *ppl)
mp_int *modulus = get_mp_ssh1(pktin);
s->authkey = auth_publickey_ssh1(
s->authpolicy, s->username, modulus);
if (!s->authkey &&
s->ssc->stunt_pretend_to_accept_any_pubkey) {
mp_int *zero = mp_from_integer(0);
mp_int *fake_challenge = mp_random_in_range(zero, modulus);
pktout = ssh_bpp_new_pktout(
s->ppl.bpp, SSH1_SMSG_AUTH_RSA_CHALLENGE);
put_mp_ssh1(pktout, fake_challenge);
pq_push(s->ppl.out_pq, pktout);
mp_free(zero);
mp_free(fake_challenge);
}
mp_free(modulus);
}
if (!s->authkey)
if (!s->authkey &&
!s->ssc->stunt_pretend_to_accept_any_pubkey)
continue;
if (s->authkey->bytes < 32) {
if (s->authkey && s->authkey->bytes < 32) {
ppl_logevent("Auth key far too small");
continue;
}
{
if (s->authkey) {
unsigned char *rsabuf =
snewn(s->authkey->bytes, unsigned char);
@ -349,6 +365,9 @@ static void ssh1_login_server_process_queue(PacketProtocolLayer *ppl)
return;
}
if (!s->authkey)
continue;
{
ptrlen response = get_data(pktin, 16);
ptrlen expected = make_ptrlen(

View File

@ -12,6 +12,12 @@
#include "sshppl.h"
#include "sshcr.h"
typedef struct agent_key {
RSAKey key;
strbuf *comment;
ptrlen blob; /* only used during initial parsing of agent response */
} agent_key;
struct ssh1_login_state {
int crState;
@ -47,11 +53,11 @@ struct ssh1_login_state {
void *agent_response_to_free;
ptrlen agent_response;
BinarySource asrc[1]; /* response from SSH agent */
int keyi, nkeys;
size_t agent_keys_len;
agent_key *agent_keys;
size_t agent_key_index, agent_key_limit;
bool authed;
RSAKey key;
mp_int *challenge;
strbuf *agent_comment;
int dlgret;
Filename *keyfile;
RSAKey servkey, hostkey;
@ -99,7 +105,6 @@ PacketProtocolLayer *ssh1_login_new(
s->savedhost = dupstr(host);
s->savedport = port;
s->successor_layer = successor_layer;
s->agent_comment = strbuf_new();
return &s->ppl;
}
@ -118,9 +123,15 @@ static void ssh1_login_free(PacketProtocolLayer *ppl)
if (s->publickey_blob)
strbuf_free(s->publickey_blob);
sfree(s->publickey_comment);
strbuf_free(s->agent_comment);
if (s->cur_prompt)
free_prompts(s->cur_prompt);
if (s->agent_keys) {
for (size_t i = 0; i < s->agent_keys_len; i++) {
freersakey(&s->agent_keys[i].key);
strbuf_free(s->agent_keys[i].comment);
}
sfree(s->agent_keys);
}
sfree(s->agent_response_to_free);
if (s->auth_agent_query)
agent_cancel_query(s->auth_agent_query);
@ -504,122 +515,165 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
get_uint32(s->asrc); /* skip length field */
if (get_byte(s->asrc) == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
s->nkeys = toint(get_uint32(s->asrc));
if (s->nkeys < 0) {
ppl_logevent("Pageant reported negative key count %d",
s->nkeys);
s->nkeys = 0;
}
ppl_logevent("Pageant has %d SSH-1 keys", s->nkeys);
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
size_t start, end;
start = s->asrc->pos;
get_rsa_ssh1_pub(s->asrc, &s->key,
RSA_SSH1_EXPONENT_FIRST);
end = s->asrc->pos;
strbuf_clear(s->agent_comment);
put_datapl(s->agent_comment, get_string(s->asrc));
if (get_err(s->asrc)) {
ppl_logevent("Pageant key list packet was truncated");
break;
}
if (s->publickey_blob) {
ptrlen keystr = make_ptrlen(
(const char *)s->asrc->data + start, end - start);
size_t nkeys = get_uint32(s->asrc);
size_t origpos = s->asrc->pos;
if (keystr.len == s->publickey_blob->len &&
!memcmp(keystr.ptr, s->publickey_blob->s,
s->publickey_blob->len)) {
ppl_logevent("Pageant key #%d matches "
"configured key file", s->keyi);
s->tried_publickey = true;
} else
/* Skip non-configured key */
continue;
/*
* Check that the agent response is well formed.
*/
for (size_t i = 0; i < nkeys; i++) {
get_rsa_ssh1_pub(s->asrc, NULL, RSA_SSH1_EXPONENT_FIRST);
get_string(s->asrc); /* comment */
if (get_err(s->asrc)) {
ppl_logevent("Pageant's response was truncated");
goto parsed_agent_query;
}
ppl_logevent("Trying Pageant key #%d", s->keyi);
pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_RSA);
put_mp_ssh1(pkt, s->key.modulus);
pq_push(s->ppl.out_pq, pkt);
crMaybeWaitUntilV((pktin = ssh1_login_pop(s))
!= NULL);
if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
ppl_logevent("Key refused");
continue;
}
/*
* Copy the list of public-key blobs out of the Pageant
* response.
*/
BinarySource_REWIND_TO(s->asrc, origpos);
s->agent_keys_len = nkeys;
s->agent_keys = snewn(s->agent_keys_len, agent_key);
for (size_t i = 0; i < nkeys; i++) {
memset(&s->agent_keys[i].key, 0,
sizeof(s->agent_keys[i].key));
const char *blobstart = get_ptr(s->asrc);
get_rsa_ssh1_pub(s->asrc, &s->agent_keys[i].key,
RSA_SSH1_EXPONENT_FIRST);
const char *blobend = get_ptr(s->asrc);
s->agent_keys[i].comment = strbuf_new();
put_datapl(s->agent_keys[i].comment, get_string(s->asrc));
s->agent_keys[i].blob = make_ptrlen(
blobstart, blobend - blobstart);
}
ppl_logevent("Pageant has %"SIZEu" SSH-1 keys", nkeys);
if (s->publickey_blob) {
/*
* If we've been given a specific public key blob,
* filter the list of keys to try from the agent
* down to only that one, or none if it's not
* there.
*/
ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob);
size_t i;
for (i = 0; i < nkeys; i++) {
if (ptrlen_eq_ptrlen(our_blob, s->agent_keys[i].blob))
break;
}
ppl_logevent("Received RSA challenge");
s->challenge = get_mp_ssh1(pktin);
if (i < nkeys) {
ppl_logevent("Pageant key #%"SIZEu" matches "
"configured key file", i);
s->agent_key_index = i;
s->agent_key_limit = i+1;
} else {
ppl_logevent("Configured key file not in Pageant");
s->agent_key_index = 0;
s->agent_key_limit = 0;
}
} else {
/*
* Otherwise, try them all.
*/
s->agent_key_index = 0;
s->agent_key_limit = nkeys;
}
} else {
ppl_logevent("Failed to get reply from Pageant");
}
parsed_agent_query:;
for (; s->agent_key_index < s->agent_key_limit;
s->agent_key_index++) {
ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index);
pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_RSA);
put_mp_ssh1(pkt,
s->agent_keys[s->agent_key_index].key.modulus);
pq_push(s->ppl.out_pq, pkt);
crMaybeWaitUntilV((pktin = ssh1_login_pop(s))
!= NULL);
if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
ppl_logevent("Key refused");
continue;
}
ppl_logevent("Received RSA challenge");
{
mp_int *challenge = get_mp_ssh1(pktin);
if (get_err(pktin)) {
mp_free(s->challenge);
mp_free(challenge);
ssh_proto_error(s->ppl.ssh, "Server's RSA challenge "
"was badly formatted");
return;
}
{
strbuf *agentreq;
const char *ret;
strbuf *agentreq = strbuf_new_for_agent_query();
put_byte(agentreq, SSH1_AGENTC_RSA_CHALLENGE);
agentreq = strbuf_new_for_agent_query();
put_byte(agentreq, SSH1_AGENTC_RSA_CHALLENGE);
put_uint32(agentreq, mp_get_nbits(s->key.modulus));
put_mp_ssh1(agentreq, s->key.exponent);
put_mp_ssh1(agentreq, s->key.modulus);
put_mp_ssh1(agentreq, s->challenge);
put_data(agentreq, s->session_id, 16);
put_uint32(agentreq, 1); /* response format */
ssh1_login_agent_query(s, agentreq);
strbuf_free(agentreq);
crMaybeWaitUntilV(!s->auth_agent_query);
rsa_ssh1_public_blob(
BinarySink_UPCAST(agentreq),
&s->agent_keys[s->agent_key_index].key,
RSA_SSH1_EXPONENT_FIRST);
ret = s->agent_response.ptr;
if (ret) {
if (s->agent_response.len >= 5+16 &&
ret[4] == SSH1_AGENT_RSA_RESPONSE) {
ppl_logevent("Sending Pageant's response");
pkt = ssh_bpp_new_pktout(
s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE);
put_data(pkt, ret + 5, 16);
pq_push(s->ppl.out_pq, pkt);
crMaybeWaitUntilV(
(pktin = ssh1_login_pop(s))
!= NULL);
if (pktin->type == SSH1_SMSG_SUCCESS) {
ppl_logevent("Pageant's response "
"accepted");
if (seat_verbose(s->ppl.seat)) {
ptrlen comment = ptrlen_from_strbuf(
s->agent_comment);
ppl_printf("Authenticated using RSA "
"key \"%.*s\" from "
"agent\r\n",
PTRLEN_PRINTF(comment));
}
s->authed = true;
} else
ppl_logevent("Pageant's response not "
"accepted");
} else {
ppl_logevent("Pageant failed to answer "
"challenge");
sfree((char *)ret);
}
} else {
ppl_logevent("No reply received from Pageant");
}
}
mp_free(s->key.exponent);
mp_free(s->key.modulus);
mp_free(s->challenge);
if (s->authed)
break;
put_mp_ssh1(agentreq, challenge);
mp_free(challenge);
put_data(agentreq, s->session_id, 16);
put_uint32(agentreq, 1); /* response format */
ssh1_login_agent_query(s, agentreq);
strbuf_free(agentreq);
crMaybeWaitUntilV(!s->auth_agent_query);
}
sfree(s->agent_response_to_free);
s->agent_response_to_free = NULL;
if (s->publickey_blob && !s->tried_publickey)
ppl_logevent("Configured key file not in Pageant");
} else {
ppl_logevent("Failed to get reply from Pageant");
{
const unsigned char *ret = s->agent_response.ptr;
if (ret) {
if (s->agent_response.len >= 5+16 &&
ret[4] == SSH1_AGENT_RSA_RESPONSE) {
ppl_logevent("Sending Pageant's response");
pkt = ssh_bpp_new_pktout(
s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE);
put_data(pkt, ret + 5, 16);
pq_push(s->ppl.out_pq, pkt);
crMaybeWaitUntilV(
(pktin = ssh1_login_pop(s))
!= NULL);
if (pktin->type == SSH1_SMSG_SUCCESS) {
ppl_logevent("Pageant's response "
"accepted");
if (seat_verbose(s->ppl.seat)) {
ptrlen comment = ptrlen_from_strbuf(
s->agent_keys[s->agent_key_index].
comment);
ppl_printf("Authenticated using RSA "
"key \"%.*s\" from "
"agent\r\n",
PTRLEN_PRINTF(comment));
}
s->authed = true;
} else
ppl_logevent("Pageant's response not "
"accepted");
} else {
ppl_logevent("Pageant failed to answer "
"challenge");
sfree((char *)ret);
}
} else {
ppl_logevent("No reply received from Pageant");
}
}
if (s->authed)
break;
}
if (s->authed)
break;

View File

@ -576,9 +576,10 @@ static void ssh2_write_kexinit_lists(
}
} else if (first_time) {
/*
* In the first key exchange, we list all the algorithms
* we're prepared to cope with, but prefer those algorithms
* for which we have a host key for this host.
* In the first key exchange, we list all the algorithms we're
* prepared to cope with, but (if configured to) we prefer
* those algorithms for which we have a host key for this
* host.
*
* If the host key algorithm is below the warning
* threshold, we warn even if we did already have a key
@ -594,7 +595,8 @@ static void ssh2_write_kexinit_lists(
for (j = 0; j < lenof(ssh2_hostkey_algs); j++) {
if (ssh2_hostkey_algs[j].id != preferred_hk[i])
continue;
if (have_ssh_host_key(hk_host, hk_port,
if (conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys) &&
have_ssh_host_key(hk_host, hk_port,
ssh2_hostkey_algs[j].alg->cache_id)) {
alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
ssh2_hostkey_algs[j].alg->ssh_id);

View File

@ -199,7 +199,7 @@ static void ssh2_userauth_server_process_queue(PacketProtocolLayer *ppl)
goto failure;
}
} else if (ptrlen_eq_string(s->method, "publickey")) {
bool has_signature, success;
bool has_signature, success, send_pk_ok, key_really_ok;
ptrlen algorithm, blob, signature;
const ssh_keyalg *keyalg;
ssh_key *key;
@ -213,7 +213,23 @@ static void ssh2_userauth_server_process_queue(PacketProtocolLayer *ppl)
algorithm = get_string(pktin);
blob = get_string(pktin);
if (!auth_publickey(s->authpolicy, s->username, blob))
key_really_ok = auth_publickey(s->authpolicy, s->username, blob);
send_pk_ok = key_really_ok ||
s->ssc->stunt_pretend_to_accept_any_pubkey;
if (!has_signature) {
if (!send_pk_ok)
goto failure;
pktout = ssh_bpp_new_pktout(
s->ppl.bpp, SSH2_MSG_USERAUTH_PK_OK);
put_stringpl(pktout, algorithm);
put_stringpl(pktout, blob);
pq_push(s->ppl.out_pq, pktout);
continue; /* skip USERAUTH_{SUCCESS,FAILURE} epilogue */
}
if (!key_really_ok)
goto failure;
keyalg = find_pubkey_alg_len(algorithm);
@ -223,16 +239,6 @@ static void ssh2_userauth_server_process_queue(PacketProtocolLayer *ppl)
if (!key)
goto failure;
if (!has_signature) {
ssh_key_free(key);
pktout = ssh_bpp_new_pktout(
s->ppl.bpp, SSH2_MSG_USERAUTH_PK_OK);
put_stringpl(pktout, algorithm);
put_stringpl(pktout, blob);
pq_push(s->ppl.out_pq, pktout);
continue; /* skip USERAUTH_{SUCCESS,FAILURE} epilogue */
}
sigdata = strbuf_new();
ssh2_userauth_server_add_session_id(s, sigdata);
put_byte(sigdata, SSH2_MSG_USERAUTH_REQUEST);

View File

@ -18,6 +18,11 @@
#define BANNER_LIMIT 131072
typedef struct agent_key {
strbuf *blob, *comment;
ptrlen algorithm;
} agent_key;
struct ssh2_userauth_state {
int crState;
@ -69,9 +74,9 @@ struct ssh2_userauth_state {
void *agent_response_to_free;
ptrlen agent_response;
BinarySource asrc[1]; /* for reading SSH agent response */
size_t pkblob_pos_in_agent;
int keyi, nkeys;
ptrlen pk, alg, comment;
size_t agent_keys_len;
agent_key *agent_keys;
size_t agent_key_index, agent_key_limit;
int len;
PktOut *pktout;
bool want_user_input;
@ -173,6 +178,13 @@ static void ssh2_userauth_free(PacketProtocolLayer *ppl)
if (s->successor_layer)
ssh_ppl_free(s->successor_layer);
if (s->agent_keys) {
for (size_t i = 0; i < s->agent_keys_len; i++) {
strbuf_free(s->agent_keys[i].blob);
strbuf_free(s->agent_keys[i].comment);
}
sfree(s->agent_keys);
}
sfree(s->agent_response_to_free);
if (s->auth_agent_query)
agent_cancel_query(s->auth_agent_query);
@ -298,8 +310,6 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
* Find out about any keys Pageant has (but if there's a public
* key configured, filter out all others).
*/
s->nkeys = 0;
s->pkblob_pos_in_agent = 0;
if (s->tryagent && agent_exists()) {
ppl_logevent("Pageant is running. Requesting keys.");
@ -315,48 +325,75 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
get_uint32(s->asrc); /* skip length field */
if (get_byte(s->asrc) == SSH2_AGENT_IDENTITIES_ANSWER) {
int keyi;
s->nkeys = toint(get_uint32(s->asrc));
size_t nkeys = get_uint32(s->asrc);
size_t origpos = s->asrc->pos;
/*
* Vet the Pageant response to ensure that the key count
* and blob lengths make sense.
* Check that the agent response is well formed.
*/
if (s->nkeys < 0) {
ppl_logevent("Pageant response contained a negative"
" key count %d", s->nkeys);
s->nkeys = 0;
goto done_agent_query;
} else {
ppl_logevent("Pageant has %d SSH-2 keys", s->nkeys);
for (size_t i = 0; i < nkeys; i++) {
get_string(s->asrc); /* blob */
get_string(s->asrc); /* comment */
if (get_err(s->asrc)) {
ppl_logevent("Pageant's response was truncated");
goto done_agent_query;
}
}
/* See if configured key is in agent. */
for (keyi = 0; keyi < s->nkeys; keyi++) {
size_t pos = s->asrc->pos;
ptrlen blob = get_string(s->asrc);
get_string(s->asrc); /* skip comment */
if (get_err(s->asrc)) {
ppl_logevent("Pageant response was truncated");
s->nkeys = 0;
goto done_agent_query;
}
/*
* Copy the list of public-key blobs out of the Pageant
* response.
*/
BinarySource_REWIND_TO(s->asrc, origpos);
s->agent_keys_len = nkeys;
s->agent_keys = snewn(s->agent_keys_len, agent_key);
for (size_t i = 0; i < nkeys; i++) {
s->agent_keys[i].blob = strbuf_new();
put_datapl(s->agent_keys[i].blob, get_string(s->asrc));
s->agent_keys[i].comment = strbuf_new();
put_datapl(s->agent_keys[i].comment, get_string(s->asrc));
if (s->publickey_blob &&
blob.len == s->publickey_blob->len &&
!memcmp(blob.ptr, s->publickey_blob->s,
s->publickey_blob->len)) {
ppl_logevent("Pageant key #%d matches "
"configured key file", keyi);
s->keyi = keyi;
s->pkblob_pos_in_agent = pos;
/* Also, extract the algorithm string from the start
* of the public-key blob. */
BinarySource src[1];
BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(
s->agent_keys[i].blob));
s->agent_keys[i].algorithm = get_string(src);
}
ppl_logevent("Pageant has %"SIZEu" SSH-2 keys", nkeys);
if (s->publickey_blob) {
/*
* If we've been given a specific public key blob,
* filter the list of keys to try from the agent down
* to only that one, or none if it's not there.
*/
ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob);
size_t i;
for (i = 0; i < nkeys; i++) {
if (ptrlen_eq_ptrlen(our_blob, ptrlen_from_strbuf(
s->agent_keys[i].blob)))
break;
}
}
if (s->publickey_blob && !s->pkblob_pos_in_agent) {
if (i < nkeys) {
ppl_logevent("Pageant key #%"SIZEu" matches "
"configured key file", i);
s->agent_key_index = i;
s->agent_key_limit = i+1;
} else {
ppl_logevent("Configured key file not in Pageant");
s->nkeys = 0;
s->agent_key_index = 0;
s->agent_key_limit = 0;
}
} else {
/*
* Otherwise, try them all.
*/
s->agent_key_index = 0;
s->agent_key_limit = nkeys;
}
} else {
ppl_logevent("Failed to get reply from Pageant");
@ -455,17 +492,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
s->tried_pubkey_config = false;
s->kbd_inter_refused = false;
/* Reset agent request state. */
s->done_agent = false;
if (s->agent_response.ptr) {
if (s->pkblob_pos_in_agent) {
s->asrc->pos = s->pkblob_pos_in_agent;
} else {
s->asrc->pos = 9; /* skip length + type + key count */
s->keyi = 0;
}
}
while (1) {
/*
@ -686,7 +713,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
} else
#endif /* NO_GSSAPI */
if (s->can_pubkey && !s->done_agent && s->nkeys) {
if (s->can_pubkey && !s->done_agent &&
s->agent_key_index < s->agent_key_limit) {
/*
* Attempt public-key authentication using a key from Pageant.
@ -694,16 +722,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
s->ppl.bpp->pls->actx = SSH2_PKTCTX_PUBLICKEY;
ppl_logevent("Trying Pageant key #%d", s->keyi);
/* Unpack key from agent response */
s->pk = get_string(s->asrc);
s->comment = get_string(s->asrc);
{
BinarySource src[1];
BinarySource_BARE_INIT_PL(src, s->pk);
s->alg = get_string(src);
}
ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index);
/* See if server will accept it */
s->pktout = ssh_bpp_new_pktout(
@ -713,8 +732,10 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
put_stringz(s->pktout, "publickey");
/* method */
put_bool(s->pktout, false); /* no signature included */
put_stringpl(s->pktout, s->alg);
put_stringpl(s->pktout, s->pk);
put_stringpl(s->pktout,
s->agent_keys[s->agent_key_index].algorithm);
put_stringpl(s->pktout, ptrlen_from_strbuf(
s->agent_keys[s->agent_key_index].blob));
pq_push(s->ppl.out_pq, s->pktout);
s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET;
@ -727,11 +748,13 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
} else {
strbuf *agentreq, *sigdata;
ptrlen comment = ptrlen_from_strbuf(
s->agent_keys[s->agent_key_index].comment);
if (seat_verbose(s->ppl.seat))
ppl_printf("Authenticating with public key "
"\"%.*s\" from agent\r\n",
PTRLEN_PRINTF(s->comment));
PTRLEN_PRINTF(comment));
/*
* Server is willing to accept the key.
@ -744,13 +767,16 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
put_stringz(s->pktout, "publickey");
/* method */
put_bool(s->pktout, true); /* signature included */
put_stringpl(s->pktout, s->alg);
put_stringpl(s->pktout, s->pk);
put_stringpl(s->pktout,
s->agent_keys[s->agent_key_index].algorithm);
put_stringpl(s->pktout, ptrlen_from_strbuf(
s->agent_keys[s->agent_key_index].blob));
/* Ask agent for signature. */
agentreq = strbuf_new_for_agent_query();
put_byte(agentreq, SSH2_AGENTC_SIGN_REQUEST);
put_stringpl(agentreq, s->pk);
put_stringpl(agentreq, ptrlen_from_strbuf(
s->agent_keys[s->agent_key_index].blob));
/* Now the data to be signed... */
sigdata = strbuf_new();
ssh2_userauth_add_session_id(s, sigdata);
@ -772,8 +798,11 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
if (get_byte(src) == SSH2_AGENT_SIGN_RESPONSE &&
(sigblob = get_string(src), !get_err(src))) {
ppl_logevent("Sending Pageant's response");
ssh2_userauth_add_sigblob(s, s->pktout,
s->pk, sigblob);
ssh2_userauth_add_sigblob(
s, s->pktout,
ptrlen_from_strbuf(
s->agent_keys[s->agent_key_index].blob),
sigblob);
pq_push(s->ppl.out_pq, s->pktout);
s->type = AUTH_TYPE_PUBLICKEY;
} else {
@ -794,14 +823,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
}
/* Do we have any keys left to try? */
if (s->pkblob_pos_in_agent) {
if (++s->agent_key_index >= s->agent_key_limit)
s->done_agent = true;
s->tried_pubkey_config = true;
} else {
s->keyi++;
if (s->keyi >= s->nkeys)
s->done_agent = true;
}
} else if (s->can_pubkey && s->publickey_blob &&
s->privatekey_available && !s->tried_pubkey_config) {
@ -1786,7 +1809,7 @@ static void ssh2_userauth_add_sigblob(
/* debug("modulus length is %d\n", len); */
/* debug("signature length is %d\n", siglen); */
if (mod_mp.len != sig_mp.len) {
if (mod_mp.len > sig_mp.len) {
strbuf *substr = strbuf_new();
put_data(substr, sigblob.ptr, sig_prefix_len);
put_uint32(substr, mod_mp.len);

View File

@ -72,8 +72,10 @@ static char *dss_cache_str(ssh_key *key)
struct dss_key *dss = container_of(key, struct dss_key, sshk);
strbuf *sb = strbuf_new();
if (!dss->p)
if (!dss->p) {
strbuf_free(sb);
return NULL;
}
append_hex_to_strbuf(sb, dss->p);
append_hex_to_strbuf(sb, dss->q);

View File

@ -17,6 +17,8 @@ struct SshServerConfig {
unsigned long ssh1_cipher_mask;
bool ssh1_allow_compression;
bool bare_connection;
bool stunt_pretend_to_accept_any_pubkey;
};
Plug *ssh_server_plug(

View File

@ -2072,7 +2072,9 @@ static void swap_screen(Terminal *term, int which,
ttr = term->alt_screen;
term->alt_screen = term->screen;
term->screen = ttr;
term->alt_sblines = find_last_nonempty_line(term, term->alt_screen) + 1;
term->alt_sblines = (
term->alt_screen ?
find_last_nonempty_line(term, term->alt_screen) + 1 : 0);
t = term->curs.x;
if (!reset && !keep_cur_pos)
term->curs.x = term->alt_x;

View File

@ -315,22 +315,25 @@ bool do_cmdline(int argc, char **argv, bool do_everything, Conf *conf)
char *val;
/*
* Macros to make argument handling easier. Note that because
* they need to call `continue', they cannot be contained in
* the usual do {...} while (0) wrapper to make them
* syntactically single statements; hence it is not legal to
* use one of these macros as an unbraced statement between
* `if' and `else'.
* Macros to make argument handling easier.
*
* Note that because they need to call `continue', they cannot be
* contained in the usual do {...} while (0) wrapper to make them
* syntactically single statements. I use the alternative if (1)
* {...} else ((void)0).
*/
#define EXPECTS_ARG { \
if (--argc <= 0) { \
err = true; \
fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
continue; \
} else \
val = *++argv; \
}
#define SECOND_PASS_ONLY do { if (!do_everything) continue; } while (0)
#define EXPECTS_ARG if (1) { \
if (--argc <= 0) { \
err = true; \
fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
continue; \
} else \
val = *++argv; \
} else ((void)0)
#define SECOND_PASS_ONLY if (1) { \
if (!do_everything) \
continue; \
} else ((void)0)
while (--argc > 0) {
const char *p = *++argv;

View File

@ -29,6 +29,7 @@ Socket *platform_make_agent_socket(
if ((errw = make_dir_and_check_ours(socketdir)) != NULL) {
*error = dupprintf("%s: %s\n", socketdir, errw);
sfree(errw);
sfree(socketdir);
return NULL;
}

View File

@ -206,6 +206,8 @@ static void setup_utmp(char *ttyname, char *location)
struct timeval tv;
pw = getpwuid(getuid());
if (!pw)
return; /* can't stamp utmp if we don't have a username */
memset(&utmp_entry, 0, sizeof(utmp_entry));
utmp_entry.ut_type = USER_PROCESS;
utmp_entry.ut_pid = getpid();

View File

@ -775,6 +775,8 @@ int main(int argc, char **argv)
filename_free(logfile);
conf_set_int(conf, CONF_logtype, LGTYP_SSHRAW);
conf_set_int(conf, CONF_logxfovr, LGXF_OVR);
} else if (!strcmp(arg, "--pretend-to-accept-any-pubkey")) {
ssc.stunt_pretend_to_accept_any_pubkey = true;
} else {
fprintf(stderr, "%s: unrecognised option '%s'\n", appname, arg);
exit(1);

View File

@ -5225,7 +5225,7 @@ static void wintw_clip_write(
(int)udata[uindex]);
alen = 1; strcpy(after, "}");
} else {
blen = sprintf(before, "\\u%d", udata[uindex]);
blen = sprintf(before, "\\u%d", (int)udata[uindex]);
alen = 0; after[0] = '\0';
}
}

View File

@ -106,6 +106,7 @@
#define WINHELP_CTX_ssh_share "config-ssh-sharing"
#define WINHELP_CTX_ssh_kexlist "config-ssh-kex-order"
#define WINHELP_CTX_ssh_hklist "config-ssh-hostkey-order"
#define WINHELP_CTX_ssh_hk_known "config-ssh-prefer-known-hostkeys"
#define WINHELP_CTX_ssh_gssapi_kex_delegation "config-ssh-kex-gssapi-delegation"
#define WINHELP_CTX_ssh_kex_repeat "config-ssh-kex-rekey"
#define WINHELP_CTX_ssh_kex_manual_hostkeys "config-ssh-kex-manual-hostkeys"