1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-07-01 03:22:48 -05:00

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.
This commit is contained in:
Simon Tatham
2022-04-22 12:07:24 +01:00
parent df3a21d97b
commit 21d4754b6a
15 changed files with 1024 additions and 53 deletions

View File

@ -1171,3 +1171,41 @@ void old_keyfile_warning(void)
sfree(msg);
sfree(title);
}
static INT_PTR CAConfigProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam,
void *ctx)
{
PortableDialogStuff *pds = (PortableDialogStuff *)ctx;
switch (msg) {
case WM_INITDIALOG:
pds_initdialog_start(pds, hwnd);
SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
(LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
centre_window(hwnd);
pds_create_controls(pds, 0, IDCX_PANELBASE, 3, 3, 13, "Main");
pds_create_controls(pds, 0, IDCX_STDBASE, 3, 3, 235, "");
dlg_refresh(NULL, pds->dp); /* and set up control values */
pds_initdialog_finish(pds);
return 0;
default:
return pds_default_dlgproc(pds, hwnd, msg, wParam, lParam);
}
}
void show_ca_config_box(dlgparam *dp)
{
PortableDialogStuff *pds = pds_new(1);
setup_ca_config_box(pds->ctrlbox);
ShinyDialogBox(hinst, MAKEINTRESOURCE(IDD_CA_CONFIG), "PuTTYConfigBox",
dp ? dp->hwnd : NULL, CAConfigProc, pds);
pds_free(pds);
}

View File

@ -130,4 +130,13 @@ BEGIN
DEFPUSHBUTTON "&Close", IDOK, 176, 130, 48, 14
END
/* Accelerators used: aco */
IDD_CA_CONFIG DIALOG DISCARDABLE 0, 0, 300, 252
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "PuTTY trusted host certification authorities"
FONT 8, "MS Shell Dlg"
CLASS "PuTTYConfigBox"
BEGIN
END
#include "version.rc2"

View File

@ -16,6 +16,7 @@
#define IDD_HK_ABSENT 114
#define IDD_HK_WRONG 115
#define IDD_HK_MOREINFO 116
#define IDD_CA_CONFIG 117
#define IDN_LIST 1001
#define IDN_COPY 1002

View File

@ -21,6 +21,7 @@
static const char *const reg_jumplist_key = PUTTY_REG_POS "\\Jumplist";
static const char *const reg_jumplist_value = "Recent sessions";
static const char *const puttystr = PUTTY_REG_POS "\\Sessions";
static const char *const host_ca_key = PUTTY_REG_POS "\\SshHostCAs";
static bool tried_shgetfolderpath = false;
static HMODULE shell32_module = NULL;
@ -371,6 +372,128 @@ void store_host_key(const char *hostname, int port,
strbuf_free(regname);
}
struct host_ca_enum {
HKEY key;
int i;
};
host_ca_enum *enum_host_ca_start(void)
{
host_ca_enum *e;
HKEY key;
if (!(key = open_regkey(false, HKEY_CURRENT_USER, host_ca_key)))
return NULL;
e = snew(host_ca_enum);
e->key = key;
e->i = 0;
return e;
}
bool enum_host_ca_next(host_ca_enum *e, strbuf *sb)
{
char *regbuf = enum_regkey(e->key, e->i);
if (!regbuf)
return false;
unescape_registry_key(regbuf, sb);
sfree(regbuf);
e->i++;
return true;
}
void enum_host_ca_finish(host_ca_enum *e)
{
close_regkey(e->key);
sfree(e);
}
host_ca *host_ca_load(const char *name)
{
strbuf *sb;
const char *s;
sb = strbuf_new();
escape_registry_key(name, sb);
HKEY rkey = open_regkey(false, HKEY_CURRENT_USER, host_ca_key, sb->s);
strbuf_free(sb);
if (!rkey)
return NULL;
host_ca *hca = snew(host_ca);
memset(hca, 0, sizeof(*hca));
hca->name = dupstr(name);
if ((s = get_reg_sz(rkey, "PublicKey")) != NULL)
hca->ca_public_key = base64_decode_sb(ptrlen_from_asciz(s));
if ((sb = get_reg_multi_sz(rkey, "MatchHosts")) != NULL) {
BinarySource src[1];
BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(sb));
const char *wc;
size_t wcsize = 0;
while (wc = get_asciz(src), !get_err(src)) {
sgrowarray(hca->hostname_wildcards, wcsize,
hca->n_hostname_wildcards);
hca->hostname_wildcards[hca->n_hostname_wildcards++] = dupstr(wc);
}
strbuf_free(sb);
}
close_regkey(rkey);
return hca;
}
char *host_ca_save(host_ca *hca)
{
if (!*hca->name)
return dupstr("CA record must have a name");
strbuf *sb = strbuf_new();
escape_registry_key(hca->name, sb);
HKEY rkey = open_regkey(true, HKEY_CURRENT_USER, host_ca_key, sb->s);
if (!rkey) {
char *err = dupprintf("Unable to create registry key\n"
"HKEY_CURRENT_USER\\%s\\%s", host_ca_key, sb->s);
strbuf_free(sb);
return err;
}
strbuf_free(sb);
strbuf *base64_pubkey = base64_encode_sb(
ptrlen_from_strbuf(hca->ca_public_key), 0);
put_reg_sz(rkey, "PublicKey", base64_pubkey->s);
strbuf_free(base64_pubkey);
strbuf *wcs = strbuf_new();
for (size_t i = 0; i < hca->n_hostname_wildcards; i++)
put_asciz(wcs, hca->hostname_wildcards[i]);
put_reg_multi_sz(rkey, "MatchHosts", wcs);
strbuf_free(wcs);
close_regkey(rkey);
return NULL;
}
char *host_ca_delete(const char *name)
{
HKEY rkey = open_regkey(false, HKEY_CURRENT_USER, host_ca_key);
if (!rkey)
return NULL;
strbuf *sb = strbuf_new();
escape_registry_key(name, sb);
del_regkey(rkey, sb->s);
strbuf_free(sb);
return NULL;
}
/*
* Open (or delete) the random seed file.
*/