From 0d3bb73608f1a5d1d5337e51736d48dad1436af5 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 22 May 2021 12:51:23 +0100 Subject: Initial support for in-process proxy SSH connections. This introduces a new entry to the radio-button list of proxy types, in which the 'Proxy host' box is taken to be the name of an SSH server or saved session. We make an entire subsidiary SSH connection to that host, open a direct-tcpip channel through it, and use that as the connection over which to run the primary network connection. The result is basically the same as if you used a local proxy subprocess, with a command along the lines of 'plink -batch %proxyhost -nc %host:%port'. But it's all done in-process, by having an SshProxy object implement the Socket trait to talk to the main connection, and implement Seat and LogPolicy to talk to its subsidiary SSH backend. All the refactoring in recent years has got us to the point where we can do that without both SSH instances fighting over some global variable or unique piece of infrastructure. From an end user perspective, doing SSH proxying in-process like this is a little bit easier to set up: it doesn't require you to bake the full pathname of Plink into your saved session (or to have it on the system PATH), and the SshProxy setup function automatically turns off SSH features that would be inappropriate in this context, such as additional port forwardings, or acting as a connection-sharing upstream. And it has minor advantages like getting the Event Log for the subsidiary connection interleaved in the main Event Log, as if it were stderr output from a proxy subcommand, without having to deliberately configure the subsidiary Plink into verbose mode. However, this is an initial implementation only, and it doesn't yet support the _big_ payoff for doing this in-process, which (I hope) will be the ability to handle interactive prompts from the subsidiary SSH connection via the same user interface as the primary one. For example, you might need to answer two password prompts in succession, or (the first time you use a session configured this way) confirm the host keys for both proxy and destination SSH servers. Comments in the new source file discuss some design thoughts on filling in this gap. For the moment, if the proxy SSH connection encounters any situation where an interactive prompt is needed, it will make the safe assumption, the same way 'plink -batch' would do. So it's at least no _worse_ than the existing technique of putting the proxy connection in a subprocess. --- config.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index ca808511..0932f894 100644 --- a/config.c +++ b/config.c @@ -2477,16 +2477,26 @@ void setup_config_box(struct controlbox *b, bool midsession, "Options controlling proxy usage"); s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); - ctrl_radiobuttons(s, "Proxy type:", 't', 3, - HELPCTX(proxy_type), - conf_radiobutton_handler, - I(CONF_proxy_type), - "None", I(PROXY_NONE), - "SOCKS 4", I(PROXY_SOCKS4), - "SOCKS 5", I(PROXY_SOCKS5), - "HTTP", I(PROXY_HTTP), - "Telnet", I(PROXY_TELNET), - NULL); + c = ctrl_radiobuttons(s, "Proxy type:", 't', 3, + HELPCTX(proxy_type), + conf_radiobutton_handler, + I(CONF_proxy_type), + "None", I(PROXY_NONE), + "SOCKS 4", I(PROXY_SOCKS4), + "SOCKS 5", I(PROXY_SOCKS5), + "HTTP", I(PROXY_HTTP), + "Telnet", I(PROXY_TELNET), + NULL); + if (ssh_proxy_supported) { + /* Add an extra radio button to the above list. */ + c->radio.nbuttons++; + c->radio.buttons = + sresize(c->radio.buttons, c->radio.nbuttons, char *); + c->radio.buttons[c->radio.nbuttons-1] = dupstr("SSH"); + c->radio.buttondata = + sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); + c->radio.buttondata[c->radio.nbuttons-1] = I(PROXY_SSH); + } ctrl_columns(s, 2, 80, 20); c = ctrl_editbox(s, "Proxy hostname", 'y', 100, HELPCTX(proxy_main), -- cgit v1.2.3 From 5f5c710cf3704737e24ffceb2c918e412a2a674f Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 19 Jun 2021 15:39:15 +0100 Subject: New option to reject 'trivial' success of userauth. Suggested by Manfred Kaiser, who also wrote most of this patch (although outlying parts, like documentation and SSH-1 support, are by me). This is a second line of defence against the kind of spoofing attacks in which a malicious or compromised SSH server rushes the client through the userauth phase of SSH without actually requiring any auth inputs (passwords or signatures or whatever), and then at the start of the connection phase it presents something like a spoof prompt, intended to be taken for part of userauth by the user but in fact with some more sinister purpose. Our existing line of defence against this is the trust sigil system, and as far as I know, that's still working. This option allows a bit of extra defence in depth: if you don't expect your SSH server to trivially accept authentication in the first place, then enabling this option will cause PuTTY to disconnect if it unexpectedly does so, without the user having to spot the presence or absence of a fiddly little sigil anywhere. Several types of authentication count as 'trivial'. The obvious one is the SSH-2 "none" method, which clients always try first so that the failure message will tell them what else they can try, and which a server can instead accept in order to authenticate you unconditionally. But there are two other ways to do it that we know of: one is to run keyboard-interactive authentication and send an empty INFO_REQUEST packet containing no actual prompts for the user, and another even weirder one is to send USERAUTH_SUCCESS in response to the user's preliminary *offer* of a public key (instead of sending the usual PK_OK to request an actual signature from the key). This new option detects all of those, by clearing the 'is_trivial_auth' flag only when we send some kind of substantive authentication response (be it a password, a k-i prompt response, a signature, or a GSSAPI token). So even if there's a further path through the userauth maze we haven't spotted, that somehow avoids sending anything substantive, this strategy should still pick it up. --- config.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'config.c') diff --git a/config.c b/config.c index 0932f894..efb65aa3 100644 --- a/config.c +++ b/config.c @@ -2782,6 +2782,10 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(ssh_auth_bypass), conf_checkbox_handler, I(CONF_ssh_no_userauth)); + ctrl_checkbox(s, "Disconnect if authentication succeeds trivially", + 'n', HELPCTX(ssh_no_trivial_userauth), + conf_checkbox_handler, + I(CONF_ssh_no_trivial_userauth)); s = ctrl_getset(b, "Connection/SSH/Auth", "methods", "Authentication methods"); -- cgit v1.2.3 From c62b7229c1928f875c77108db50ab356c7f59001 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 14 Aug 2021 10:56:20 +0100 Subject: Bug workaround to delay sending our SSH greeting. Ian Jackson recently tried to use the recipe in the psusan manpage for talking to UML, and found that the connection was not successfully set up, because at some point during startup, UML read the SSH greeting (ok, the bare-ssh-connection greeting) from its input fd and threw it away. So by the time psusan was run by the guest init process, the greeting wasn't there to be read. Ian's report: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=991958 I was also able to reproduce this locally, which makes me wonder why I _didn't_ notice it when I originally wrote that part of the psusan man page. It worked for me before, honest! But now it doesn't. Anyway. The ssh verstring module already has a mode switch to decide whether we ought to send our greeting before or after waiting for the other side's greeting (because that decision varies between client and server, and between SSH-1 and SSH-2). So it's easy to implement an override that forces it to 'wait for the server greeting first'. I've added this as yet another bug workaround flag. But unlike all the others, it can't be autodetected from the server's version string, because, of course, we have to act on it _before_ seeing the server's greeting and version string! So it's a manual-only flag. However, I've mentioned it in the UML section of the psusan man page, since that's the place where I _know_ people are likely to need to use this flag. --- config.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'config.c') diff --git a/config.c b/config.c index efb65aa3..4c5da469 100644 --- a/config.c +++ b/config.c @@ -735,6 +735,37 @@ static void sshbug_handler(union control *ctrl, dlgparam *dlg, } } +static void sshbug_handler_manual_only(union control *ctrl, dlgparam *dlg, + void *data, int event) +{ + /* + * This is just like sshbug_handler, except that there's no 'Auto' + * option. Used for bug workaround flags that can't be + * autodetected, and have to be manually enabled if they're to be + * used at all. + */ + Conf *conf = (Conf *)data; + if (event == EVENT_REFRESH) { + int oldconf = conf_get_int(conf, ctrl->listbox.context.i); + dlg_update_start(ctrl, dlg); + dlg_listbox_clear(ctrl, dlg); + dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF); + dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON); + switch (oldconf) { + case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 0); break; + case FORCE_ON: dlg_listbox_select(ctrl, dlg, 1); break; + } + dlg_update_done(ctrl, dlg); + } else if (event == EVENT_SELCHANGE) { + int i = dlg_listbox_index(ctrl, dlg); + if (i < 0) + i = FORCE_OFF; + else + i = dlg_listbox_getid(ctrl, dlg, i); + conf_set_int(conf, ctrl->listbox.context.i, i); + } +} + struct sessionsaver_data { union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton; union control *okbutton, *cancelbutton; @@ -3049,6 +3080,13 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(ssh_bugs_maxpkt2), sshbug_handler, I(CONF_sshbug_maxpkt2)); + s = ctrl_getset(b, "Connection/SSH/Bugs", "manual", + "Manually enabled workarounds"); + ctrl_droplist(s, "Discards data sent before its greeting", 'd', 20, + HELPCTX(ssh_bugs_dropstart), + sshbug_handler_manual_only, + I(CONF_sshbug_dropstart)); + ctrl_settitle(b, "Connection/SSH/More bugs", "Further workarounds for SSH server bugs"); -- cgit v1.2.3 From 22911ccdcc37c7955bfc1c45a88d3762d1206da7 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 18 Oct 2021 20:00:25 +0100 Subject: New config option for shifted arrow key handling. This commit introduces a new config option for how to handle shifted arrow keys. In the default mode (SHARROW_APPLICATION), we do what we've always done: Ctrl flips the arrow keys between sending their most usual escape sequences (ESC [ A ... ESC [ D) and sending the 'application cursor keys' sequences (ESC O A ... ESC O D). Whichever of those modes is currently configured, Ctrl+arrow sends the other one. In the new mode (SHARROW_BITMAP), application cursor key mode is unaffected by any shift keys, but the default sequences acquire two numeric arguments. The first argument is 1 (reflecting the fact that a shifted arrow key still notionally moves just 1 character cell); the second is the bitmap (1 for Shift) + (2 for Alt) + (4 for Ctrl), offset by 1. (Except that if _none_ of those modifiers is pressed, both numeric arguments are simply omitted.) The new bitmap mode is what current xterm generates, and also what Windows ConPTY seems to expect. If you start an ordinary Command Prompt and launch into WSL, those are the sequences it will generate for shifted arrow keys; conversely, if you run a Command Prompt within a ConPTY, then these sequences for Ctrl+arrow will have the effect you expect in cmd.exe command-line editing (going backward or forward a word). For that reason, I enable this mode unconditionally when launching Windows pterm. --- config.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'config.c') diff --git a/config.c b/config.c index 4c5da469..dd39862d 100644 --- a/config.c +++ b/config.c @@ -2016,6 +2016,12 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_funky_type), "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2), "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL); + ctrl_radiobuttons(s, "Shift/Ctrl/Alt with the arrow keys", 'w', 2, + HELPCTX(keyboard_sharrow), + conf_radiobutton_handler, + I(CONF_sharrow_type), + "Ctrl toggles app mode", I(SHARROW_APPLICATION), + "xterm-style bitmap", I(SHARROW_BITMAP), NULL); s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad", "Application keypad settings:"); -- cgit v1.2.3 From b13f3d079b66f25d26179185c65d40b348b4b570 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 23 Oct 2021 11:04:53 +0100 Subject: New function-key mode similar to modern xterm. This is the same as the previous FUNKY_XTERM mode if you don't press any modifier keys, but now Shift or Ctrl or Alt with function keys adds an extra bitmap parameter. The bitmaps are the same as the ones used by the new SHARROW_BITMAP arrow key mode. --- config.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index dd39862d..e7597831 100644 --- a/config.c +++ b/config.c @@ -2010,12 +2010,18 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_bool_handler, I(CONF_rxvt_homeend), "Standard", I(false), "rxvt", I(true), NULL); - ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3, + ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 4, HELPCTX(keyboard_funkeys), conf_radiobutton_handler, I(CONF_funky_type), - "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2), - "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL); + "ESC[n~", I(FUNKY_TILDE), + "Linux", I(FUNKY_LINUX), + "Xterm R6", I(FUNKY_XTERM), + "VT400", I(FUNKY_VT400), + "VT100+", I(FUNKY_VT100P), + "SCO", I(FUNKY_SCO), + "Xterm 216+", I(FUNKY_XTERM_216), + NULL); ctrl_radiobuttons(s, "Shift/Ctrl/Alt with the arrow keys", 'w', 2, HELPCTX(keyboard_sharrow), conf_radiobutton_handler, -- cgit v1.2.3 From 5374444879057ad6f013e57716e49dfd3a10ed6e Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 23 Oct 2021 18:26:34 +0100 Subject: Lowercase version of BackendVtable's displayname. The current 'displayname' field is designed for presenting in the config UI, so it starts with a capital letter even when it's not a proper noun. If I want to name the backend in the middle of a sentence, I'll need a version that starts with lowercase where appropriate. The old field is renamed displayname_tc, to avoid ambiguity. --- config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index e7597831..8114a586 100644 --- a/config.c +++ b/config.c @@ -312,7 +312,7 @@ static void config_protocols_handler(union control *ctrl, dlgparam *dlg, for (size_t i = n_ui_backends; i < PROTOCOL_LIMIT && backends[i]; i++) { dlg_listbox_addwithid(ctrl, dlg, - backends[i]->displayname, + backends[i]->displayname_tc, backends[i]->protocol); if (backends[i]->protocol == curproto) curentry = i - n_ui_backends; @@ -1793,7 +1793,7 @@ void setup_config_box(struct controlbox *b, bool midsession, for (size_t i = 0; i < n_ui_backends; i++) { assert(backends[i]); c->radio.buttons[c->radio.nbuttons] = - dupstr(backends[i]->displayname); + dupstr(backends[i]->displayname_tc); c->radio.shortcuts[c->radio.nbuttons] = (backends[i]->protocol == PROT_SSH ? 's' : backends[i]->protocol == PROT_SERIAL ? 'r' : -- cgit v1.2.3 From faf1601a5549eda9298f72f7c0f68f39c8f97764 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 15 Apr 2022 17:19:47 +0100 Subject: Implement OpenSSH 9.x's NTRU Prime / Curve25519 kex. This consists of DJB's 'Streamlined NTRU Prime' quantum-resistant cryptosystem, currently in round 3 of the NIST post-quantum key exchange competition; it's run in parallel with ordinary Curve25519, and generates a shared secret combining the output of both systems. (Hence, even if you don't trust this newfangled NTRU Prime thing at all, it's at least no _less_ secure than the kex you were using already.) As the OpenSSH developers point out, key exchange is the most urgent thing to make quantum-resistant, even before working quantum computers big enough to break crypto become available, because a break of the kex algorithm can be applied retroactively to recordings of your past sessions. By contrast, authentication is a real-time protocol, and can only be broken by a quantum computer if there's one available to attack you _already_. I've implemented both sides of the mechanism, so that PuTTY and Uppity both support it. In my initial testing, the two sides can both interoperate with the appropriate half of OpenSSH, and also (of course, but it would be embarrassing to mess it up) with each other. --- config.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'config.c') diff --git a/config.c b/config.c index 8114a586..1fbbb3e8 100644 --- a/config.c +++ b/config.c @@ -567,6 +567,8 @@ static void kexlist_handler(union control *ctrl, dlgparam *dlg, { "Diffie-Hellman group exchange", KEX_DHGEX }, { "RSA-based key exchange", KEX_RSA }, { "ECDH key exchange", KEX_ECDH }, + { "NTRU Prime / Curve25519 hybrid kex" + " (quantum-resistant)", KEX_NTRU_HYBRID }, { "-- warn below here --", KEX_WARN } }; -- cgit v1.2.3 From 2a26ebd0d58a4adb6d5f2c3fde0db01525837d74 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 22 Apr 2022 14:55:44 +0100 Subject: Turn the proxy type radio buttons into a dropdown list. This makes room to add more entries without the Proxy panel overflowing. It also means we can put in a bit more explanation in some of the more cryptic one-word names! --- config.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 21 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 1fbbb3e8..7b96c7b4 100644 --- a/config.c +++ b/config.c @@ -1708,6 +1708,58 @@ static void serial_flow_handler(union control *ctrl, dlgparam *dlg, } } +void proxy_type_handler(union control *ctrl, dlgparam *dlg, + void *data, int event) +{ + Conf *conf = (Conf *)data; + if (event == EVENT_REFRESH) { + /* + * We must fetch the previously configured value from the Conf + * before we start modifying the drop-down list, otherwise the + * spurious SELCHANGE we trigger in the process will overwrite + * the value we wanted to keep. + */ + int proxy_type = conf_get_int(conf, CONF_proxy_type); + + dlg_update_start(ctrl, dlg); + dlg_listbox_clear(ctrl, dlg); + + int index_to_select = 0, current_index = 0; + +#define ADD(id, title) do { \ + dlg_listbox_addwithid(ctrl, dlg, title, id); \ + if (id == proxy_type) \ + index_to_select = current_index; \ + current_index++; \ + } while (0) + + ADD(PROXY_NONE, "None"); + ADD(PROXY_SOCKS5, "SOCKS 5"); + ADD(PROXY_SOCKS4, "SOCKS 4"); + ADD(PROXY_HTTP, "HTTP CONNECT"); + if (ssh_proxy_supported) { + ADD(PROXY_SSH, "SSH to proxy and use port forwarding"); + } + if (ctrl->generic.context.i & PROXY_UI_FLAG_LOCAL) { + ADD(PROXY_CMD, "Local (run a subprogram to connect)"); + } + ADD(PROXY_TELNET, "'Telnet' (send an ad-hoc command)"); + +#undef ADD + + dlg_listbox_select(ctrl, dlg, index_to_select); + + dlg_update_done(ctrl, dlg); + } else if (event == EVENT_SELCHANGE) { + int i = dlg_listbox_index(ctrl, dlg); + if (i < 0) + i = AUTO; + else + i = dlg_listbox_getid(ctrl, dlg, i); + conf_set_int(conf, CONF_proxy_type, i); + } +} + void setup_config_box(struct controlbox *b, bool midsession, int protocol, int protcfginfo) { @@ -2522,26 +2574,8 @@ void setup_config_box(struct controlbox *b, bool midsession, "Options controlling proxy usage"); s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); - c = ctrl_radiobuttons(s, "Proxy type:", 't', 3, - HELPCTX(proxy_type), - conf_radiobutton_handler, - I(CONF_proxy_type), - "None", I(PROXY_NONE), - "SOCKS 4", I(PROXY_SOCKS4), - "SOCKS 5", I(PROXY_SOCKS5), - "HTTP", I(PROXY_HTTP), - "Telnet", I(PROXY_TELNET), - NULL); - if (ssh_proxy_supported) { - /* Add an extra radio button to the above list. */ - c->radio.nbuttons++; - c->radio.buttons = - sresize(c->radio.buttons, c->radio.nbuttons, char *); - c->radio.buttons[c->radio.nbuttons-1] = dupstr("SSH"); - c->radio.buttondata = - sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); - c->radio.buttondata[c->radio.nbuttons-1] = I(PROXY_SSH); - } + c = ctrl_droplist(s, "Proxy type:", 't', 70, + HELPCTX(proxy_type), proxy_type_handler, I(0)); ctrl_columns(s, 2, 80, 20); c = ctrl_editbox(s, "Proxy hostname", 'y', 100, HELPCTX(proxy_main), @@ -2579,7 +2613,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_editbox_handler, I(CONF_proxy_password), I(1)); c->editbox.password = true; - ctrl_editbox(s, "Telnet command", 'm', 100, + ctrl_editbox(s, "Command to send to proxy (for some types)", 'm', 100, HELPCTX(proxy_command), conf_editbox_handler, I(CONF_proxy_telnet_command), I(1)); -- cgit v1.2.3 From 6f7c52dccee36f6593a3fa4498ce1cc5d9952c66 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 22 Apr 2022 14:12:15 +0100 Subject: Add exec/subsystem versions of SSH proxying. This is a simple tweak to the existing in-process SSH jump host support, where instead of opening a direct-tcpip channel to the destination host, we open a session channel and run a process in it to make the connection to the destination. So, where the existing jump host support replaced a local proxy command along the lines of "plink %proxyhost -nc %host %port", this one replaces "plink %proxyhost run-some-command". Also added a corresponding option to use a subsystem to make the connection. (Someone could configure an SSH server to support specific subsystem names for particular destinations, or a general schema of subsystem names that include the destination address in some standard format.) To avoid overflowing the already-full Proxy config panel with an extra subtype selector, I've put these in as additional top-level proxy types, so that instead of just PROXY_SSH we now have three PROXY_SSH_foo. --- config.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'config.c') diff --git a/config.c b/config.c index 7b96c7b4..f48e6de7 100644 --- a/config.c +++ b/config.c @@ -1738,7 +1738,9 @@ void proxy_type_handler(union control *ctrl, dlgparam *dlg, ADD(PROXY_SOCKS4, "SOCKS 4"); ADD(PROXY_HTTP, "HTTP CONNECT"); if (ssh_proxy_supported) { - ADD(PROXY_SSH, "SSH to proxy and use port forwarding"); + ADD(PROXY_SSH_TCPIP, "SSH to proxy and use port forwarding"); + ADD(PROXY_SSH_EXEC, "SSH to proxy and execute a command"); + ADD(PROXY_SSH_SUBSYSTEM, "SSH to proxy and invoke a subsystem"); } if (ctrl->generic.context.i & PROXY_UI_FLAG_LOCAL) { ADD(PROXY_CMD, "Local (run a subprogram to connect)"); -- cgit v1.2.3 From df3a21d97b5f1d022d561cd58da843bf6a87340b Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 21 Apr 2022 10:55:44 +0100 Subject: Support for detached certificates in userauth. This is triggered by a new config option, or alternatively a -cert command-line option. You provide a certificate file (i.e. a public key containing one of the cert key formats), and then, whenever you authenticate with a private key that matches the public key inside that certificate, the certificate will be sent to the server in place of whatever public key it would have used before. I expect this to be more convenient for some users than the approach of baking the certificate into a modified version of the PPK file - especially users who want to use different certificates on the same key, either in sequence (if a CA continually reissues certificates with short lifetimes) or in parallel (if different hosts trust different CAs). In particular, this substitution is applied consistently, even when doing authentication via an agent. So if your bare private key is held in Pageant, you can _still_ specify a detached certificate, and PuTTY will spot that the key it's picked from Pageant matches that certificate, and do the same substitution. The detached certificate also overrides an existing certificate, if there was one on the public key already. --- config.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'config.c') diff --git a/config.c b/config.c index f48e6de7..3ab92bb8 100644 --- a/config.c +++ b/config.c @@ -2896,6 +2896,10 @@ void setup_config_box(struct controlbox *b, bool midsession, FILTER_KEY_FILES, false, "Select private key file", HELPCTX(ssh_auth_privkey), conf_filesel_handler, I(CONF_keyfile)); + ctrl_filesel(s, "Certificate to use with the private key:", 'e', + NULL, false, "Select certificate file", + HELPCTX(ssh_auth_privkey), + conf_filesel_handler, I(CONF_detached_cert)); #ifndef NO_GSSAPI /* -- cgit v1.2.3 From 21d4754b6a0c98c6d75f5a374ff71f108263f02f Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 22 Apr 2022 12:07:24 +0100 Subject: Initial support for host certificates. Now we offer the OpenSSH certificate key types in our KEXINIT host key algorithm list, so that if the server has a certificate, they can send it to us. There's a new storage.h abstraction for representing a list of trusted host CAs, and which ones are trusted to certify hosts for what domains. This is stored outside the normal saved session data, because the whole point of host certificates is to avoid per-host faffing. Configuring this set of trusted CAs is done via a new GUI dialog box, separate from the main PuTTY config box (because it modifies a single set of settings across all saved sessions), which you can launch by clicking a button in the 'Host keys' pane. The GUI is pretty crude for the moment, and very much at a 'just about usable' stage right now. It will want some polishing. If we have no CA configured that matches the hostname, we don't offer to receive certified host keys in the first place. So for existing users who haven't set any of this up yet, nothing will immediately change. Currently, if we do offer to receive certified host keys and the server presents one signed by a CA we don't trust, PuTTY will bomb out unconditionally with an error, instead of offering a confirmation box. That's an unfinished part which I plan to fix before this goes into a release. --- config.c | 379 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 379 insertions(+) (limited to 'config.c') diff --git a/config.c b/config.c index 3ab92bb8..01d58abd 100644 --- a/config.c +++ b/config.c @@ -9,6 +9,7 @@ #include "putty.h" #include "dialog.h" #include "storage.h" +#include "tree234.h" #define PRINTER_DISABLED_STRING "None (printing disabled)" @@ -1762,6 +1763,13 @@ void proxy_type_handler(union control *ctrl, dlgparam *dlg, } } +static void host_ca_button_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + if (event == EVENT_ACTION) + show_ca_config_box(dp); +} + void setup_config_box(struct controlbox *b, bool midsession, int protocol, int protcfginfo) { @@ -2826,6 +2834,16 @@ void setup_config_box(struct controlbox *b, bool midsession, ctrl_columns(s, 1, 100); } + /* + * But there's no reason not to forbid access to the host CA + * configuration box, which is common across sessions in any + * case. + */ + s = ctrl_getset(b, "Connection/SSH/Host keys", "ca", + "Configure trusted certification authorities"); + c = ctrl_pushbutton(s, "Configure host CAs", NO_SHORTCUT, + HELPCTX(no_help), host_ca_button_handler, I(0)); + if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) { /* * The Connection/SSH/Cipher panel. @@ -3298,3 +3316,364 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_supdup_scroll)); } } + +struct ca_state { + union control *ca_name_edit; + union control *ca_reclist; + union control *ca_pubkey_edit; + union control *ca_wclist; + union control *ca_wc_edit; + char *name, *pubkey, *wc; + tree234 *ca_names; /* stores plain 'char *' */ + tree234 *host_wcs; /* stores plain 'char *' */ +}; + +static int ca_name_compare(void *av, void *bv) +{ + return strcmp((const char *)av, (const char *)bv); +} + +static inline void clear_string_tree(tree234 *t) +{ + char *p; + while ((p = delpos234(t, 0)) != NULL) + sfree(p); +} + +static void ca_state_free(void *vctx) +{ + struct ca_state *st = (struct ca_state *)vctx; + clear_string_tree(st->ca_names); + freetree234(st->ca_names); + clear_string_tree(st->host_wcs); + freetree234(st->host_wcs); + sfree(st->name); + sfree(st->wc); + sfree(st); +} + +static void ca_refresh_name_list(struct ca_state *st) +{ + clear_string_tree(st->ca_names); + + host_ca_enum *hce = enum_host_ca_start(); + if (hce) { + strbuf *namebuf = strbuf_new(); + + while (strbuf_clear(namebuf), enum_host_ca_next(hce, namebuf)) { + char *name = dupstr(namebuf->s); + char *added = add234(st->ca_names, name); + /* Just imaginable that concurrent filesystem access might + * cause a repetition; avoid leaking memory if so */ + if (added != name) + sfree(name); + } + + strbuf_free(namebuf); + enum_host_ca_finish(hce); + } +} + +static void ca_load_selected_record(struct ca_state *st, dlgparam *dp) +{ + int i = dlg_listbox_index(st->ca_reclist, dp); + if (i < 0) { + dlg_beep(dp); + return; + } + const char *name = index234(st->ca_names, i); + if (!name) { /* in case the list box and the tree got out of sync */ + dlg_beep(dp); + return; + } + host_ca *hca = host_ca_load(name); + if (!hca) { + char *msg = dupprintf("Unable to load host CA record '%s'", name); + dlg_error_msg(dp, msg); + sfree(msg); + return; + } + + sfree(st->name); + st->name = dupstr(hca->name); + + sfree(st->pubkey); + st->pubkey = strbuf_to_str( + base64_encode_sb(ptrlen_from_strbuf(hca->ca_public_key), 0)); + + clear_string_tree(st->host_wcs); + for (size_t i = 0; i < hca->n_hostname_wildcards; i++) { + char *name = dupstr(hca->hostname_wildcards[i]); + char *added = add234(st->host_wcs, name); + if (added != name) + sfree(name); /* de-duplicate, just in case */ + } + + host_ca_free(hca); + + dlg_refresh(st->ca_name_edit, dp); + dlg_refresh(st->ca_pubkey_edit, dp); + dlg_refresh(st->ca_wclist, dp); +} + +static void ca_ok_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + if (event == EVENT_ACTION) + dlg_end(dp, 0); +} + +static void ca_name_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_REFRESH) { + dlg_editbox_set(ctrl, dp, st->name); + } else if (event == EVENT_VALCHANGE) { + sfree(st->name); + st->name = dlg_editbox_get(ctrl, dp); + + /* + * Try to auto-select the typed name in the list. + */ + int index; + if (!findrelpos234(st->ca_names, st->name, NULL, REL234_GE, &index)) + index = count234(st->ca_names) - 1; + if (index >= 0) + dlg_listbox_select(st->ca_reclist, dp, index); + } +} + +static void ca_reclist_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_REFRESH) { + dlg_update_start(ctrl, dp); + dlg_listbox_clear(ctrl, dp); + const char *name; + for (int i = 0; (name = index234(st->ca_names, i)) != NULL; i++) + dlg_listbox_add(ctrl, dp, name); + dlg_update_done(ctrl, dp); + } else if (event == EVENT_ACTION) { + /* Double-clicking a session loads it */ + ca_load_selected_record(st, dp); + } +} + +static void ca_load_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_ACTION) { + ca_load_selected_record(st, dp); + } +} + +static void ca_save_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_ACTION) { + host_ca *hca = snew(host_ca); + memset(hca, 0, sizeof(*hca)); + hca->name = dupstr(st->name); + hca->ca_public_key = base64_decode_sb(ptrlen_from_asciz(st->pubkey)); + hca->n_hostname_wildcards = count234(st->host_wcs); + hca->hostname_wildcards = snewn(hca->n_hostname_wildcards, char *); + for (size_t i = 0; i < hca->n_hostname_wildcards; i++) + hca->hostname_wildcards[i] = dupstr(index234(st->host_wcs, i)); + char *error = host_ca_save(hca); + host_ca_free(hca); + + if (error) { + dlg_error_msg(dp, error); + sfree(error); + } else { + ca_refresh_name_list(st); + dlg_refresh(st->ca_reclist, dp); + } + } +} + +static void ca_delete_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_ACTION) { + int i = dlg_listbox_index(st->ca_reclist, dp); + if (i < 0) { + dlg_beep(dp); + return; + } + const char *name = index234(st->ca_names, i); + if (!name) { /* in case the list box and the tree got out of sync */ + dlg_beep(dp); + return; + } + + char *error = host_ca_delete(name); + if (error) { + dlg_error_msg(dp, error); + sfree(error); + } else { + ca_refresh_name_list(st); + dlg_refresh(st->ca_reclist, dp); + } + } +} + +static void ca_pubkey_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_REFRESH) { + dlg_editbox_set(ctrl, dp, st->pubkey); + } else if (event == EVENT_VALCHANGE) { + sfree(st->pubkey); + st->pubkey = dlg_editbox_get(ctrl, dp); + } +} + +static void ca_wclist_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_REFRESH) { + dlg_update_start(ctrl, dp); + dlg_listbox_clear(ctrl, dp); + const char *name; + for (int i = 0; (name = index234(st->host_wcs, i)) != NULL; i++) + dlg_listbox_add(ctrl, dp, name); + dlg_update_done(ctrl, dp); + } +} + +static void ca_wc_edit_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_REFRESH) { + dlg_editbox_set(ctrl, dp, st->wc); + } else if (event == EVENT_VALCHANGE) { + sfree(st->wc); + st->wc = dlg_editbox_get(ctrl, dp); + } +} + +static void ca_wc_add_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_ACTION) { + if (!st->wc) { + dlg_beep(dp); + return; + } + + if (add234(st->host_wcs, st->wc) == st->wc) { + dlg_refresh(st->ca_wclist, dp); + } else { + sfree(st->wc); + } + + st->wc = dupstr(""); + dlg_refresh(st->ca_wc_edit, dp); + } +} + +static void ca_wc_rem_handler(union control *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + if (event == EVENT_ACTION) { + int i = dlg_listbox_index(st->ca_wclist, dp); + if (i < 0) { + dlg_beep(dp); + return; + } + char *wc = delpos234(st->host_wcs, i); + if (!wc) { + dlg_beep(dp); + return; + } + + sfree(st->wc); + st->wc = wc; + dlg_refresh(st->ca_wclist, dp); + dlg_refresh(st->ca_wc_edit, dp); + } +} + +void setup_ca_config_box(struct controlbox *b) +{ + struct controlset *s; + union control *c; + + /* Internal state for manipulating the host CA system */ + struct ca_state *st = (struct ca_state *)ctrl_alloc_with_free( + b, sizeof(struct ca_state), ca_state_free); + memset(st, 0, sizeof(*st)); + st->name = dupstr(""); + st->pubkey = dupstr(""); + st->ca_names = newtree234(ca_name_compare); + st->host_wcs = newtree234(ca_name_compare); + ca_refresh_name_list(st); + + /* Action area, with the Done button in it */ + s = ctrl_getset(b, "", "", ""); + ctrl_columns(s, 5, 20, 20, 20, 20, 20); + c = ctrl_pushbutton(s, "Done", 'o', HELPCTX(no_help), + ca_ok_handler, P(st)); + c->button.isdefault = true; + c->generic.column = 4; + + /* Load/save box, as similar as possible to the main saved sessions one */ + s = ctrl_getset(b, "Main", "loadsave", + "Load, save or delete a host CA record"); + ctrl_columns(s, 2, 75, 25); + c = ctrl_editbox(s, "Name for this CA (shown in log messages)", + 'n', 100, HELPCTX(no_help), + ca_name_handler, P(st), P(NULL)); + c->generic.column = 0; + st->ca_name_edit = c; + /* Reset columns so that the buttons are alongside the list, rather + * than alongside that edit box. */ + ctrl_columns(s, 1, 100); + ctrl_columns(s, 2, 75, 25); + c = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(no_help), + ca_reclist_handler, P(st)); + c->generic.column = 0; + c->listbox.height = 6; + st->ca_reclist = c; + c = ctrl_pushbutton(s, "Load", 'l', HELPCTX(no_help), + ca_load_handler, P(st)); + c->generic.column = 1; + c = ctrl_pushbutton(s, "Save", 'v', HELPCTX(no_help), + ca_save_handler, P(st)); + c->generic.column = 1; + c = ctrl_pushbutton(s, "Delete", 'd', HELPCTX(no_help), + ca_delete_handler, P(st)); + c->generic.column = 1; + + /* Box containing the details of a specific CA record */ + s = ctrl_getset(b, "Main", "details", "Details of a host CA record"); + c = ctrl_editbox(s, "Public key of certification authority", 'k', 100, + HELPCTX(no_help), ca_pubkey_handler, P(st), P(NULL)); + st->ca_pubkey_edit = c; + c = ctrl_listbox(s, "Hostname patterns this key is trusted to certify", + NO_SHORTCUT, HELPCTX(no_help), ca_wclist_handler, P(st)); + c->listbox.height = 3; + st->ca_wclist = c; + ctrl_columns(s, 3, 70, 15, 15); + c = ctrl_editbox(s, "Hostname pattern to add", 'h', 100, + HELPCTX(no_help), ca_wc_edit_handler, P(st), P(NULL)); + c->generic.column = 0; + st->ca_wc_edit = c; + c = ctrl_pushbutton(s, "Add", NO_SHORTCUT, HELPCTX(no_help), + ca_wc_add_handler, P(st)); + c->generic.column = 1; + c = ctrl_pushbutton(s, "Remove", NO_SHORTCUT, HELPCTX(no_help), + ca_wc_rem_handler, P(st)); + c->generic.column = 2; +} -- cgit v1.2.3 From 77d15c46c308d262feca61698a74e59fe0c1f3f1 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 1 May 2022 09:48:38 +0100 Subject: New typedef 'dlgcontrol' wrapping 'union control'. I'm about to change my mind about whether its top-level nature is struct or union, and rather than change the key word 'union' to 'struct' at every point of use, it's nicer to just get rid of the keyword completely. So it has a shiny new name. --- config.c | 120 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 60 insertions(+), 60 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 01d58abd..c4c932a9 100644 --- a/config.c +++ b/config.c @@ -16,7 +16,7 @@ #define HOST_BOX_TITLE "Host Name (or IP address)" #define PORT_BOX_TITLE "Port" -void conf_radiobutton_handler(union control *ctrl, dlgparam *dlg, +void conf_radiobutton_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { int button; @@ -44,7 +44,7 @@ void conf_radiobutton_handler(union control *ctrl, dlgparam *dlg, } } -void conf_radiobutton_bool_handler(union control *ctrl, dlgparam *dlg, +void conf_radiobutton_bool_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { int button; @@ -72,7 +72,7 @@ void conf_radiobutton_bool_handler(union control *ctrl, dlgparam *dlg, } #define CHECKBOX_INVERT (1<<30) -void conf_checkbox_handler(union control *ctrl, dlgparam *dlg, +void conf_checkbox_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { int key; @@ -104,7 +104,7 @@ void conf_checkbox_handler(union control *ctrl, dlgparam *dlg, } } -void conf_editbox_handler(union control *ctrl, dlgparam *dlg, +void conf_editbox_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { /* @@ -153,7 +153,7 @@ void conf_editbox_handler(union control *ctrl, dlgparam *dlg, } } -void conf_filesel_handler(union control *ctrl, dlgparam *dlg, +void conf_filesel_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { int key = ctrl->fileselect.context.i; @@ -169,7 +169,7 @@ void conf_filesel_handler(union control *ctrl, dlgparam *dlg, } } -void conf_fontsel_handler(union control *ctrl, dlgparam *dlg, +void conf_fontsel_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { int key = ctrl->fontselect.context.i; @@ -185,7 +185,7 @@ void conf_fontsel_handler(union control *ctrl, dlgparam *dlg, } } -static void config_host_handler(union control *ctrl, dlgparam *dlg, +static void config_host_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -217,7 +217,7 @@ static void config_host_handler(union control *ctrl, dlgparam *dlg, } } -static void config_port_handler(union control *ctrl, dlgparam *dlg, +static void config_port_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -258,7 +258,7 @@ static void config_port_handler(union control *ctrl, dlgparam *dlg, } struct hostport { - union control *host, *port, *protradio, *protlist; + dlgcontrol *host, *port, *protradio, *protlist; bool mid_refresh; }; @@ -269,7 +269,7 @@ struct hostport { * and refreshes both host and port boxes when switching to/from the * serial backend. */ -static void config_protocols_handler(union control *ctrl, dlgparam *dlg, +static void config_protocols_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -422,7 +422,7 @@ static void config_protocols_handler(union control *ctrl, dlgparam *dlg, } } -static void loggingbuttons_handler(union control *ctrl, dlgparam *dlg, +static void loggingbuttons_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { int button; @@ -451,7 +451,7 @@ static void loggingbuttons_handler(union control *ctrl, dlgparam *dlg, } } -static void numeric_keypad_handler(union control *ctrl, dlgparam *dlg, +static void numeric_keypad_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { int button; @@ -482,7 +482,7 @@ static void numeric_keypad_handler(union control *ctrl, dlgparam *dlg, } } -static void cipherlist_handler(union control *ctrl, dlgparam *dlg, +static void cipherlist_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -528,7 +528,7 @@ static void cipherlist_handler(union control *ctrl, dlgparam *dlg, } #ifndef NO_GSSAPI -static void gsslist_handler(union control *ctrl, dlgparam *dlg, +static void gsslist_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -555,7 +555,7 @@ static void gsslist_handler(union control *ctrl, dlgparam *dlg, } #endif -static void kexlist_handler(union control *ctrl, dlgparam *dlg, +static void kexlist_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -601,7 +601,7 @@ static void kexlist_handler(union control *ctrl, dlgparam *dlg, } } -static void hklist_handler(union control *ctrl, dlgparam *dlg, +static void hklist_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -645,7 +645,7 @@ static void hklist_handler(union control *ctrl, dlgparam *dlg, } } -static void printerbox_handler(union control *ctrl, dlgparam *dlg, +static void printerbox_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -681,7 +681,7 @@ static void printerbox_handler(union control *ctrl, dlgparam *dlg, } } -static void codepage_handler(union control *ctrl, dlgparam *dlg, +static void codepage_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -705,7 +705,7 @@ static void codepage_handler(union control *ctrl, dlgparam *dlg, } } -static void sshbug_handler(union control *ctrl, dlgparam *dlg, +static void sshbug_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -738,7 +738,7 @@ static void sshbug_handler(union control *ctrl, dlgparam *dlg, } } -static void sshbug_handler_manual_only(union control *ctrl, dlgparam *dlg, +static void sshbug_handler_manual_only(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { /* @@ -770,8 +770,8 @@ static void sshbug_handler_manual_only(union control *ctrl, dlgparam *dlg, } struct sessionsaver_data { - union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton; - union control *okbutton, *cancelbutton; + dlgcontrol *editbox, *listbox, *loadbutton, *savebutton, *delbutton; + dlgcontrol *okbutton, *cancelbutton; struct sesslist sesslist; bool midsession; char *savedsession; /* the current contents of ssd->editbox */ @@ -813,7 +813,7 @@ static bool load_selected_session( return true; } -static void sessionsaver_handler(union control *ctrl, dlgparam *dlg, +static void sessionsaver_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -951,10 +951,10 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg, } struct charclass_data { - union control *listbox, *editbox, *button; + dlgcontrol *listbox, *editbox, *button; }; -static void charclass_handler(union control *ctrl, dlgparam *dlg, +static void charclass_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -992,7 +992,7 @@ static void charclass_handler(union control *ctrl, dlgparam *dlg, } struct colour_data { - union control *listbox, *redit, *gedit, *bedit, *button; + dlgcontrol *listbox, *redit, *gedit, *bedit, *button; }; /* Array of the user-visible colour names defined in the list macro in @@ -1003,7 +1003,7 @@ static const char *const colours[] = { #undef CONF_COLOUR_NAME_DECL }; -static void colour_handler(union control *ctrl, dlgparam *dlg, +static void colour_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -1109,10 +1109,10 @@ static void colour_handler(union control *ctrl, dlgparam *dlg, } struct ttymodes_data { - union control *valradio, *valbox, *setbutton, *listbox; + dlgcontrol *valradio, *valbox, *setbutton, *listbox; }; -static void ttymodes_handler(union control *ctrl, dlgparam *dlg, +static void ttymodes_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -1194,10 +1194,10 @@ static void ttymodes_handler(union control *ctrl, dlgparam *dlg, } struct environ_data { - union control *varbox, *valbox, *addbutton, *rembutton, *listbox; + dlgcontrol *varbox, *valbox, *addbutton, *rembutton, *listbox; }; -static void environ_handler(union control *ctrl, dlgparam *dlg, +static void environ_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -1266,14 +1266,14 @@ static void environ_handler(union control *ctrl, dlgparam *dlg, } struct portfwd_data { - union control *addbutton, *rembutton, *listbox; - union control *sourcebox, *destbox, *direction; + dlgcontrol *addbutton, *rembutton, *listbox; + dlgcontrol *sourcebox, *destbox, *direction; #ifndef NO_IPV6 - union control *addressfamily; + dlgcontrol *addressfamily; #endif }; -static void portfwd_handler(union control *ctrl, dlgparam *dlg, +static void portfwd_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -1434,10 +1434,10 @@ static void portfwd_handler(union control *ctrl, dlgparam *dlg, } struct manual_hostkey_data { - union control *addbutton, *rembutton, *listbox, *keybox; + dlgcontrol *addbutton, *rembutton, *listbox, *keybox; }; -static void manual_hostkey_handler(union control *ctrl, dlgparam *dlg, +static void manual_hostkey_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -1500,7 +1500,7 @@ static void manual_hostkey_handler(union control *ctrl, dlgparam *dlg, } } -static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg, +static void clipboard_selector_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -1601,7 +1601,7 @@ static void clipboard_control(struct controlset *s, const char *label, #endif } -static void serial_parity_handler(union control *ctrl, dlgparam *dlg, +static void serial_parity_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { static const struct { @@ -1656,7 +1656,7 @@ static void serial_parity_handler(union control *ctrl, dlgparam *dlg, } } -static void serial_flow_handler(union control *ctrl, dlgparam *dlg, +static void serial_flow_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { static const struct { @@ -1709,7 +1709,7 @@ static void serial_flow_handler(union control *ctrl, dlgparam *dlg, } } -void proxy_type_handler(union control *ctrl, dlgparam *dlg, +void proxy_type_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; @@ -1763,7 +1763,7 @@ void proxy_type_handler(union control *ctrl, dlgparam *dlg, } } -static void host_ca_button_handler(union control *ctrl, dlgparam *dp, +static void host_ca_button_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { if (event == EVENT_ACTION) @@ -1782,7 +1782,7 @@ void setup_config_box(struct controlbox *b, bool midsession, struct environ_data *ed; struct portfwd_data *pfd; struct manual_hostkey_data *mh; - union control *c; + dlgcontrol *c; bool resize_forbidden = false; char *str; @@ -3318,11 +3318,11 @@ void setup_config_box(struct controlbox *b, bool midsession, } struct ca_state { - union control *ca_name_edit; - union control *ca_reclist; - union control *ca_pubkey_edit; - union control *ca_wclist; - union control *ca_wc_edit; + dlgcontrol *ca_name_edit; + dlgcontrol *ca_reclist; + dlgcontrol *ca_pubkey_edit; + dlgcontrol *ca_wclist; + dlgcontrol *ca_wc_edit; char *name, *pubkey, *wc; tree234 *ca_names; /* stores plain 'char *' */ tree234 *host_wcs; /* stores plain 'char *' */ @@ -3416,14 +3416,14 @@ static void ca_load_selected_record(struct ca_state *st, dlgparam *dp) dlg_refresh(st->ca_wclist, dp); } -static void ca_ok_handler(union control *ctrl, dlgparam *dp, +static void ca_ok_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { if (event == EVENT_ACTION) dlg_end(dp, 0); } -static void ca_name_handler(union control *ctrl, dlgparam *dp, +static void ca_name_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3444,7 +3444,7 @@ static void ca_name_handler(union control *ctrl, dlgparam *dp, } } -static void ca_reclist_handler(union control *ctrl, dlgparam *dp, +static void ca_reclist_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3461,7 +3461,7 @@ static void ca_reclist_handler(union control *ctrl, dlgparam *dp, } } -static void ca_load_handler(union control *ctrl, dlgparam *dp, +static void ca_load_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3470,7 +3470,7 @@ static void ca_load_handler(union control *ctrl, dlgparam *dp, } } -static void ca_save_handler(union control *ctrl, dlgparam *dp, +static void ca_save_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3496,7 +3496,7 @@ static void ca_save_handler(union control *ctrl, dlgparam *dp, } } -static void ca_delete_handler(union control *ctrl, dlgparam *dp, +static void ca_delete_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3523,7 +3523,7 @@ static void ca_delete_handler(union control *ctrl, dlgparam *dp, } } -static void ca_pubkey_handler(union control *ctrl, dlgparam *dp, +static void ca_pubkey_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3535,7 +3535,7 @@ static void ca_pubkey_handler(union control *ctrl, dlgparam *dp, } } -static void ca_wclist_handler(union control *ctrl, dlgparam *dp, +static void ca_wclist_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3549,7 +3549,7 @@ static void ca_wclist_handler(union control *ctrl, dlgparam *dp, } } -static void ca_wc_edit_handler(union control *ctrl, dlgparam *dp, +static void ca_wc_edit_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3561,7 +3561,7 @@ static void ca_wc_edit_handler(union control *ctrl, dlgparam *dp, } } -static void ca_wc_add_handler(union control *ctrl, dlgparam *dp, +static void ca_wc_add_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3582,7 +3582,7 @@ static void ca_wc_add_handler(union control *ctrl, dlgparam *dp, } } -static void ca_wc_rem_handler(union control *ctrl, dlgparam *dp, +static void ca_wc_rem_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; @@ -3608,7 +3608,7 @@ static void ca_wc_rem_handler(union control *ctrl, dlgparam *dp, void setup_ca_config_box(struct controlbox *b) { struct controlset *s; - union control *c; + dlgcontrol *c; /* Internal state for manipulating the host CA system */ struct ca_state *st = (struct ca_state *)ctrl_alloc_with_free( -- cgit v1.2.3 From 89883bf158a4615b13047869755a71abbf91e204 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 1 May 2022 09:55:52 +0100 Subject: Restructure dlgcontrol as a struct with an anon union. This gets rid of that awkward STANDARD_PREFIX system in which every branch of the old 'union control' had to repeat all the generic fields, and then call sites had to make an arbitrary decision about which branch to access them through. That was the best we could do before accepting C99 features in this code base. But now we have anonymous unions, so we don't need to put up with that nonsense any more! 'dlgcontrol' is now a struct rather than a union, and the generic fields common to all control types are ordinary members of the struct, so you don't have to refer to them as ctrl->generic.foo at all, just ctrl->foo, which saves verbiage at the point of use. The extra per-control fields are still held in structures named after the control type, so you'll still say ctrl->listbox.height or whatever. But now those structures are themselves members of an anonymous union field following the generic fields, so those sub-structures don't have to reiterate all the standard stuff too. While I'm here, I've promoted 'context2' from an editbox-specific field to a generic one (it just seems silly _not_ to allow any control to have two context fields if it needs it). Also, I had to rename the boolean field 'tabdelay' to avoid it clashing with the subsidiary structure field 'tabdelay', now that the former isn't generic.tabdelay any more. --- config.c | 180 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 90 insertions(+), 90 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index c4c932a9..062fe9b6 100644 --- a/config.c +++ b/config.c @@ -29,7 +29,7 @@ void conf_radiobutton_handler(dlgcontrol *ctrl, dlgparam *dlg, * is the one selected. */ if (event == EVENT_REFRESH) { - int val = conf_get_int(conf, ctrl->radio.context.i); + int val = conf_get_int(conf, ctrl->context.i); for (button = 0; button < ctrl->radio.nbuttons; button++) if (val == ctrl->radio.buttondata[button].i) break; @@ -39,7 +39,7 @@ void conf_radiobutton_handler(dlgcontrol *ctrl, dlgparam *dlg, } else if (event == EVENT_VALCHANGE) { button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); - conf_set_int(conf, ctrl->radio.context.i, + conf_set_int(conf, ctrl->context.i, ctrl->radio.buttondata[button].i); } } @@ -56,7 +56,7 @@ void conf_radiobutton_bool_handler(dlgcontrol *ctrl, dlgparam *dlg, * config option. */ if (event == EVENT_REFRESH) { - int val = conf_get_bool(conf, ctrl->radio.context.i); + int val = conf_get_bool(conf, ctrl->context.i); for (button = 0; button < ctrl->radio.nbuttons; button++) if (val == ctrl->radio.buttondata[button].i) break; @@ -66,7 +66,7 @@ void conf_radiobutton_bool_handler(dlgcontrol *ctrl, dlgparam *dlg, } else if (event == EVENT_VALCHANGE) { button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); - conf_set_bool(conf, ctrl->radio.context.i, + conf_set_bool(conf, ctrl->context.i, ctrl->radio.buttondata[button].i); } } @@ -83,7 +83,7 @@ void conf_checkbox_handler(dlgcontrol *ctrl, dlgparam *dlg, * For a standard checkbox, the context parameter gives the * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT. */ - key = ctrl->checkbox.context.i; + key = ctrl->context.i; if (key & CHECKBOX_INVERT) { key &= ~CHECKBOX_INVERT; invert = true; @@ -120,8 +120,8 @@ void conf_editbox_handler(dlgcontrol *ctrl, dlgparam *dlg, * context2 == -1000, then typing 1.2 into the box will set * the field to 1200.) */ - int key = ctrl->editbox.context.i; - int length = ctrl->editbox.context2.i; + int key = ctrl->context.i; + int length = ctrl->context2.i; Conf *conf = (Conf *)data; if (length > 0) { @@ -156,7 +156,7 @@ void conf_editbox_handler(dlgcontrol *ctrl, dlgparam *dlg, void conf_filesel_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { - int key = ctrl->fileselect.context.i; + int key = ctrl->context.i; Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { @@ -172,7 +172,7 @@ void conf_filesel_handler(dlgcontrol *ctrl, dlgparam *dlg, void conf_fontsel_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { - int key = ctrl->fontselect.context.i; + int key = ctrl->context.i; Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { @@ -274,7 +274,7 @@ static void config_protocols_handler(dlgcontrol *ctrl, dlgparam *dlg, { Conf *conf = (Conf *)data; int curproto = conf_get_int(conf, CONF_protocol); - struct hostport *hp = (struct hostport *)ctrl->generic.context.p; + struct hostport *hp = (struct hostport *)ctrl->context.p; if (event == EVENT_REFRESH) { /* @@ -716,7 +716,7 @@ static void sshbug_handler(dlgcontrol *ctrl, dlgparam *dlg, * spurious SELCHANGE we trigger in the process will overwrite * the value we wanted to keep. */ - int oldconf = conf_get_int(conf, ctrl->listbox.context.i); + int oldconf = conf_get_int(conf, ctrl->context.i); dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO); @@ -734,7 +734,7 @@ static void sshbug_handler(dlgcontrol *ctrl, dlgparam *dlg, i = AUTO; else i = dlg_listbox_getid(ctrl, dlg, i); - conf_set_int(conf, ctrl->listbox.context.i, i); + conf_set_int(conf, ctrl->context.i, i); } } @@ -749,7 +749,7 @@ static void sshbug_handler_manual_only(dlgcontrol *ctrl, dlgparam *dlg, */ Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { - int oldconf = conf_get_int(conf, ctrl->listbox.context.i); + int oldconf = conf_get_int(conf, ctrl->context.i); dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF); @@ -765,7 +765,7 @@ static void sshbug_handler_manual_only(dlgcontrol *ctrl, dlgparam *dlg, i = FORCE_OFF; else i = dlg_listbox_getid(ctrl, dlg, i); - conf_set_int(conf, ctrl->listbox.context.i, i); + conf_set_int(conf, ctrl->context.i, i); } } @@ -818,7 +818,7 @@ static void sessionsaver_handler(dlgcontrol *ctrl, dlgparam *dlg, { Conf *conf = (Conf *)data; struct sessionsaver_data *ssd = - (struct sessionsaver_data *)ctrl->generic.context.p; + (struct sessionsaver_data *)ctrl->context.p; if (event == EVENT_REFRESH) { if (ctrl == ssd->editbox) { @@ -959,7 +959,7 @@ static void charclass_handler(dlgcontrol *ctrl, dlgparam *dlg, { Conf *conf = (Conf *)data; struct charclass_data *ccd = - (struct charclass_data *)ctrl->generic.context.p; + (struct charclass_data *)ctrl->context.p; if (event == EVENT_REFRESH) { if (ctrl == ccd->listbox) { @@ -1008,7 +1008,7 @@ static void colour_handler(dlgcontrol *ctrl, dlgparam *dlg, { Conf *conf = (Conf *)data; struct colour_data *cd = - (struct colour_data *)ctrl->generic.context.p; + (struct colour_data *)ctrl->context.p; bool update = false, clear = false; int r, g, b; @@ -1117,7 +1117,7 @@ static void ttymodes_handler(dlgcontrol *ctrl, dlgparam *dlg, { Conf *conf = (Conf *)data; struct ttymodes_data *td = - (struct ttymodes_data *)ctrl->generic.context.p; + (struct ttymodes_data *)ctrl->context.p; if (event == EVENT_REFRESH) { if (ctrl == td->listbox) { @@ -1202,7 +1202,7 @@ static void environ_handler(dlgcontrol *ctrl, dlgparam *dlg, { Conf *conf = (Conf *)data; struct environ_data *ed = - (struct environ_data *)ctrl->generic.context.p; + (struct environ_data *)ctrl->context.p; if (event == EVENT_REFRESH) { if (ctrl == ed->listbox) { @@ -1278,7 +1278,7 @@ static void portfwd_handler(dlgcontrol *ctrl, dlgparam *dlg, { Conf *conf = (Conf *)data; struct portfwd_data *pfd = - (struct portfwd_data *)ctrl->generic.context.p; + (struct portfwd_data *)ctrl->context.p; if (event == EVENT_REFRESH) { if (ctrl == pfd->listbox) { @@ -1442,7 +1442,7 @@ static void manual_hostkey_handler(dlgcontrol *ctrl, dlgparam *dlg, { Conf *conf = (Conf *)data; struct manual_hostkey_data *mh = - (struct manual_hostkey_data *)ctrl->generic.context.p; + (struct manual_hostkey_data *)ctrl->context.p; if (event == EVENT_REFRESH) { if (ctrl == mh->listbox) { @@ -1504,9 +1504,9 @@ static void clipboard_selector_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; - int setting = ctrl->generic.context.i; + int setting = ctrl->context.i; #ifdef NAMED_CLIPBOARDS - int strsetting = ctrl->editbox.context2.i; + int strsetting = ctrl->context2.i; #endif static const struct { @@ -1614,7 +1614,7 @@ static void serial_parity_handler(dlgcontrol *ctrl, dlgparam *dlg, {"Mark", SER_PAR_MARK}, {"Space", SER_PAR_SPACE}, }; - int mask = ctrl->listbox.context.i; + int mask = ctrl->context.i; int i, j; Conf *conf = (Conf *)data; @@ -1668,7 +1668,7 @@ static void serial_flow_handler(dlgcontrol *ctrl, dlgparam *dlg, {"RTS/CTS", SER_FLOW_RTSCTS}, {"DSR/DTR", SER_FLOW_DSRDTR}, }; - int mask = ctrl->listbox.context.i; + int mask = ctrl->context.i; int i, j; Conf *conf = (Conf *)data; @@ -1743,7 +1743,7 @@ void proxy_type_handler(dlgcontrol *ctrl, dlgparam *dlg, ADD(PROXY_SSH_EXEC, "SSH to proxy and execute a command"); ADD(PROXY_SSH_SUBSYSTEM, "SSH to proxy and invoke a subsystem"); } - if (ctrl->generic.context.i & PROXY_UI_FLAG_LOCAL) { + if (ctrl->context.i & PROXY_UI_FLAG_LOCAL) { ADD(PROXY_CMD, "Local (run a subprogram to connect)"); } ADD(PROXY_TELNET, "'Telnet' (send an ad-hoc command)"); @@ -1805,11 +1805,11 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(no_help), sessionsaver_handler, P(ssd)); ssd->okbutton->button.isdefault = true; - ssd->okbutton->generic.column = 3; + ssd->okbutton->column = 3; ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help), sessionsaver_handler, P(ssd)); ssd->cancelbutton->button.iscancel = true; - ssd->cancelbutton->generic.column = 4; + ssd->cancelbutton->column = 4; /* We carefully don't close the 5-column part, so that platform- * specific add-ons can put extra buttons alongside Open and Cancel. */ @@ -1831,12 +1831,12 @@ void setup_config_box(struct controlbox *b, bool midsession, c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100, HELPCTX(session_hostname), config_host_handler, I(0), I(0)); - c->generic.column = 0; + c->column = 0; hp->host = c; c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100, HELPCTX(session_hostname), config_port_handler, I(0), I(0)); - c->generic.column = 1; + c->column = 1; hp->port = c; ctrl_columns(s, 1, 100); @@ -1845,7 +1845,7 @@ void setup_config_box(struct controlbox *b, bool midsession, c = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(session_hostname), config_protocols_handler, P(hp), NULL); - c->generic.column = 0; + c->column = 0; hp->protradio = c; c->radio.buttons = sresize(c->radio.buttons, PROTOCOL_LIMIT, char *); c->radio.shortcuts = sresize(c->radio.shortcuts, PROTOCOL_LIMIT, char); @@ -1880,10 +1880,10 @@ void setup_config_box(struct controlbox *b, bool midsession, config_protocols_handler, P(hp)); hp->protlist = c; /* droplist is populated in config_protocols_handler */ - c->generic.column = 1; + c->column = 1; /* Vertically centre the two protocol controls w.r.t. each other */ - hp->protlist->generic.align_next_to = hp->protradio; + hp->protlist->align_next_to = hp->protradio; ctrl_columns(s, 1, 100); } @@ -1899,7 +1899,7 @@ void setup_config_box(struct controlbox *b, bool midsession, ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100, HELPCTX(session_saved), sessionsaver_handler, P(ssd), P(NULL)); - ssd->editbox->generic.column = 0; + ssd->editbox->column = 0; /* Reset columns so that the buttons are alongside the list, rather * than alongside that edit box. */ ctrl_columns(s, 1, 100); @@ -1907,13 +1907,13 @@ void setup_config_box(struct controlbox *b, bool midsession, ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(session_saved), sessionsaver_handler, P(ssd)); - ssd->listbox->generic.column = 0; + ssd->listbox->column = 0; ssd->listbox->listbox.height = 7; if (!midsession) { ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l', HELPCTX(session_saved), sessionsaver_handler, P(ssd)); - ssd->loadbutton->generic.column = 1; + ssd->loadbutton->column = 1; } else { /* We can't offer the Load button mid-session, as it would allow the * user to load and subsequently save settings they can't see. (And @@ -1925,12 +1925,12 @@ void setup_config_box(struct controlbox *b, bool midsession, ssd->savebutton = ctrl_pushbutton(s, "Save", 'v', HELPCTX(session_saved), sessionsaver_handler, P(ssd)); - ssd->savebutton->generic.column = 1; + ssd->savebutton->column = 1; if (!midsession) { ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd', HELPCTX(session_saved), sessionsaver_handler, P(ssd)); - ssd->delbutton->generic.column = 1; + ssd->delbutton->column = 1; } else { /* Disable the Delete button mid-session too, for UI consistency. */ ssd->delbutton = NULL; @@ -2207,11 +2207,11 @@ void setup_config_box(struct controlbox *b, bool midsession, c = ctrl_editbox(s, "Columns", 'm', 100, HELPCTX(window_size), conf_editbox_handler, I(CONF_width), I(-1)); - c->generic.column = 0; + c->column = 0; c = ctrl_editbox(s, "Rows", 'r', 100, HELPCTX(window_size), conf_editbox_handler, I(CONF_height),I(-1)); - c->generic.column = 1; + c->column = 1; ctrl_columns(s, 1, 100); } @@ -2394,11 +2394,11 @@ void setup_config_box(struct controlbox *b, bool midsession, ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50, HELPCTX(copy_charclasses), charclass_handler, P(ccd), P(NULL)); - ccd->editbox->generic.column = 0; + ccd->editbox->column = 0; ccd->button = ctrl_pushbutton(s, "Set", 's', HELPCTX(copy_charclasses), charclass_handler, P(ccd)); - ccd->button->generic.column = 1; + ccd->button->column = 1; ctrl_columns(s, 1, 100); /* @@ -2435,22 +2435,22 @@ void setup_config_box(struct controlbox *b, bool midsession, cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data)); cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u', HELPCTX(colours_config), colour_handler, P(cd)); - cd->listbox->generic.column = 0; + cd->listbox->column = 0; cd->listbox->listbox.height = 7; c = ctrl_text(s, "RGB value:", HELPCTX(colours_config)); - c->generic.column = 1; + c->column = 1; cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config), colour_handler, P(cd), P(NULL)); - cd->redit->generic.column = 1; + cd->redit->column = 1; cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config), colour_handler, P(cd), P(NULL)); - cd->gedit->generic.column = 1; + cd->gedit->column = 1; cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config), colour_handler, P(cd), P(NULL)); - cd->bedit->generic.column = 1; + cd->bedit->column = 1; cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config), colour_handler, P(cd)); - cd->button->generic.column = 1; + cd->button->column = 1; ctrl_columns(s, 1, 100); /* @@ -2550,19 +2550,19 @@ void setup_config_box(struct controlbox *b, bool midsession, ed->varbox = ctrl_editbox(s, "Variable", 'v', 60, HELPCTX(telnet_environ), environ_handler, P(ed), P(NULL)); - ed->varbox->generic.column = 0; + ed->varbox->column = 0; ed->valbox = ctrl_editbox(s, "Value", 'l', 60, HELPCTX(telnet_environ), environ_handler, P(ed), P(NULL)); - ed->valbox->generic.column = 0; + ed->valbox->column = 0; ed->addbutton = ctrl_pushbutton(s, "Add", 'd', HELPCTX(telnet_environ), environ_handler, P(ed)); - ed->addbutton->generic.column = 1; + ed->addbutton->column = 1; ed->rembutton = ctrl_pushbutton(s, "Remove", 'r', HELPCTX(telnet_environ), environ_handler, P(ed)); - ed->rembutton->generic.column = 1; + ed->rembutton->column = 1; ctrl_columns(s, 1, 100); ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(telnet_environ), @@ -2591,13 +2591,13 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(proxy_main), conf_editbox_handler, I(CONF_proxy_host), I(1)); - c->generic.column = 0; + c->column = 0; c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(proxy_main), conf_editbox_handler, I(CONF_proxy_port), I(-1)); - c->generic.column = 1; + c->column = 1; ctrl_columns(s, 1, 100); ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100, HELPCTX(proxy_exclude), @@ -2803,7 +2803,7 @@ void setup_config_box(struct controlbox *b, bool midsession, 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; + c->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 *) @@ -2811,8 +2811,8 @@ void setup_config_box(struct controlbox *b, bool midsession, 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 = true; + mh->rembutton->column = 1; + mh->rembutton->delay_taborder = true; mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(ssh_kex_manual_hostkeys), manual_hostkey_handler, P(mh)); @@ -2826,11 +2826,11 @@ void setup_config_box(struct controlbox *b, bool midsession, 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->keybox->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; + mh->addbutton->column = 1; ctrl_columns(s, 1, 100); } @@ -3007,12 +3007,12 @@ void setup_config_box(struct controlbox *b, bool midsession, td->listbox->listbox.percentages[1] = 60; ctrl_columns(s, 2, 75, 25); c = ctrl_text(s, "For selected mode, send:", HELPCTX(ssh_ttymodes)); - c->generic.column = 0; + c->column = 0; td->setbutton = ctrl_pushbutton(s, "Set", 's', HELPCTX(ssh_ttymodes), ttymodes_handler, P(td)); - td->setbutton->generic.column = 1; - td->setbutton->generic.tabdelay = true; + td->setbutton->column = 1; + td->setbutton->delay_taborder = true; ctrl_columns(s, 1, 100); /* column break */ /* Bit of a hack to get the value radio buttons and * edit-box on the same row. */ @@ -3024,12 +3024,12 @@ void setup_config_box(struct controlbox *b, bool midsession, "Nothing", NO_SHORTCUT, P(NULL), "This:", NO_SHORTCUT, P(NULL), NULL); - td->valradio->generic.column = 0; + td->valradio->column = 0; td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100, HELPCTX(ssh_ttymodes), ttymodes_handler, P(td), P(NULL)); - td->valbox->generic.column = 1; - td->valbox->generic.align_next_to = td->valradio; + td->valbox->column = 1; + td->valbox->align_next_to = td->valradio; ctrl_tabdelay(s, td->setbutton); } @@ -3074,15 +3074,15 @@ void setup_config_box(struct controlbox *b, bool midsession, ctrl_columns(s, 3, 55, 20, 25); c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd)); - c->generic.column = COLUMN_FIELD(0,2); + c->column = COLUMN_FIELD(0,2); /* You want to select from the list, _then_ hit Remove. So tab order * should be that way round. */ pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data)); pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r', HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd)); - pfd->rembutton->generic.column = 2; - pfd->rembutton->generic.tabdelay = true; + pfd->rembutton->column = 2; + pfd->rembutton->delay_taborder = true; pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd)); @@ -3098,12 +3098,12 @@ void setup_config_box(struct controlbox *b, bool midsession, pfd->addbutton = ctrl_pushbutton(s, "Add", 'd', HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd)); - pfd->addbutton->generic.column = 2; - pfd->addbutton->generic.tabdelay = true; + pfd->addbutton->column = 2; + pfd->addbutton->delay_taborder = true; pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40, HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd), P(NULL)); - pfd->sourcebox->generic.column = 0; + pfd->sourcebox->column = 0; pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67, HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd), P(NULL)); @@ -3426,7 +3426,7 @@ static void ca_ok_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_name_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_REFRESH) { dlg_editbox_set(ctrl, dp, st->name); } else if (event == EVENT_VALCHANGE) { @@ -3447,7 +3447,7 @@ static void ca_name_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_reclist_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_REFRESH) { dlg_update_start(ctrl, dp); dlg_listbox_clear(ctrl, dp); @@ -3464,7 +3464,7 @@ static void ca_reclist_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_load_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_ACTION) { ca_load_selected_record(st, dp); } @@ -3473,7 +3473,7 @@ static void ca_load_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_save_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_ACTION) { host_ca *hca = snew(host_ca); memset(hca, 0, sizeof(*hca)); @@ -3499,7 +3499,7 @@ static void ca_save_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_delete_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_ACTION) { int i = dlg_listbox_index(st->ca_reclist, dp); if (i < 0) { @@ -3526,7 +3526,7 @@ static void ca_delete_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_pubkey_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_REFRESH) { dlg_editbox_set(ctrl, dp, st->pubkey); } else if (event == EVENT_VALCHANGE) { @@ -3538,7 +3538,7 @@ static void ca_pubkey_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_wclist_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_REFRESH) { dlg_update_start(ctrl, dp); dlg_listbox_clear(ctrl, dp); @@ -3552,7 +3552,7 @@ static void ca_wclist_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_wc_edit_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_REFRESH) { dlg_editbox_set(ctrl, dp, st->wc); } else if (event == EVENT_VALCHANGE) { @@ -3564,7 +3564,7 @@ static void ca_wc_edit_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_wc_add_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_ACTION) { if (!st->wc) { dlg_beep(dp); @@ -3585,7 +3585,7 @@ static void ca_wc_add_handler(dlgcontrol *ctrl, dlgparam *dp, static void ca_wc_rem_handler(dlgcontrol *ctrl, dlgparam *dp, void *data, int event) { - struct ca_state *st = (struct ca_state *)ctrl->generic.context.p; + struct ca_state *st = (struct ca_state *)ctrl->context.p; if (event == EVENT_ACTION) { int i = dlg_listbox_index(st->ca_wclist, dp); if (i < 0) { @@ -3626,7 +3626,7 @@ void setup_ca_config_box(struct controlbox *b) c = ctrl_pushbutton(s, "Done", 'o', HELPCTX(no_help), ca_ok_handler, P(st)); c->button.isdefault = true; - c->generic.column = 4; + c->column = 4; /* Load/save box, as similar as possible to the main saved sessions one */ s = ctrl_getset(b, "Main", "loadsave", @@ -3635,7 +3635,7 @@ void setup_ca_config_box(struct controlbox *b) c = ctrl_editbox(s, "Name for this CA (shown in log messages)", 'n', 100, HELPCTX(no_help), ca_name_handler, P(st), P(NULL)); - c->generic.column = 0; + c->column = 0; st->ca_name_edit = c; /* Reset columns so that the buttons are alongside the list, rather * than alongside that edit box. */ @@ -3643,18 +3643,18 @@ void setup_ca_config_box(struct controlbox *b) ctrl_columns(s, 2, 75, 25); c = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(no_help), ca_reclist_handler, P(st)); - c->generic.column = 0; + c->column = 0; c->listbox.height = 6; st->ca_reclist = c; c = ctrl_pushbutton(s, "Load", 'l', HELPCTX(no_help), ca_load_handler, P(st)); - c->generic.column = 1; + c->column = 1; c = ctrl_pushbutton(s, "Save", 'v', HELPCTX(no_help), ca_save_handler, P(st)); - c->generic.column = 1; + c->column = 1; c = ctrl_pushbutton(s, "Delete", 'd', HELPCTX(no_help), ca_delete_handler, P(st)); - c->generic.column = 1; + c->column = 1; /* Box containing the details of a specific CA record */ s = ctrl_getset(b, "Main", "details", "Details of a host CA record"); @@ -3668,12 +3668,12 @@ void setup_ca_config_box(struct controlbox *b) ctrl_columns(s, 3, 70, 15, 15); c = ctrl_editbox(s, "Hostname pattern to add", 'h', 100, HELPCTX(no_help), ca_wc_edit_handler, P(st), P(NULL)); - c->generic.column = 0; + c->column = 0; st->ca_wc_edit = c; c = ctrl_pushbutton(s, "Add", NO_SHORTCUT, HELPCTX(no_help), ca_wc_add_handler, P(st)); - c->generic.column = 1; + c->column = 1; c = ctrl_pushbutton(s, "Remove", NO_SHORTCUT, HELPCTX(no_help), ca_wc_rem_handler, P(st)); - c->generic.column = 2; + c->column = 2; } -- cgit v1.2.3 From 4fcb3bbe818b681edb49caa3264106238b889368 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 1 May 2022 09:16:46 +0100 Subject: Move host CA config box out into its own source file. In the course of polishing up this dialog box, I'm going to want it to actually do cryptographic things (such as checking validity of a public key blob and printing its fingerprint), which means it will need to link against SSH utility functions. So I've moved the dialog-box setup and handling code out of config.c into a new file in the ssh subdirectory and in the ssh library, where those facilities will be conveniently available. This also means that dialog-box setup code _won't_ be linked into PuTTYtel or pterm (on either platform), so I've added a stub source file to provide its entry-point function in those tools. Also, provided a const bool to indicate whether that dialog is available, which we use to decide whether to recognise that command-line option. --- config.c | 361 --------------------------------------------------------------- 1 file changed, 361 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 062fe9b6..94b434a7 100644 --- a/config.c +++ b/config.c @@ -3316,364 +3316,3 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_supdup_scroll)); } } - -struct ca_state { - dlgcontrol *ca_name_edit; - dlgcontrol *ca_reclist; - dlgcontrol *ca_pubkey_edit; - dlgcontrol *ca_wclist; - dlgcontrol *ca_wc_edit; - char *name, *pubkey, *wc; - tree234 *ca_names; /* stores plain 'char *' */ - tree234 *host_wcs; /* stores plain 'char *' */ -}; - -static int ca_name_compare(void *av, void *bv) -{ - return strcmp((const char *)av, (const char *)bv); -} - -static inline void clear_string_tree(tree234 *t) -{ - char *p; - while ((p = delpos234(t, 0)) != NULL) - sfree(p); -} - -static void ca_state_free(void *vctx) -{ - struct ca_state *st = (struct ca_state *)vctx; - clear_string_tree(st->ca_names); - freetree234(st->ca_names); - clear_string_tree(st->host_wcs); - freetree234(st->host_wcs); - sfree(st->name); - sfree(st->wc); - sfree(st); -} - -static void ca_refresh_name_list(struct ca_state *st) -{ - clear_string_tree(st->ca_names); - - host_ca_enum *hce = enum_host_ca_start(); - if (hce) { - strbuf *namebuf = strbuf_new(); - - while (strbuf_clear(namebuf), enum_host_ca_next(hce, namebuf)) { - char *name = dupstr(namebuf->s); - char *added = add234(st->ca_names, name); - /* Just imaginable that concurrent filesystem access might - * cause a repetition; avoid leaking memory if so */ - if (added != name) - sfree(name); - } - - strbuf_free(namebuf); - enum_host_ca_finish(hce); - } -} - -static void ca_load_selected_record(struct ca_state *st, dlgparam *dp) -{ - int i = dlg_listbox_index(st->ca_reclist, dp); - if (i < 0) { - dlg_beep(dp); - return; - } - const char *name = index234(st->ca_names, i); - if (!name) { /* in case the list box and the tree got out of sync */ - dlg_beep(dp); - return; - } - host_ca *hca = host_ca_load(name); - if (!hca) { - char *msg = dupprintf("Unable to load host CA record '%s'", name); - dlg_error_msg(dp, msg); - sfree(msg); - return; - } - - sfree(st->name); - st->name = dupstr(hca->name); - - sfree(st->pubkey); - st->pubkey = strbuf_to_str( - base64_encode_sb(ptrlen_from_strbuf(hca->ca_public_key), 0)); - - clear_string_tree(st->host_wcs); - for (size_t i = 0; i < hca->n_hostname_wildcards; i++) { - char *name = dupstr(hca->hostname_wildcards[i]); - char *added = add234(st->host_wcs, name); - if (added != name) - sfree(name); /* de-duplicate, just in case */ - } - - host_ca_free(hca); - - dlg_refresh(st->ca_name_edit, dp); - dlg_refresh(st->ca_pubkey_edit, dp); - dlg_refresh(st->ca_wclist, dp); -} - -static void ca_ok_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - if (event == EVENT_ACTION) - dlg_end(dp, 0); -} - -static void ca_name_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_REFRESH) { - dlg_editbox_set(ctrl, dp, st->name); - } else if (event == EVENT_VALCHANGE) { - sfree(st->name); - st->name = dlg_editbox_get(ctrl, dp); - - /* - * Try to auto-select the typed name in the list. - */ - int index; - if (!findrelpos234(st->ca_names, st->name, NULL, REL234_GE, &index)) - index = count234(st->ca_names) - 1; - if (index >= 0) - dlg_listbox_select(st->ca_reclist, dp, index); - } -} - -static void ca_reclist_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_REFRESH) { - dlg_update_start(ctrl, dp); - dlg_listbox_clear(ctrl, dp); - const char *name; - for (int i = 0; (name = index234(st->ca_names, i)) != NULL; i++) - dlg_listbox_add(ctrl, dp, name); - dlg_update_done(ctrl, dp); - } else if (event == EVENT_ACTION) { - /* Double-clicking a session loads it */ - ca_load_selected_record(st, dp); - } -} - -static void ca_load_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_ACTION) { - ca_load_selected_record(st, dp); - } -} - -static void ca_save_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_ACTION) { - host_ca *hca = snew(host_ca); - memset(hca, 0, sizeof(*hca)); - hca->name = dupstr(st->name); - hca->ca_public_key = base64_decode_sb(ptrlen_from_asciz(st->pubkey)); - hca->n_hostname_wildcards = count234(st->host_wcs); - hca->hostname_wildcards = snewn(hca->n_hostname_wildcards, char *); - for (size_t i = 0; i < hca->n_hostname_wildcards; i++) - hca->hostname_wildcards[i] = dupstr(index234(st->host_wcs, i)); - char *error = host_ca_save(hca); - host_ca_free(hca); - - if (error) { - dlg_error_msg(dp, error); - sfree(error); - } else { - ca_refresh_name_list(st); - dlg_refresh(st->ca_reclist, dp); - } - } -} - -static void ca_delete_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_ACTION) { - int i = dlg_listbox_index(st->ca_reclist, dp); - if (i < 0) { - dlg_beep(dp); - return; - } - const char *name = index234(st->ca_names, i); - if (!name) { /* in case the list box and the tree got out of sync */ - dlg_beep(dp); - return; - } - - char *error = host_ca_delete(name); - if (error) { - dlg_error_msg(dp, error); - sfree(error); - } else { - ca_refresh_name_list(st); - dlg_refresh(st->ca_reclist, dp); - } - } -} - -static void ca_pubkey_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_REFRESH) { - dlg_editbox_set(ctrl, dp, st->pubkey); - } else if (event == EVENT_VALCHANGE) { - sfree(st->pubkey); - st->pubkey = dlg_editbox_get(ctrl, dp); - } -} - -static void ca_wclist_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_REFRESH) { - dlg_update_start(ctrl, dp); - dlg_listbox_clear(ctrl, dp); - const char *name; - for (int i = 0; (name = index234(st->host_wcs, i)) != NULL; i++) - dlg_listbox_add(ctrl, dp, name); - dlg_update_done(ctrl, dp); - } -} - -static void ca_wc_edit_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_REFRESH) { - dlg_editbox_set(ctrl, dp, st->wc); - } else if (event == EVENT_VALCHANGE) { - sfree(st->wc); - st->wc = dlg_editbox_get(ctrl, dp); - } -} - -static void ca_wc_add_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_ACTION) { - if (!st->wc) { - dlg_beep(dp); - return; - } - - if (add234(st->host_wcs, st->wc) == st->wc) { - dlg_refresh(st->ca_wclist, dp); - } else { - sfree(st->wc); - } - - st->wc = dupstr(""); - dlg_refresh(st->ca_wc_edit, dp); - } -} - -static void ca_wc_rem_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_ACTION) { - int i = dlg_listbox_index(st->ca_wclist, dp); - if (i < 0) { - dlg_beep(dp); - return; - } - char *wc = delpos234(st->host_wcs, i); - if (!wc) { - dlg_beep(dp); - return; - } - - sfree(st->wc); - st->wc = wc; - dlg_refresh(st->ca_wclist, dp); - dlg_refresh(st->ca_wc_edit, dp); - } -} - -void setup_ca_config_box(struct controlbox *b) -{ - struct controlset *s; - dlgcontrol *c; - - /* Internal state for manipulating the host CA system */ - struct ca_state *st = (struct ca_state *)ctrl_alloc_with_free( - b, sizeof(struct ca_state), ca_state_free); - memset(st, 0, sizeof(*st)); - st->name = dupstr(""); - st->pubkey = dupstr(""); - st->ca_names = newtree234(ca_name_compare); - st->host_wcs = newtree234(ca_name_compare); - ca_refresh_name_list(st); - - /* Action area, with the Done button in it */ - s = ctrl_getset(b, "", "", ""); - ctrl_columns(s, 5, 20, 20, 20, 20, 20); - c = ctrl_pushbutton(s, "Done", 'o', HELPCTX(no_help), - ca_ok_handler, P(st)); - c->button.isdefault = true; - c->column = 4; - - /* Load/save box, as similar as possible to the main saved sessions one */ - s = ctrl_getset(b, "Main", "loadsave", - "Load, save or delete a host CA record"); - ctrl_columns(s, 2, 75, 25); - c = ctrl_editbox(s, "Name for this CA (shown in log messages)", - 'n', 100, HELPCTX(no_help), - ca_name_handler, P(st), P(NULL)); - c->column = 0; - st->ca_name_edit = c; - /* Reset columns so that the buttons are alongside the list, rather - * than alongside that edit box. */ - ctrl_columns(s, 1, 100); - ctrl_columns(s, 2, 75, 25); - c = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(no_help), - ca_reclist_handler, P(st)); - c->column = 0; - c->listbox.height = 6; - st->ca_reclist = c; - c = ctrl_pushbutton(s, "Load", 'l', HELPCTX(no_help), - ca_load_handler, P(st)); - c->column = 1; - c = ctrl_pushbutton(s, "Save", 'v', HELPCTX(no_help), - ca_save_handler, P(st)); - c->column = 1; - c = ctrl_pushbutton(s, "Delete", 'd', HELPCTX(no_help), - ca_delete_handler, P(st)); - c->column = 1; - - /* Box containing the details of a specific CA record */ - s = ctrl_getset(b, "Main", "details", "Details of a host CA record"); - c = ctrl_editbox(s, "Public key of certification authority", 'k', 100, - HELPCTX(no_help), ca_pubkey_handler, P(st), P(NULL)); - st->ca_pubkey_edit = c; - c = ctrl_listbox(s, "Hostname patterns this key is trusted to certify", - NO_SHORTCUT, HELPCTX(no_help), ca_wclist_handler, P(st)); - c->listbox.height = 3; - st->ca_wclist = c; - ctrl_columns(s, 3, 70, 15, 15); - c = ctrl_editbox(s, "Hostname pattern to add", 'h', 100, - HELPCTX(no_help), ca_wc_edit_handler, P(st), P(NULL)); - c->column = 0; - st->ca_wc_edit = c; - c = ctrl_pushbutton(s, "Add", NO_SHORTCUT, HELPCTX(no_help), - ca_wc_add_handler, P(st)); - c->column = 1; - c = ctrl_pushbutton(s, "Remove", NO_SHORTCUT, HELPCTX(no_help), - ca_wc_rem_handler, P(st)); - c->column = 2; -} -- cgit v1.2.3 From e0959d46471aeb94b3db4de662e124041f7d10f3 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Wed, 1 Jun 2022 11:14:21 +0100 Subject: Macro wrapper on ctrl_radiobuttons to fill in the NULL. ctrl_radiobuttons has a variadic argument list that it expects to be terminated by a null pointer where a const char * should be. So the terminating NULL at each call site ought to be cast to const char *, for the usual reason (NULL might expand literally to something not the same size as a pointer and get mis-marshalled in the variadic argument list). It wasn't being. Fixed in the same way as dupcat: I've turned ctrl_radiobuttons into a macro wrapper on the underlying function, which adds the NULL automatically with its correct cast. This also saves verbiage at every call site, because now I can leave out all the NULLs there. --- config.c | 80 ++++++++++++++++++++++++++++------------------------------------ 1 file changed, 35 insertions(+), 45 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 94b434a7..cca76b29 100644 --- a/config.c +++ b/config.c @@ -1844,7 +1844,7 @@ void setup_config_box(struct controlbox *b, bool midsession, ctrl_columns(s, 2, 62, 38); c = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(session_hostname), - config_protocols_handler, P(hp), NULL); + config_protocols_handler, P(hp)); c->column = 0; hp->protradio = c; c->radio.buttons = sresize(c->radio.buttons, PROTOCOL_LIMIT, char *); @@ -1944,7 +1944,7 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_close_on_exit), "Always", I(FORCE_ON), "Never", I(FORCE_OFF), - "Only on clean exit", I(AUTO), NULL); + "Only on clean exit", I(AUTO)); /* * The Session/Logging panel. @@ -1974,8 +1974,7 @@ void setup_config_box(struct controlbox *b, bool midsession, "Printable output", 'p', I(LGTYP_ASCII), "All session output", 'l', I(LGTYP_DEBUG), sshlogname, 's', I(LGTYP_PACKETS), - sshrawlogname, 'r', I(LGTYP_SSHRAW), - NULL); + sshrawlogname, 'r', I(LGTYP_SSHRAW)); } ctrl_filesel(s, "Log file name:", 'f', NULL, true, "Select session log file name", @@ -1989,7 +1988,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_handler, I(CONF_logxfovr), "Always overwrite it", I(LGXF_OVR), "Always append to the end of it", I(LGXF_APN), - "Ask the user every time", I(LGXF_ASK), NULL); + "Ask the user every time", I(LGXF_ASK)); ctrl_checkbox(s, "Flush log file frequently", 'u', HELPCTX(logging_flush), conf_checkbox_handler, I(CONF_logflush)); @@ -2043,13 +2042,13 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_handler,I(CONF_localecho), "Auto", I(AUTO), "Force on", I(FORCE_ON), - "Force off", I(FORCE_OFF), NULL); + "Force off", I(FORCE_OFF)); ctrl_radiobuttons(s, "Local line editing:", 't', 3, HELPCTX(terminal_localedit), conf_radiobutton_handler,I(CONF_localedit), "Auto", I(AUTO), "Force on", I(FORCE_ON), - "Force off", I(FORCE_OFF), NULL); + "Force off", I(FORCE_OFF)); s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing"); ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100, @@ -2068,12 +2067,12 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(keyboard_backspace), conf_radiobutton_bool_handler, I(CONF_bksp_is_delete), - "Control-H", I(0), "Control-? (127)", I(1), NULL); + "Control-H", I(0), "Control-? (127)", I(1)); ctrl_radiobuttons(s, "The Home and End keys", 'e', 2, HELPCTX(keyboard_homeend), conf_radiobutton_bool_handler, I(CONF_rxvt_homeend), - "Standard", I(false), "rxvt", I(true), NULL); + "Standard", I(false), "rxvt", I(true)); ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 4, HELPCTX(keyboard_funkeys), conf_radiobutton_handler, @@ -2084,14 +2083,13 @@ void setup_config_box(struct controlbox *b, bool midsession, "VT400", I(FUNKY_VT400), "VT100+", I(FUNKY_VT100P), "SCO", I(FUNKY_SCO), - "Xterm 216+", I(FUNKY_XTERM_216), - NULL); + "Xterm 216+", I(FUNKY_XTERM_216)); ctrl_radiobuttons(s, "Shift/Ctrl/Alt with the arrow keys", 'w', 2, HELPCTX(keyboard_sharrow), conf_radiobutton_handler, I(CONF_sharrow_type), "Ctrl toggles app mode", I(SHARROW_APPLICATION), - "xterm-style bitmap", I(SHARROW_BITMAP), NULL); + "xterm-style bitmap", I(SHARROW_BITMAP)); s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad", "Application keypad settings:"); @@ -2099,12 +2097,11 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(keyboard_appcursor), conf_radiobutton_bool_handler, I(CONF_app_cursor), - "Normal", I(0), "Application", I(1), NULL); + "Normal", I(0), "Application", I(1)); ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3, HELPCTX(keyboard_appkeypad), numeric_keypad_handler, P(NULL), - "Normal", I(0), "Application", I(1), "NetHack", I(2), - NULL); + "Normal", I(0), "Application", I(1), "NetHack", I(2)); /* * The Terminal/Bell panel. @@ -2118,7 +2115,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_handler, I(CONF_beep), "None (bell disabled)", I(BELL_DISABLED), "Make default system alert sound", I(BELL_DEFAULT), - "Visual bell (flash window)", I(BELL_VISUAL), NULL); + "Visual bell (flash window)", I(BELL_VISUAL)); s = ctrl_getset(b, "Terminal/Bell", "overload", "Control the bell overload behaviour"); @@ -2172,7 +2169,7 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_remote_qtitle_action), "None", I(TITLE_NONE), "Empty string", I(TITLE_EMPTY), - "Window title", I(TITLE_REAL), NULL); + "Window title", I(TITLE_REAL)); ctrl_checkbox(s, "Disable remote-controlled clearing of scrollback", 'e', HELPCTX(features_clearscroll), conf_checkbox_handler, @@ -2249,7 +2246,7 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_cursor_type), "Block", 'l', I(0), "Underline", 'u', I(1), - "Vertical line", 'v', I(2), NULL); + "Vertical line", 'v', I(2)); ctrl_checkbox(s, "Cursor blinks", 'b', HELPCTX(appearance_cursor), conf_checkbox_handler, I(CONF_blink_cur)); @@ -2315,13 +2312,12 @@ void setup_config_box(struct controlbox *b, bool midsession, str = dupprintf("Adjust how %s handles line drawing characters", appname); s = ctrl_getset(b, "Window/Translation", "linedraw", str); sfree(str); - ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1, - HELPCTX(translation_linedraw), - conf_radiobutton_handler, - I(CONF_vtmode), - "Use Unicode line drawing code points",'u',I(VT_UNICODE), - "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN), - NULL); + ctrl_radiobuttons( + s, "Handling of line drawing characters:", NO_SHORTCUT,1, + HELPCTX(translation_linedraw), + conf_radiobutton_handler, I(CONF_vtmode), + "Use Unicode line drawing code points",'u',I(VT_UNICODE), + "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN)); ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d', HELPCTX(selection_linedraw), conf_checkbox_handler, I(CONF_rawcnp)); @@ -2346,7 +2342,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_bool_handler, I(CONF_rect_select), "Normal", 'n', I(false), - "Rectangular block", 'r', I(true), NULL); + "Rectangular block", 'r', I(true)); s = ctrl_getset(b, "Window/Selection", "clipboards", "Assign copy/paste actions to clipboards"); @@ -2422,8 +2418,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_handler, I(CONF_bold_style), "The font", I(1), "The colour", I(2), - "Both", I(3), - NULL); + "Both", I(3)); str = dupprintf("Adjust the precise colours %s displays", appname); s = ctrl_getset(b, "Window/Colours", "adjust", str); @@ -2488,8 +2483,7 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_addressfamily), "Auto", 'u', I(ADDRTYPE_UNSPEC), "IPv4", '4', I(ADDRTYPE_IPV4), - "IPv6", '6', I(ADDRTYPE_IPV6), - NULL); + "IPv6", '6', I(ADDRTYPE_IPV6)); #endif { @@ -2528,8 +2522,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_bool_handler, I(CONF_username_from_env), "Prompt", I(false), - userlabel, I(true), - NULL); + userlabel, I(true)); sfree(userlabel); } @@ -2613,7 +2606,7 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_proxy_dns), "No", I(FORCE_OFF), "Auto", I(AUTO), - "Yes", I(FORCE_ON), NULL); + "Yes", I(FORCE_ON)); ctrl_editbox(s, "Username", 'u', 60, HELPCTX(proxy_auth), conf_editbox_handler, @@ -2635,7 +2628,7 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_proxy_log_to_term), "No", I(FORCE_OFF), "Yes", I(FORCE_ON), - "Only until session starts", I(AUTO), NULL); + "Only until session starts", I(AUTO)); } /* @@ -2722,7 +2715,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_handler, I(CONF_sshprot), "2", '2', I(3), - "1 (INSECURE)", '1', I(0), NULL); + "1 (INSECURE)", '1', I(0)); } /* @@ -3022,8 +3015,7 @@ void setup_config_box(struct controlbox *b, bool midsession, ttymodes_handler, P(td), "Auto", NO_SHORTCUT, P(NULL), "Nothing", NO_SHORTCUT, P(NULL), - "This:", NO_SHORTCUT, P(NULL), - NULL); + "This:", NO_SHORTCUT, P(NULL)); td->valradio->column = 0; td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100, HELPCTX(ssh_ttymodes), @@ -3052,7 +3044,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_handler, I(CONF_x11_auth), "MIT-Magic-Cookie-1", I(X11_MIT), - "XDM-Authorization-1", I(X11_XDM), NULL); + "XDM-Authorization-1", I(X11_XDM)); } /* @@ -3112,8 +3104,7 @@ void setup_config_box(struct controlbox *b, bool midsession, portfwd_handler, P(pfd), "Local", 'l', P(NULL), "Remote", 'm', P(NULL), - "Dynamic", 'y', P(NULL), - NULL); + "Dynamic", 'y', P(NULL)); #ifndef NO_IPV6 pfd->addressfamily = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, @@ -3121,8 +3112,7 @@ void setup_config_box(struct controlbox *b, bool midsession, portfwd_handler, P(pfd), "Auto", 'u', I(ADDRTYPE_UNSPEC), "IPv4", '4', I(ADDRTYPE_IPV4), - "IPv6", '6', I(ADDRTYPE_IPV6), - NULL); + "IPv6", '6', I(ADDRTYPE_IPV6)); #endif ctrl_tabdelay(s, pfd->addbutton); ctrl_columns(s, 1, 100); @@ -3251,12 +3241,12 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_radiobutton_bool_handler, I(CONF_rfc_environ), "BSD (commonplace)", 'b', I(false), - "RFC 1408 (unusual)", 'f', I(true), NULL); + "RFC 1408 (unusual)", 'f', I(true)); ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2, HELPCTX(telnet_passive), conf_radiobutton_bool_handler, I(CONF_passive_telnet), - "Passive", I(true), "Active", I(false), NULL); + "Passive", I(true), "Active", I(false)); } ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k', HELPCTX(telnet_specialkeys), @@ -3303,7 +3293,7 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_supdup_ascii_set), "None", I(SUPDUP_CHARSET_ASCII), "ITS", I(SUPDUP_CHARSET_ITS), - "WAITS", I(SUPDUP_CHARSET_WAITS), NULL); + "WAITS", I(SUPDUP_CHARSET_WAITS)); ctrl_checkbox(s, "**MORE** processing", 'm', HELPCTX(supdup_more), -- cgit v1.2.3 From d8f8c8972aecd96d9b2ec552f21421087dd7828c Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 5 Jul 2022 18:11:54 +0100 Subject: Make HelpCtx a per-platform type, not an intorptr. Partly, this just seems more sensible, since it may well vary per platform beyond the ability of intorptr to specify. But more immediately it means the definition of the HELPCTX macro doesn't have to use the P() function from dialog.h, which isn't defined in any circumstances outside the config subsystem. And I'm about to want to put a help context well outside that subsystem. --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config.c') diff --git a/config.c b/config.c index cca76b29..a2537dbb 100644 --- a/config.c +++ b/config.c @@ -1588,7 +1588,7 @@ static void clipboard_selector_handler(dlgcontrol *ctrl, dlgparam *dlg, } static void clipboard_control(struct controlset *s, const char *label, - char shortcut, int percentage, intorptr helpctx, + char shortcut, int percentage, HelpCtx helpctx, int setting, int strsetting) { #ifdef NAMED_CLIPBOARDS -- cgit v1.2.3 From 4fa3480444a64a5be3055cf27d96d8e68d0a1d12 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Wed, 3 Aug 2022 20:48:46 +0100 Subject: Formatting: realign run-on parenthesised stuff. My bulk indentation check also turned up a lot of cases where a run-on function call or if statement didn't have its later lines aligned correctly relative to the open paren. I think this is quite easy to do by getting things out of sync (editing the first line of the function call and forgetting to update the rest, perhaps even because you never _saw_ the rest during a search-replace). But a few didn't quite fit into that pattern, in particular an outright misleading case in unix/askpass.c where the second line of a call was aligned neatly below the _wrong_ one of the open parens on the opening line. Restored as many alignments as I could easily find. --- config.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index a2537dbb..f77384d5 100644 --- a/config.c +++ b/config.c @@ -602,7 +602,7 @@ static void kexlist_handler(dlgcontrol *ctrl, dlgparam *dlg, } static void hklist_handler(dlgcontrol *ctrl, dlgparam *dlg, - void *data, int event) + void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { @@ -1990,11 +1990,11 @@ void setup_config_box(struct controlbox *b, bool midsession, "Always append to the end of it", I(LGXF_APN), "Ask the user every time", I(LGXF_ASK)); ctrl_checkbox(s, "Flush log file frequently", 'u', - HELPCTX(logging_flush), - conf_checkbox_handler, I(CONF_logflush)); + HELPCTX(logging_flush), + conf_checkbox_handler, I(CONF_logflush)); ctrl_checkbox(s, "Include header", 'i', - HELPCTX(logging_header), - conf_checkbox_handler, I(CONF_logheader)); + HELPCTX(logging_header), + conf_checkbox_handler, I(CONF_logheader)); if ((midsession && protocol == PROT_SSH) || (!midsession && backend_vt_from_proto(PROT_SSH))) { @@ -2476,14 +2476,14 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_tcp_keepalives)); #ifndef NO_IPV6 s = ctrl_getset(b, "Connection", "ipversion", - "Internet protocol version"); + "Internet protocol version"); ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, - HELPCTX(connection_ipversion), - conf_radiobutton_handler, - I(CONF_addressfamily), - "Auto", 'u', I(ADDRTYPE_UNSPEC), - "IPv4", '4', I(ADDRTYPE_IPV4), - "IPv6", '6', I(ADDRTYPE_IPV6)); + HELPCTX(connection_ipversion), + conf_radiobutton_handler, + I(CONF_addressfamily), + "Auto", 'u', I(ADDRTYPE_UNSPEC), + "IPv4", '4', I(ADDRTYPE_IPV4), + "IPv6", '6', I(ADDRTYPE_IPV6)); #endif { -- cgit v1.2.3 From e52087719c4e185e433f7b5b1fb8383b4d910c2e Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 7 Aug 2022 12:06:36 +0100 Subject: Documentation for OpenSSH certificates. Also I've filled in the help contexts in all the new GUI controls. --- config.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index f77384d5..59b6976a 100644 --- a/config.c +++ b/config.c @@ -2835,7 +2835,8 @@ void setup_config_box(struct controlbox *b, bool midsession, s = ctrl_getset(b, "Connection/SSH/Host keys", "ca", "Configure trusted certification authorities"); c = ctrl_pushbutton(s, "Configure host CAs", NO_SHORTCUT, - HELPCTX(no_help), host_ca_button_handler, I(0)); + HELPCTX(ssh_kex_cert), + host_ca_button_handler, I(0)); if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) { /* @@ -2909,7 +2910,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_filesel_handler, I(CONF_keyfile)); ctrl_filesel(s, "Certificate to use with the private key:", 'e', NULL, false, "Select certificate file", - HELPCTX(ssh_auth_privkey), + HELPCTX(ssh_auth_cert), conf_filesel_handler, I(CONF_detached_cert)); #ifndef NO_GSSAPI -- cgit v1.2.3 From c1a2114b28125572cf54c393bd51a6a39c4f00bd Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 16 Aug 2022 18:36:58 +0100 Subject: Implement AES-GCM using the @openssh.com protocol IDs. I only recently found out that OpenSSH defined their own protocol IDs for AES-GCM, defined to work the same as the standard ones except that they fixed the semantics for how you select the linked cipher+MAC pair during key exchange. (RFC 5647 defines protocol ids for AES-GCM in both the cipher and MAC namespaces, and requires that you MUST select both or neither - but this contradicts the selection policy set out in the base SSH RFCs, and there's no discussion of how you resolve a conflict between them! OpenSSH's answer is to do it the same way ChaCha20-Poly1305 works, because that will ensure the two suites don't fight.) People do occasionally ask us for this linked cipher/MAC pair, and now I know it's actually feasible, I've implemented it, including a pair of vector implementations for x86 and Arm using their respective architecture extensions for multiplying polynomials over GF(2). Unlike ChaCha20-Poly1305, I've kept the cipher and MAC implementations in separate objects, with an arm's-length link between them that the MAC uses when it needs to encrypt single cipher blocks to use as the inputs to the MAC algorithm. That enables the cipher and the MAC to be independently selected from their hardware-accelerated versions, just in case someone runs on a system that has polynomial multiplication instructions but not AES acceleration, or vice versa. There's a fourth implementation of the GCM MAC, which is a pure software implementation of the same algorithm used in the vectorised versions. It's too slow to use live, but I've kept it in the code for future testing needs, and because it's a convenient place to dump my design comments. The vectorised implementations are fairly crude as far as optimisation goes. I'm sure serious x86 _or_ Arm optimisation engineers would look at them and laugh. But GCM is a fast MAC compared to HMAC-SHA-256 (indeed compared to HMAC-anything-at-all), so it should at least be good enough to use. And we've got a working version with some tests now, so if someone else wants to improve them, they can. --- config.c | 1 + 1 file changed, 1 insertion(+) (limited to 'config.c') diff --git a/config.c b/config.c index 59b6976a..747af814 100644 --- a/config.c +++ b/config.c @@ -491,6 +491,7 @@ static void cipherlist_handler(dlgcontrol *ctrl, dlgparam *dlg, static const struct { const char *s; int c; } ciphers[] = { { "ChaCha20 (SSH-2 only)", CIPHER_CHACHA20 }, + { "AES-GCM (SSH-2 only)", CIPHER_AESGCM }, { "3DES", CIPHER_3DES }, { "Blowfish", CIPHER_BLOWFISH }, { "DES", CIPHER_DES }, -- cgit v1.2.3 From 031d86ed5ba4dd4f7b61af483a20f48f7811f2ab Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 29 Aug 2022 07:44:39 +0100 Subject: Add RFC8268 / RFC3126 Diffie-Hellman group{15,16,17,18}. These are a new set of larger integer Diffie-Hellman fixed groups, using SHA-512 as the hash. --- config.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 747af814..03043912 100644 --- a/config.c +++ b/config.c @@ -564,14 +564,18 @@ static void kexlist_handler(dlgcontrol *ctrl, dlgparam *dlg, int i; 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 }, + { "Diffie-Hellman group 1 (1024-bit)", KEX_DHGROUP1 }, + { "Diffie-Hellman group 14 (2048-bit)", KEX_DHGROUP14 }, + { "Diffie-Hellman group 15 (3072-bit)", KEX_DHGROUP15 }, + { "Diffie-Hellman group 16 (4096-bit)", KEX_DHGROUP16 }, + { "Diffie-Hellman group 17 (6144-bit)", KEX_DHGROUP17 }, + { "Diffie-Hellman group 18 (8192-bit)", KEX_DHGROUP18 }, + { "Diffie-Hellman group exchange", KEX_DHGEX }, + { "RSA-based key exchange", KEX_RSA }, + { "ECDH key exchange", KEX_ECDH }, { "NTRU Prime / Curve25519 hybrid kex" - " (quantum-resistant)", KEX_NTRU_HYBRID }, - { "-- warn below here --", KEX_WARN } + " (quantum-resistant)", KEX_NTRU_HYBRID }, + { "-- warn below here --", KEX_WARN } }; /* Set up the "kex preference" box. */ -- cgit v1.2.3 From 5e2acd9af7033b681023e026d4607e554f7ab984 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 30 Aug 2022 18:51:33 +0100 Subject: New bug workaround: KEXINIT filtering. We've occasionally had reports of SSH servers disconnecting as soon as they receive PuTTY's KEXINIT. I think all such reports have involved the kind of simple ROM-based SSH server software you find in small embedded devices. I've never been able to prove it, but I've always suspected that one possible cause of this is simply that PuTTY's KEXINIT is _too long_, either in number of algorithms listed or in total length (especially given all the ones that end in @very.long.domain.name suffixes). If I'm right about either of those being the cause, then it's just become even more likely to happen, because of all the extra Diffie-Hellman groups and GSSAPI algorithms we just threw into our already-long list in the previous few commits. A workaround I've had in mind for ages is to wait for the server's KEXINIT, and then filter our own down to just the algorithms the server also mentioned. Then our KEXINIT is no longer than that of the server, and hence, presumably fits in whatever buffer it has. So I've implemented that workaround, in anticipation of it being needed in the near future. (Well ... it's not _quite_ true that our KEXINIT is at most the same length as the server. In fact I had to leave in one KEXINIT item that won't match anything in the server's list, namely "ext-info-c" which gates access to SHA-2 based RSA. So if we turn out to support absolutely everything on all the server's lists, then our KEXINIT would be a few bytes longer than the server's, even with this workaround. But that would only cause trouble if the server's outgoing KEXINIT was skating very close to whatever buffer size it has for the incoming one, and I'm guessing that's not very likely.) ((Another possible cause of this kind of disconnection would be a server that simply objects to seeing any KEXINIT string it doesn't know how to speak. But _surely_ no such server would have survived initial testing against any full-featured client at all!)) --- config.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'config.c') diff --git a/config.c b/config.c index 03043912..12c6767c 100644 --- a/config.c +++ b/config.c @@ -3154,6 +3154,10 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(ssh_bugs_dropstart), sshbug_handler_manual_only, I(CONF_sshbug_dropstart)); + ctrl_droplist(s, "Chokes on PuTTY's full KEXINIT", 'p', 20, + HELPCTX(ssh_bugs_filter_kexinit), + sshbug_handler_manual_only, + I(CONF_sshbug_filter_kexinit)); ctrl_settitle(b, "Connection/SSH/More bugs", "Further workarounds for SSH server bugs"); -- cgit v1.2.3 From 761df2fca63ad4ac2badec4e0d359ae18a94608d Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Wed, 24 Aug 2022 07:56:45 +0100 Subject: Replace integer context2 encoding in conf_editbox_handler. I was just about to add another ordinary edit box control, and found I couldn't remember what went in the context2 argument to conf_editbox. When I looked it up, I realised it was one of those horrid integer encodings of the form '1 means this, -1 means that, less than -1 means some parametrised property where the parameter is obtained by negating the encoded integer'. Those are always awkward to remember, and worse to extend. So I've replaced the integer context2 used with conf_editbox_handler with a pointer to a small struct type in which the types and parameters have sensible names and are documented. (To avoid annoying const warnings everywhere, this also meant extending the 'intorptr' union to have a const void * branch as well as a 'void *'. Surprised I haven't needed that before. But if I introduce any more of these parameter structures, it'll come in useful again.) --- config.c | 103 ++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 53 insertions(+), 50 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 12c6767c..c8e72d13 100644 --- a/config.c +++ b/config.c @@ -104,27 +104,23 @@ void conf_checkbox_handler(dlgcontrol *ctrl, dlgparam *dlg, } } +const struct conf_editbox_handler_type conf_editbox_str = {.type = EDIT_STR}; +const struct conf_editbox_handler_type conf_editbox_int = {.type = EDIT_INT}; + void conf_editbox_handler(dlgcontrol *ctrl, dlgparam *dlg, void *data, int event) { /* - * The standard edit-box handler expects the main `context' - * field to contain the primary key. The secondary `context2' - * field indicates the type of this field: - * - * - if context2 > 0, the field is a string. - * - if context2 == -1, the field is an int and the edit box - * is numeric. - * - if context2 < -1, the field is an int and the edit box is - * _floating_, and (-context2) gives the scale. (E.g. if - * context2 == -1000, then typing 1.2 into the box will set - * the field to 1200.) + * The standard edit-box handler expects the main `context' field + * to contain the primary key. The secondary `context2' field is a + * pointer to the struct conf_editbox_handler_type defined in + * putty.h. */ int key = ctrl->context.i; - int length = ctrl->context2.i; + const struct conf_editbox_handler_type *type = ctrl->context2.cp; Conf *conf = (Conf *)data; - if (length > 0) { + if (type->type == EDIT_STR) { if (event == EVENT_REFRESH) { char *field = conf_get_str(conf, key); dlg_editbox_set(ctrl, dlg, field); @@ -133,21 +129,21 @@ void conf_editbox_handler(dlgcontrol *ctrl, dlgparam *dlg, conf_set_str(conf, key, field); sfree(field); } - } else if (length < 0) { + } else { if (event == EVENT_REFRESH) { char str[80]; int value = conf_get_int(conf, key); - if (length == -1) + if (type->type == EDIT_INT) sprintf(str, "%d", value); else - sprintf(str, "%g", (double)value / (double)(-length)); + sprintf(str, "%g", (double)value / type->denominator); dlg_editbox_set(ctrl, dlg, str); } else if (event == EVENT_VALCHANGE) { char *str = dlg_editbox_get(ctrl, dlg); - if (length == -1) + if (type->type == EDIT_INT) conf_set_int(conf, key, atoi(str)); else - conf_set_int(conf, key, (int)((-length) * atof(str))); + conf_set_int(conf, key, (int)(type->denominator * atof(str))); sfree(str); } } @@ -2039,7 +2035,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_checkbox_handler, I(CONF_blinktext)); ctrl_editbox(s, "Answerback to ^E:", 's', 100, HELPCTX(terminal_answerback), - conf_editbox_handler, I(CONF_answerback), I(1)); + conf_editbox_handler, I(CONF_answerback), ED_STR); s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options"); ctrl_radiobuttons(s, "Local echo:", 'l', 3, @@ -2129,17 +2125,21 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_checkbox_handler, I(CONF_bellovl)); ctrl_editbox(s, "Over-use means this many bells...", 'm', 20, HELPCTX(bell_overload), - conf_editbox_handler, I(CONF_bellovl_n), I(-1)); + conf_editbox_handler, I(CONF_bellovl_n), ED_INT); + + static const struct conf_editbox_handler_type conf_editbox_tickspersec = { + .type = EDIT_FIXEDPOINT, .denominator = TICKSPERSEC}; + ctrl_editbox(s, "... in this many seconds", 't', 20, HELPCTX(bell_overload), conf_editbox_handler, I(CONF_bellovl_t), - I(-TICKSPERSEC)); + CP(&conf_editbox_tickspersec)); ctrl_text(s, "The bell is re-enabled after a few seconds of silence.", HELPCTX(bell_overload)); ctrl_editbox(s, "Seconds of silence required", 's', 20, HELPCTX(bell_overload), conf_editbox_handler, I(CONF_bellovl_s), - I(-TICKSPERSEC)); + CP(&conf_editbox_tickspersec)); /* * The Terminal/Features panel. @@ -2208,11 +2208,11 @@ void setup_config_box(struct controlbox *b, bool midsession, ctrl_columns(s, 2, 50, 50); c = ctrl_editbox(s, "Columns", 'm', 100, HELPCTX(window_size), - conf_editbox_handler, I(CONF_width), I(-1)); + conf_editbox_handler, I(CONF_width), ED_INT); c->column = 0; c = ctrl_editbox(s, "Rows", 'r', 100, HELPCTX(window_size), - conf_editbox_handler, I(CONF_height),I(-1)); + conf_editbox_handler, I(CONF_height),ED_INT); c->column = 1; ctrl_columns(s, 1, 100); } @@ -2221,7 +2221,7 @@ void setup_config_box(struct controlbox *b, bool midsession, "Control the scrollback in the window"); ctrl_editbox(s, "Lines of scrollback", 's', 50, HELPCTX(window_scrollback), - conf_editbox_handler, I(CONF_savelines), I(-1)); + conf_editbox_handler, I(CONF_savelines), ED_INT); ctrl_checkbox(s, "Display scrollbar", 'd', HELPCTX(window_scrollback), conf_checkbox_handler, I(CONF_scrollbar)); @@ -2273,7 +2273,7 @@ void setup_config_box(struct controlbox *b, bool midsession, ctrl_editbox(s, "Gap between text and window edge:", 'e', 20, HELPCTX(appearance_border), conf_editbox_handler, - I(CONF_window_border), I(-1)); + I(CONF_window_border), ED_INT); /* * The Window/Behaviour panel. @@ -2286,7 +2286,7 @@ void setup_config_box(struct controlbox *b, bool midsession, "Adjust the behaviour of the window title"); ctrl_editbox(s, "Window title:", 't', 100, HELPCTX(appearance_title), - conf_editbox_handler, I(CONF_wintitle), I(1)); + conf_editbox_handler, I(CONF_wintitle), ED_STR); ctrl_checkbox(s, "Separate window and icon titles", 'i', HELPCTX(appearance_title), conf_checkbox_handler, @@ -2465,8 +2465,7 @@ void setup_config_box(struct controlbox *b, bool midsession, "Sending of null packets to keep session active"); ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20, HELPCTX(connection_keepalive), - conf_editbox_handler, I(CONF_ping_interval), - I(-1)); + conf_editbox_handler, I(CONF_ping_interval), ED_INT); if (!midsession) { s = ctrl_getset(b, "Connection", "tcp", @@ -2499,7 +2498,7 @@ void setup_config_box(struct controlbox *b, bool midsession, "Logical name of remote host"); ctrl_editbox(s, label, 'm', 100, HELPCTX(connection_loghost), - conf_editbox_handler, I(CONF_loghost), I(1)); + conf_editbox_handler, I(CONF_loghost), ED_STR); } } @@ -2514,7 +2513,7 @@ void setup_config_box(struct controlbox *b, bool midsession, "Login details"); ctrl_editbox(s, "Auto-login username", 'u', 50, HELPCTX(connection_username), - conf_editbox_handler, I(CONF_username), I(1)); + conf_editbox_handler, I(CONF_username), ED_STR); { /* We assume the local username is sufficiently stable * to include on the dialog box. */ @@ -2535,10 +2534,10 @@ void setup_config_box(struct controlbox *b, bool midsession, "Terminal details"); ctrl_editbox(s, "Terminal-type string", 't', 50, HELPCTX(connection_termtype), - conf_editbox_handler, I(CONF_termtype), I(1)); + conf_editbox_handler, I(CONF_termtype), ED_STR); ctrl_editbox(s, "Terminal speeds", 's', 50, HELPCTX(connection_termspeed), - conf_editbox_handler, I(CONF_termspeed), I(1)); + conf_editbox_handler, I(CONF_termspeed), ED_STR); s = ctrl_getset(b, "Connection/Data", "env", "Environment variables"); @@ -2588,19 +2587,19 @@ void setup_config_box(struct controlbox *b, bool midsession, c = ctrl_editbox(s, "Proxy hostname", 'y', 100, HELPCTX(proxy_main), conf_editbox_handler, - I(CONF_proxy_host), I(1)); + I(CONF_proxy_host), ED_STR); c->column = 0; c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(proxy_main), conf_editbox_handler, I(CONF_proxy_port), - I(-1)); + ED_INT); c->column = 1; ctrl_columns(s, 1, 100); ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100, HELPCTX(proxy_exclude), conf_editbox_handler, - I(CONF_proxy_exclude_list), I(1)); + I(CONF_proxy_exclude_list), ED_STR); ctrl_checkbox(s, "Consider proxying local host connections", 'x', HELPCTX(proxy_exclude), conf_checkbox_handler, @@ -2615,16 +2614,16 @@ void setup_config_box(struct controlbox *b, bool midsession, ctrl_editbox(s, "Username", 'u', 60, HELPCTX(proxy_auth), conf_editbox_handler, - I(CONF_proxy_username), I(1)); + I(CONF_proxy_username), ED_STR); c = ctrl_editbox(s, "Password", 'w', 60, HELPCTX(proxy_auth), conf_editbox_handler, - I(CONF_proxy_password), I(1)); + I(CONF_proxy_password), ED_STR); c->editbox.password = true; ctrl_editbox(s, "Command to send to proxy (for some types)", 'm', 100, HELPCTX(proxy_command), conf_editbox_handler, - I(CONF_proxy_telnet_command), I(1)); + I(CONF_proxy_telnet_command), ED_STR); ctrl_radiobuttons(s, "Print proxy diagnostics " "in the terminal window", 'r', 5, @@ -2674,7 +2673,7 @@ void setup_config_box(struct controlbox *b, bool midsession, "Data to send to the server"); ctrl_editbox(s, "Remote command:", 'r', 100, HELPCTX(ssh_command), - conf_editbox_handler, I(CONF_remote_cmd), I(1)); + conf_editbox_handler, I(CONF_remote_cmd), ED_STR); s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options"); ctrl_checkbox(s, "Don't start a shell or command at all", 'n', @@ -2753,19 +2752,19 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(ssh_kex_repeat), conf_editbox_handler, I(CONF_ssh_rekey_time), - I(-1)); + ED_INT); #ifndef NO_GSSAPI ctrl_editbox(s, "Minutes between GSS checks (0 for never)", NO_SHORTCUT, 20, HELPCTX(ssh_kex_repeat), conf_editbox_handler, I(CONF_gssapirekey), - I(-1)); + ED_INT); #endif ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20, HELPCTX(ssh_kex_repeat), conf_editbox_handler, I(CONF_ssh_rekey_data), - I(16)); + ED_STR); ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)", HELPCTX(ssh_kex_repeat)); } @@ -3044,7 +3043,7 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_checkbox_handler,I(CONF_x11_forward)); ctrl_editbox(s, "X display location", 'x', 50, HELPCTX(ssh_tunnels_x11), - conf_editbox_handler, I(CONF_x11_display), I(1)); + conf_editbox_handler, I(CONF_x11_display), ED_STR); ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2, HELPCTX(ssh_tunnels_x11auth), conf_radiobutton_handler, @@ -3210,22 +3209,26 @@ void setup_config_box(struct controlbox *b, bool midsession, "Select a serial line"); ctrl_editbox(s, "Serial line to connect to", 'l', 40, HELPCTX(serial_line), - conf_editbox_handler, I(CONF_serline), I(1)); + conf_editbox_handler, I(CONF_serline), ED_STR); } s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line"); ctrl_editbox(s, "Speed (baud)", 's', 40, HELPCTX(serial_speed), - conf_editbox_handler, I(CONF_serspeed), I(-1)); + conf_editbox_handler, I(CONF_serspeed), ED_INT); ctrl_editbox(s, "Data bits", 'b', 40, HELPCTX(serial_databits), - conf_editbox_handler, I(CONF_serdatabits), I(-1)); + conf_editbox_handler, I(CONF_serdatabits), ED_INT); /* * Stop bits come in units of one half. */ + static const struct conf_editbox_handler_type conf_editbox_stopbits = { + .type = EDIT_FIXEDPOINT, .denominator = 2}; + ctrl_editbox(s, "Stop bits", 't', 40, HELPCTX(serial_stopbits), - conf_editbox_handler, I(CONF_serstopbits), I(-2)); + conf_editbox_handler, I(CONF_serstopbits), + CP(&conf_editbox_stopbits)); ctrl_droplist(s, "Parity", 'p', 40, HELPCTX(serial_parity), serial_parity_handler, I(ser_vt->serial_parity_mask)); @@ -3279,7 +3282,7 @@ void setup_config_box(struct controlbox *b, bool midsession, "Data to send to the server"); ctrl_editbox(s, "Local username:", 'l', 50, HELPCTX(rlogin_localuser), - conf_editbox_handler, I(CONF_localusername), I(1)); + conf_editbox_handler, I(CONF_localusername), ED_STR); } @@ -3295,7 +3298,7 @@ void setup_config_box(struct controlbox *b, bool midsession, ctrl_editbox(s, "Location string", 'l', 70, HELPCTX(supdup_location), conf_editbox_handler, I(CONF_supdup_location), - I(1)); + ED_STR); ctrl_radiobuttons(s, "Extended ASCII Character set:", 'e', 4, HELPCTX(supdup_ascii), -- cgit v1.2.3 From 15f097f3997c3d0f4720423af9b478a66e844e1d Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 1 Sep 2022 19:38:46 +0100 Subject: New feature: k-i authentication helper plugins. In recent months I've had two requests from different people to build support into PuTTY for automatically handling complicated third-party auth protocols layered on top of keyboard-interactive - the kind of thing where you're asked to enter some auth response, and you have to refer to some external source like a web server to find out what the right response _is_, which is a pain to do by hand, so you'd prefer it to be automated in the SSH client. That seems like a reasonable thing for an end user to want, but I didn't think it was a good idea to build support for specific protocols of that kind directly into PuTTY, where there would no doubt be an ever-lengthening list, and maintenance needed on all of them. So instead, in collaboration with one of my correspondents, I've designed and implemented a protocol to be spoken between PuTTY and a plugin running as a subprocess. The plugin can opt to handle the keyboard-interactive authentication loop on behalf of the user, in which case PuTTY passes on all the INFO_REQUEST packets to it, and lets it make up responses. It can also ask questions of the user if necessary. The protocol spec is provided in a documentation appendix. The entire configuration for the end user consists of providing a full command line to use as the subprocess. In the contrib directory I've provided an example plugin written in Python. It gives a set of fixed responses suitable for getting through Uppity's made-up k-i system, because that was a reasonable thing I already had lying around to test against. But it also provides example code that someone else could pick up and insert their own live response-provider into the middle of, assuming they were happy with it being in Python. --- config.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index c8e72d13..3885c7be 100644 --- a/config.c +++ b/config.c @@ -2899,8 +2899,8 @@ void setup_config_box(struct controlbox *b, bool midsession, conf_checkbox_handler, I(CONF_try_ki_auth)); - s = ctrl_getset(b, "Connection/SSH/Auth", "params", - "Authentication parameters"); + s = ctrl_getset(b, "Connection/SSH/Auth", "aux", + "Other authentication-related options"); ctrl_checkbox(s, "Allow agent forwarding", 'f', HELPCTX(ssh_auth_agentfwd), conf_checkbox_handler, I(CONF_agentfwd)); @@ -2908,6 +2908,12 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(ssh_auth_changeuser), conf_checkbox_handler, I(CONF_change_username)); + + ctrl_settitle(b, "Connection/SSH/Auth/Credentials", + "Credentials to authenticate with"); + + s = ctrl_getset(b, "Connection/SSH/Auth/Credentials", "publickey", + "Public-key authentication"); ctrl_filesel(s, "Private key file for authentication:", 'k', FILTER_KEY_FILES, false, "Select private key file", HELPCTX(ssh_auth_privkey), @@ -2917,6 +2923,11 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(ssh_auth_cert), conf_filesel_handler, I(CONF_detached_cert)); + s = ctrl_getset(b, "Connection/SSH/Auth/Credentials", "plugin", + "Plugin to provide authentication responses"); + ctrl_editbox(s, "Plugin command to run", NO_SHORTCUT, 100, + HELPCTX(ssh_auth_plugin), + conf_editbox_handler, I(CONF_auth_plugin), ED_STR); #ifndef NO_GSSAPI /* * Connection/SSH/Auth/GSSAPI, which sadly won't fit on -- cgit v1.2.3 From dc9ab5e0f0c52f7f41960c807ae73a4e857a3df4 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Fri, 21 Oct 2022 17:46:38 +0100 Subject: Rename NTRU Prime / Curve25519 kex in UI. The previous name, which included '(quantum-resistant)', was too long to be completely seen in the Windows config dialog's kex list (which is narrower than the Gtk one, due to the Up/Down buttons). No point including that explanation if people can't actually read it, so we'll have to rely on docs to explain it. (I did try squashing the rest of the name to "SNTRUP/X25519 hybrid", but that wasn't enough.) As some sort of compensation, index it more thoroughly in the docs, and while I'm there, tweak the indexing of other key exchange algorithms too. --- config.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 3885c7be..8cdeee24 100644 --- a/config.c +++ b/config.c @@ -569,8 +569,7 @@ static void kexlist_handler(dlgcontrol *ctrl, dlgparam *dlg, { "Diffie-Hellman group exchange", KEX_DHGEX }, { "RSA-based key exchange", KEX_RSA }, { "ECDH key exchange", KEX_ECDH }, - { "NTRU Prime / Curve25519 hybrid kex" - " (quantum-resistant)", KEX_NTRU_HYBRID }, + { "NTRU Prime / Curve25519 hybrid kex", KEX_NTRU_HYBRID }, { "-- warn below here --", KEX_WARN } }; -- cgit v1.2.3