if (event == EVENT_REFRESH) {
int i;
- static const struct { char *s; int c; } ciphers[] = {
+ static const struct { const char *s; int c; } ciphers[] = {
+ { "ChaCha20 (SSH-2 only)", CIPHER_CHACHA20 },
{ "3DES", CIPHER_3DES },
{ "Blowfish", CIPHER_BLOWFISH },
{ "DES", CIPHER_DES },
for (i = 0; i < CIPHER_MAX; i++) {
int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
int j;
- char *cstr = NULL;
+ const char *cstr = NULL;
for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
if (ciphers[j].c == c) {
cstr = ciphers[j].s;
if (event == EVENT_REFRESH) {
int i;
- static const struct { char *s; int k; } kexes[] = {
+ static const struct { const char *s; int k; } kexes[] = {
{ "Diffie-Hellman group 1", KEX_DHGROUP1 },
{ "Diffie-Hellman group 14", KEX_DHGROUP14 },
{ "Diffie-Hellman group exchange", KEX_DHGEX },
{ "RSA-based key exchange", KEX_RSA },
+ { "ECDH key exchange", KEX_ECDH },
{ "-- warn below here --", KEX_WARN }
};
for (i = 0; i < KEX_MAX; i++) {
int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);
int j;
- char *kstr = NULL;
+ const char *kstr = NULL;
for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
if (kexes[j].k == k) {
kstr = kexes[j].s;
}
}
+static void hklist_handler(union control *ctrl, void *dlg,
+ void *data, int event)
+{
+ Conf *conf = (Conf *)data;
+ if (event == EVENT_REFRESH) {
+ int i;
+
+ static const struct { const char *s; int k; } hks[] = {
+ { "Ed25519", HK_ED25519 },
+ { "ECDSA", HK_ECDSA },
+ { "DSA", HK_DSA },
+ { "RSA", HK_RSA },
+ { "-- warn below here --", HK_WARN }
+ };
+
+ /* Set up the "host key preference" box. */
+ /* (hklist assumed to contain all algorithms) */
+ dlg_update_start(ctrl, dlg);
+ dlg_listbox_clear(ctrl, dlg);
+ for (i = 0; i < HK_MAX; i++) {
+ int k = conf_get_int_int(conf, CONF_ssh_hklist, i);
+ int j;
+ const char *kstr = NULL;
+ for (j = 0; j < lenof(hks); j++) {
+ if (hks[j].k == k) {
+ kstr = hks[j].s;
+ break;
+ }
+ }
+ dlg_listbox_addwithid(ctrl, dlg, kstr, k);
+ }
+ dlg_update_done(ctrl, dlg);
+
+ } else if (event == EVENT_VALCHANGE) {
+ int i;
+
+ /* Update array to match the list box. */
+ for (i=0; i < HK_MAX; i++)
+ conf_set_int_int(conf, CONF_ssh_hklist, i,
+ dlg_listbox_getid(ctrl, dlg, i));
+ }
+}
+
static void printerbox_handler(union control *ctrl, void *dlg,
void *data, int event)
{
if (event == EVENT_REFRESH) {
int nprinters, i;
printer_enum *pe;
- char *printer;
+ const char *printer;
dlg_update_start(ctrl, dlg);
/*
static void sessionsaver_data_free(void *ssdv)
{
struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv;
+ get_sesslist(&ssd->sesslist, FALSE);
sfree(ssd->savedsession);
sfree(ssd);
}
val != NULL;
val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
char *p;
- if (!strcmp(val, "D"))
- p = dupprintf("D%s\t", key+1);
- else
+ if (!strcmp(val, "D")) {
+ char *L;
+ /*
+ * A dynamic forwarding is stored as L12345=D or
+ * 6L12345=D (since it's mutually exclusive with
+ * L12345=anything else), but displayed as D12345
+ * to match the fiction that 'Local', 'Remote' and
+ * 'Dynamic' are three distinct modes and also to
+ * align with OpenSSH's command line option syntax
+ * that people will already be used to. So, for
+ * display purposes, find the L in the key string
+ * and turn it into a D.
+ */
+ p = dupprintf("%s\t", key);
+ L = strchr(p, 'L');
+ if (L) *L = 'D';
+ } else
p = dupprintf("%s\t%s", key, val);
dlg_listbox_add(ctrl, dlg, p);
sfree(p);
}
} else if (event == EVENT_ACTION) {
if (ctrl == pfd->addbutton) {
- char *family, *type, *src, *key, *val;
+ const char *family, *type;
+ char *src, *key, *val;
int whichbutton;
#ifndef NO_IPV6
else if (whichbutton == 2)
family = "6";
else
- family = "";
#endif
+ family = "";
whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
if (whichbutton == 0)
}
if (*type != 'D') {
val = dlg_editbox_get(pfd->destbox, dlg);
- if (!*val || !strchr(val, ':')) {
+ if (!*val || !host_strchr(val, ':')) {
dlg_error_msg(dlg,
"You need to specify a destination address\n"
"in the form \"host.name:port\"");
if (i < 0) {
dlg_beep(dlg);
} else {
- char *key, *val, *p;
+ char *key, *p;
+ const char *val;
key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
if (key) {
}
}
+struct manual_hostkey_data {
+ union control *addbutton, *rembutton, *listbox, *keybox;
+};
+
+static void manual_hostkey_handler(union control *ctrl, void *dlg,
+ void *data, int event)
+{
+ Conf *conf = (Conf *)data;
+ struct manual_hostkey_data *mh =
+ (struct manual_hostkey_data *)ctrl->generic.context.p;
+
+ if (event == EVENT_REFRESH) {
+ if (ctrl == mh->listbox) {
+ char *key, *val;
+ dlg_update_start(ctrl, dlg);
+ dlg_listbox_clear(ctrl, dlg);
+ for (val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
+ NULL, &key);
+ val != NULL;
+ val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
+ key, &key)) {
+ dlg_listbox_add(ctrl, dlg, key);
+ }
+ dlg_update_done(ctrl, dlg);
+ }
+ } else if (event == EVENT_ACTION) {
+ if (ctrl == mh->addbutton) {
+ char *key;
+
+ key = dlg_editbox_get(mh->keybox, dlg);
+ if (!*key) {
+ dlg_error_msg(dlg, "You need to specify a host key or "
+ "fingerprint");
+ sfree(key);
+ return;
+ }
+
+ if (!validate_manual_hostkey(key)) {
+ dlg_error_msg(dlg, "Host key is not in a valid format");
+ } else if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
+ key)) {
+ dlg_error_msg(dlg, "Specified host key is already listed");
+ } else {
+ conf_set_str_str(conf, CONF_ssh_manual_hostkeys, key, "");
+ }
+
+ sfree(key);
+ dlg_refresh(mh->listbox, dlg);
+ } else if (ctrl == mh->rembutton) {
+ int i = dlg_listbox_index(mh->listbox, dlg);
+ if (i < 0) {
+ dlg_beep(dlg);
+ } else {
+ char *key;
+
+ key = conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, i);
+ if (key) {
+ dlg_editbox_set(mh->keybox, dlg, key);
+ /* And delete it */
+ conf_del_str_str(conf, CONF_ssh_manual_hostkeys, key);
+ }
+ }
+ dlg_refresh(mh->listbox, dlg);
+ }
+ }
+}
+
void setup_config_box(struct controlbox *b, int midsession,
int protocol, int protcfginfo)
{
struct ttymodes_data *td;
struct environ_data *ed;
struct portfwd_data *pfd;
+ struct manual_hostkey_data *mh;
union control *c;
char *str;
ctrl_columns(s, 1, 100);
s = ctrl_getset(b, "Session", "otheropts", NULL);
- c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
- HELPCTX(session_coe),
- conf_radiobutton_handler,
- I(CONF_close_on_exit),
- "Always", I(FORCE_ON),
- "Never", I(FORCE_OFF),
- "Only on clean exit", I(AUTO), NULL);
+ ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
+ HELPCTX(session_coe),
+ conf_radiobutton_handler,
+ I(CONF_close_on_exit),
+ "Always", I(FORCE_ON),
+ "Never", I(FORCE_OFF),
+ "Only on clean exit", I(AUTO), NULL);
/*
* The Session/Logging panel.
* logging can sensibly be available.
*/
{
- char *sshlogname, *sshrawlogname;
+ const char *sshlogname, *sshrawlogname;
if ((midsession && protocol == PROT_SSH) ||
(!midsession && backend_from_proto(PROT_SSH))) {
sshlogname = "SSH packets";
HELPCTX(logging_filename),
conf_filesel_handler, I(CONF_logfilename));
ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
- " &T for time, and &H for host name)",
+ " &T for time, &H for host name, and &P for port number)",
HELPCTX(logging_filename));
ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
HELPCTX(logging_exists),
HELPCTX(features_retitle),
conf_checkbox_handler,
I(CONF_no_remote_wintitle));
+ ctrl_checkbox(s, "Disable remote-controlled clearing of scrollback", 'e',
+ HELPCTX(features_clearscroll),
+ conf_checkbox_handler,
+ I(CONF_no_remote_clearscroll));
ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
HELPCTX(features_qtitle),
conf_radiobutton_handler,
#endif
{
- char *label = backend_from_proto(PROT_SSH) ?
+ const char *label = backend_from_proto(PROT_SSH) ?
"Logical name of remote host (e.g. for SSH key lookup):" :
"Logical name of remote host:";
s = ctrl_getset(b, "Connection", "identity",
HELPCTX(proxy_command),
conf_editbox_handler,
I(CONF_proxy_telnet_command), I(1));
+
+ ctrl_radiobuttons(s, "Print proxy diagnostics "
+ "in the terminal window", 'r', 5,
+ HELPCTX(proxy_logging),
+ conf_radiobutton_handler,
+ I(CONF_proxy_log_to_term),
+ "No", I(FORCE_OFF),
+ "Yes", I(FORCE_ON),
+ "Only until session starts", I(AUTO), NULL);
}
/*
ctrl_settitle(b, "Connection/SSH",
"Options controlling SSH connections");
- if (midsession && protcfginfo == 1) {
+ /* SSH-1 or connection-sharing downstream */
+ if (midsession && (protcfginfo == 1 || protcfginfo == -1)) {
s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
"session; it is only here so that sub-panels of it can "
I(CONF_ssh_no_shell));
}
- if (!midsession || protcfginfo != 1) {
+ if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
ctrl_checkbox(s, "Enable compression", 'e',
I(CONF_compression));
}
+ if (!midsession) {
+ s = ctrl_getset(b, "Connection/SSH", "sharing", "Sharing an SSH connection between PuTTY tools");
+
+ ctrl_checkbox(s, "Share SSH connections if possible", 's',
+ HELPCTX(ssh_share),
+ conf_checkbox_handler,
+ I(CONF_ssh_connection_sharing));
+
+ ctrl_text(s, "Permitted roles in a shared connection:",
+ HELPCTX(ssh_share));
+ ctrl_checkbox(s, "Upstream (connecting to the real server)", 'u',
+ HELPCTX(ssh_share),
+ conf_checkbox_handler,
+ I(CONF_ssh_connection_sharing_upstream));
+ ctrl_checkbox(s, "Downstream (connecting to the upstream PuTTY)", 'd',
+ HELPCTX(ssh_share),
+ conf_checkbox_handler,
+ I(CONF_ssh_connection_sharing_downstream));
+ }
+
if (!midsession) {
s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
- ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
+ ctrl_radiobuttons(s, "SSH protocol version:", NO_SHORTCUT, 2,
HELPCTX(ssh_protocol),
conf_radiobutton_handler,
I(CONF_sshprot),
- "1 only", 'l', I(0),
- "1", '1', I(1),
- "2", '2', I(2),
- "2 only", 'y', I(3), NULL);
- }
-
- if (!midsession || protcfginfo != 1) {
- s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
- c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
- HELPCTX(ssh_ciphers),
- cipherlist_handler, P(NULL));
- c->listbox.height = 6;
-
- ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
- HELPCTX(ssh_ciphers),
- conf_checkbox_handler,
- I(CONF_ssh2_des_cbc));
+ "2", '2', I(3),
+ "1 (INSECURE)", '1', I(0), NULL);
}
/*
* The Connection/SSH/Kex panel. (Owing to repeat key
- * exchange, this is all meaningful in mid-session _if_
- * we're using SSH-2 or haven't decided yet.)
+ * exchange, much of this is meaningful in mid-session _if_
+ * we're using SSH-2 and are not a connection-sharing
+ * downstream, or haven't decided yet.)
*/
- if (protcfginfo != 1) {
+ if (protcfginfo != 1 && protcfginfo != -1) {
ctrl_settitle(b, "Connection/SSH/Kex",
"Options controlling SSH key exchange");
HELPCTX(ssh_kex_repeat));
}
+ /*
+ * The 'Connection/SSH/Host keys' panel.
+ */
+ if (protcfginfo != 1 && protcfginfo != -1) {
+ ctrl_settitle(b, "Connection/SSH/Host keys",
+ "Options controlling SSH host keys");
+
+ s = ctrl_getset(b, "Connection/SSH/Host keys", "main",
+ "Host key algorithm preference");
+ c = ctrl_draglist(s, "Algorithm selection policy:", 's',
+ HELPCTX(ssh_hklist),
+ hklist_handler, P(NULL));
+ c->listbox.height = 5;
+ }
+
+ /*
+ * Manual host key configuration is irrelevant mid-session,
+ * as we enforce that the host key for rekeys is the
+ * same as that used at the start of the session.
+ */
+ if (!midsession) {
+ s = ctrl_getset(b, "Connection/SSH/Host keys", "hostkeys",
+ "Manually configure host keys for this connection");
+
+ ctrl_columns(s, 2, 75, 25);
+ c = ctrl_text(s, "Host keys or fingerprints to accept:",
+ HELPCTX(ssh_kex_manual_hostkeys));
+ c->generic.column = 0;
+ /* You want to select from the list, _then_ hit Remove. So
+ * tab order should be that way round. */
+ mh = (struct manual_hostkey_data *)
+ ctrl_alloc(b,sizeof(struct manual_hostkey_data));
+ mh->rembutton = ctrl_pushbutton(s, "Remove", 'r',
+ HELPCTX(ssh_kex_manual_hostkeys),
+ manual_hostkey_handler, P(mh));
+ mh->rembutton->generic.column = 1;
+ mh->rembutton->generic.tabdelay = 1;
+ mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
+ HELPCTX(ssh_kex_manual_hostkeys),
+ manual_hostkey_handler, P(mh));
+ /* This list box can't be very tall, because there's not
+ * much room in the pane on Windows at least. This makes
+ * it become really unhelpful if a horizontal scrollbar
+ * appears, so we suppress that. */
+ mh->listbox->listbox.height = 2;
+ mh->listbox->listbox.hscroll = FALSE;
+ ctrl_tabdelay(s, mh->rembutton);
+ mh->keybox = ctrl_editbox(s, "Key", 'k', 80,
+ HELPCTX(ssh_kex_manual_hostkeys),
+ manual_hostkey_handler, P(mh), P(NULL));
+ mh->keybox->generic.column = 0;
+ mh->addbutton = ctrl_pushbutton(s, "Add key", 'y',
+ HELPCTX(ssh_kex_manual_hostkeys),
+ manual_hostkey_handler, P(mh));
+ mh->addbutton->generic.column = 1;
+ ctrl_columns(s, 1, 100);
+ }
+
+ if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
+ /*
+ * The Connection/SSH/Cipher panel.
+ */
+ ctrl_settitle(b, "Connection/SSH/Cipher",
+ "Options controlling SSH encryption");
+
+ s = ctrl_getset(b, "Connection/SSH/Cipher",
+ "encryption", "Encryption options");
+ c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
+ HELPCTX(ssh_ciphers),
+ cipherlist_handler, P(NULL));
+ c->listbox.height = 6;
+
+ ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
+ HELPCTX(ssh_ciphers),
+ conf_checkbox_handler,
+ I(CONF_ssh2_des_cbc));
+ }
+
if (!midsession) {
/*
"Options controlling SSH authentication");
s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
- ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
- HELPCTX(ssh_auth_bypass),
- conf_checkbox_handler,
- I(CONF_ssh_no_userauth));
ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
'd', HELPCTX(ssh_auth_banner),
conf_checkbox_handler,
I(CONF_ssh_show_banner));
+ ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
+ HELPCTX(ssh_auth_bypass),
+ conf_checkbox_handler,
+ I(CONF_ssh_no_userauth));
s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
"Authentication methods");
if (!midsession) {
/*
- * The Connection/SSH/Bugs panel.
+ * The Connection/SSH/Bugs panels.
*/
ctrl_settitle(b, "Connection/SSH/Bugs",
"Workarounds for SSH server bugs");
s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
"Detection of known bugs in SSH servers");
- ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
- HELPCTX(ssh_bugs_ignore1),
- sshbug_handler, I(CONF_sshbug_ignore1));
- ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
- HELPCTX(ssh_bugs_plainpw1),
- sshbug_handler, I(CONF_sshbug_plainpw1));
- ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
- HELPCTX(ssh_bugs_rsa1),
- sshbug_handler, I(CONF_sshbug_rsa1));
ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
HELPCTX(ssh_bugs_ignore2),
sshbug_handler, I(CONF_sshbug_ignore2));
+ ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
+ HELPCTX(ssh_bugs_rekey2),
+ sshbug_handler, I(CONF_sshbug_rekey2));
ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j',
20, HELPCTX(ssh_bugs_winadj),
sshbug_handler, I(CONF_sshbug_winadj));
- ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
- HELPCTX(ssh_bugs_hmac2),
- sshbug_handler, I(CONF_sshbug_hmac2));
- ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
- HELPCTX(ssh_bugs_derivekey2),
- sshbug_handler, I(CONF_sshbug_derivekey2));
+ ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20,
+ HELPCTX(ssh_bugs_chanreq),
+ sshbug_handler, I(CONF_sshbug_chanreq));
+ ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
+ HELPCTX(ssh_bugs_maxpkt2),
+ sshbug_handler, I(CONF_sshbug_maxpkt2));
+
+ ctrl_settitle(b, "Connection/SSH/More bugs",
+ "Further workarounds for SSH server bugs");
+
+ s = ctrl_getset(b, "Connection/SSH/More bugs", "main",
+ "Detection of known bugs in SSH servers");
ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
HELPCTX(ssh_bugs_rsapad2),
sshbug_handler, I(CONF_sshbug_rsapad2));
+ ctrl_droplist(s, "Only supports pre-RFC4419 SSH-2 DH GEX", 'd', 20,
+ HELPCTX(ssh_bugs_oldgex2),
+ sshbug_handler, I(CONF_sshbug_oldgex2));
+ ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
+ HELPCTX(ssh_bugs_hmac2),
+ sshbug_handler, I(CONF_sshbug_hmac2));
ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
HELPCTX(ssh_bugs_pksessid2),
sshbug_handler, I(CONF_sshbug_pksessid2));
- ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
- HELPCTX(ssh_bugs_rekey2),
- sshbug_handler, I(CONF_sshbug_rekey2));
- ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
- HELPCTX(ssh_bugs_maxpkt2),
- sshbug_handler, I(CONF_sshbug_maxpkt2));
+ ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
+ HELPCTX(ssh_bugs_derivekey2),
+ sshbug_handler, I(CONF_sshbug_derivekey2));
+ ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
+ HELPCTX(ssh_bugs_ignore1),
+ sshbug_handler, I(CONF_sshbug_ignore1));
+ ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
+ HELPCTX(ssh_bugs_plainpw1),
+ sshbug_handler, I(CONF_sshbug_plainpw1));
+ ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
+ HELPCTX(ssh_bugs_rsa1),
+ sshbug_handler, I(CONF_sshbug_rsa1));
}
}
}