1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/windows/storage.c

869 lines
24 KiB
C
Raw Permalink Normal View History

/*
* storage.c: Windows-specific implementation of the interface
* defined in storage.h.
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
#include "putty.h"
#include "storage.h"
#include <shlobj.h>
#ifndef CSIDL_APPDATA
#define CSIDL_APPDATA 0x001a
#endif
#ifndef CSIDL_LOCAL_APPDATA
#define CSIDL_LOCAL_APPDATA 0x001c
#endif
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";
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
static bool tried_shgetfolderpath = false;
static HMODULE shell32_module = NULL;
DECL_WINDOWS_FUNCTION(static, HRESULT, SHGetFolderPathA,
(HWND, int, HANDLE, DWORD, LPSTR));
struct settings_w {
HKEY sesskey;
};
settings_w *open_settings_w(const char *sessionname, char **errmsg)
{
*errmsg = NULL;
if (!sessionname || !*sessionname)
sessionname = "Default Settings";
strbuf *sb = strbuf_new();
escape_registry_key(sessionname, sb);
2022-09-14 15:04:14 +00:00
HKEY sesskey = create_regkey(HKEY_CURRENT_USER, puttystr, sb->s);
if (!sesskey) {
*errmsg = dupprintf("Unable to create registry key\n"
"HKEY_CURRENT_USER\\%s\\%s", puttystr, sb->s);
strbuf_free(sb);
return NULL;
}
strbuf_free(sb);
Rename 'ret' variables passed from allocation to return. I mentioned recently (in commit 9e7d4c53d80b6eb) message that I'm no longer fond of the variable name 'ret', because it's used in two quite different contexts: it's the return value from a subroutine you just called (e.g. 'int ret = read(fd, buf, len);' and then check for error or EOF), or it's the value you're preparing to return from the _containing_ routine (maybe by assigning it a default value and then conditionally modifying it, or by starting at NULL and reallocating, or setting it just before using the 'goto out' cleanup idiom). In the past I've occasionally made mistakes by forgetting which meaning the variable had, or accidentally conflating both uses. If all else fails, I now prefer 'retd' (short for 'returned') in the former situation, and 'toret' (obviously, the value 'to return') in the latter case. But even better is to pick a name that actually says something more specific about what the thing actually is. One particular bad habit throughout this codebase is to have a set of functions that deal with some object type (say 'Foo'), all *but one* of which take a 'Foo *foo' parameter, but the foo_new() function starts with 'Foo *ret = snew(Foo)'. If all the rest of them think the canonical name for the ambient Foo is 'foo', so should foo_new()! So here's a no-brainer start on cutting down on the uses of 'ret': I looked for all the cases where it was being assigned the result of an allocation, and renamed the variable to be a description of the thing being allocated. In the case of a new() function belonging to a family, I picked the same name as the rest of the functions in its own family, for consistency. In other cases I picked something sensible. One case where it _does_ make sense not to use your usual name for the variable type is when you're cloning an existing object. In that case, _neither_ of the Foo objects involved should be called 'foo', because it's ambiguous! They should be named so you can see which is which. In the two cases I found here, I've called them 'orig' and 'copy'. As in the previous refactoring, many thanks to clang-rename for the help.
2022-09-13 13:53:36 +00:00
settings_w *handle = snew(settings_w);
handle->sesskey = sesskey;
return handle;
}
void write_setting_s(settings_w *handle, const char *key, const char *value)
{
if (handle)
put_reg_sz(handle->sesskey, key, value);
}
void write_setting_i(settings_w *handle, const char *key, int value)
{
if (handle)
put_reg_dword(handle->sesskey, key, value);
}
void close_settings_w(settings_w *handle)
{
close_regkey(handle->sesskey);
sfree(handle);
}
struct settings_r {
HKEY sesskey;
};
settings_r *open_settings_r(const char *sessionname)
{
if (!sessionname || !*sessionname)
sessionname = "Default Settings";
strbuf *sb = strbuf_new();
escape_registry_key(sessionname, sb);
2022-09-14 15:04:14 +00:00
HKEY sesskey = open_regkey_ro(HKEY_CURRENT_USER, puttystr, sb->s);
strbuf_free(sb);
if (!sesskey)
return NULL;
Rename 'ret' variables passed from allocation to return. I mentioned recently (in commit 9e7d4c53d80b6eb) message that I'm no longer fond of the variable name 'ret', because it's used in two quite different contexts: it's the return value from a subroutine you just called (e.g. 'int ret = read(fd, buf, len);' and then check for error or EOF), or it's the value you're preparing to return from the _containing_ routine (maybe by assigning it a default value and then conditionally modifying it, or by starting at NULL and reallocating, or setting it just before using the 'goto out' cleanup idiom). In the past I've occasionally made mistakes by forgetting which meaning the variable had, or accidentally conflating both uses. If all else fails, I now prefer 'retd' (short for 'returned') in the former situation, and 'toret' (obviously, the value 'to return') in the latter case. But even better is to pick a name that actually says something more specific about what the thing actually is. One particular bad habit throughout this codebase is to have a set of functions that deal with some object type (say 'Foo'), all *but one* of which take a 'Foo *foo' parameter, but the foo_new() function starts with 'Foo *ret = snew(Foo)'. If all the rest of them think the canonical name for the ambient Foo is 'foo', so should foo_new()! So here's a no-brainer start on cutting down on the uses of 'ret': I looked for all the cases where it was being assigned the result of an allocation, and renamed the variable to be a description of the thing being allocated. In the case of a new() function belonging to a family, I picked the same name as the rest of the functions in its own family, for consistency. In other cases I picked something sensible. One case where it _does_ make sense not to use your usual name for the variable type is when you're cloning an existing object. In that case, _neither_ of the Foo objects involved should be called 'foo', because it's ambiguous! They should be named so you can see which is which. In the two cases I found here, I've called them 'orig' and 'copy'. As in the previous refactoring, many thanks to clang-rename for the help.
2022-09-13 13:53:36 +00:00
settings_r *handle = snew(settings_r);
handle->sesskey = sesskey;
return handle;
}
char *read_setting_s(settings_r *handle, const char *key)
{
Post-release destabilisation! Completely remove the struct type 'Config' in putty.h, which stores all PuTTY's settings and includes an arbitrary length limit on every single one of those settings which is stored in string form. In place of it is 'Conf', an opaque data type everywhere outside the new file conf.c, which stores a list of (key, value) pairs in which every key contains an integer identifying a configuration setting, and for some of those integers the key also contains extra parts (so that, for instance, CONF_environmt is a string-to-string mapping). Everywhere that a Config was previously used, a Conf is now; everywhere there was a Config structure copy, conf_copy() is called; every lookup, adjustment, load and save operation on a Config has been rewritten; and there's a mechanism for serialising a Conf into a binary blob and back for use with Duplicate Session. User-visible effects of this change _should_ be minimal, though I don't doubt I've introduced one or two bugs here and there which will eventually be found. The _intended_ visible effects of this change are that all arbitrary limits on configuration strings and lists (e.g. limit on number of port forwardings) should now disappear; that list boxes in the configuration will now be displayed in a sorted order rather than the arbitrary order in which they were added to the list (since the underlying data structure is now a sorted tree234 rather than an ad-hoc comma-separated string); and one more specific change, which is that local and dynamic port forwardings on the same port number are now mutually exclusive in the configuration (putting 'D' in the key rather than the value was a mistake in the first place). One other reorganisation as a result of this is that I've moved all the dialog.c standard handlers (dlg_stdeditbox_handler and friends) out into config.c, because I can't really justify calling them generic any more. When they took a pointer to an arbitrary structure type and the offset of a field within that structure, they were independent of whether that structure was a Config or something completely different, but now they really do expect to talk to a Conf, which can _only_ be used for PuTTY configuration, so I've renamed them all things like conf_editbox_handler and moved them out of the nominally independent dialog-box management module into the PuTTY-specific config.c. [originally from svn r9214]
2011-07-14 18:52:21 +00:00
if (!handle)
return NULL;
return get_reg_sz(handle->sesskey, key);
}
int read_setting_i(settings_r *handle, const char *key, int defvalue)
{
DWORD val;
if (!handle || !get_reg_dword(handle->sesskey, key, &val))
return defvalue;
else
return val;
}
FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
{
char *settingname;
Post-release destabilisation! Completely remove the struct type 'Config' in putty.h, which stores all PuTTY's settings and includes an arbitrary length limit on every single one of those settings which is stored in string form. In place of it is 'Conf', an opaque data type everywhere outside the new file conf.c, which stores a list of (key, value) pairs in which every key contains an integer identifying a configuration setting, and for some of those integers the key also contains extra parts (so that, for instance, CONF_environmt is a string-to-string mapping). Everywhere that a Config was previously used, a Conf is now; everywhere there was a Config structure copy, conf_copy() is called; every lookup, adjustment, load and save operation on a Config has been rewritten; and there's a mechanism for serialising a Conf into a binary blob and back for use with Duplicate Session. User-visible effects of this change _should_ be minimal, though I don't doubt I've introduced one or two bugs here and there which will eventually be found. The _intended_ visible effects of this change are that all arbitrary limits on configuration strings and lists (e.g. limit on number of port forwardings) should now disappear; that list boxes in the configuration will now be displayed in a sorted order rather than the arbitrary order in which they were added to the list (since the underlying data structure is now a sorted tree234 rather than an ad-hoc comma-separated string); and one more specific change, which is that local and dynamic port forwardings on the same port number are now mutually exclusive in the configuration (putting 'D' in the key rather than the value was a mistake in the first place). One other reorganisation as a result of this is that I've moved all the dialog.c standard handlers (dlg_stdeditbox_handler and friends) out into config.c, because I can't really justify calling them generic any more. When they took a pointer to an arbitrary structure type and the offset of a field within that structure, they were independent of whether that structure was a Config or something completely different, but now they really do expect to talk to a Conf, which can _only_ be used for PuTTY configuration, so I've renamed them all things like conf_editbox_handler and moved them out of the nominally independent dialog-box management module into the PuTTY-specific config.c. [originally from svn r9214]
2011-07-14 18:52:21 +00:00
char *fontname;
FontSpec *ret;
int isbold, height, charset;
Post-release destabilisation! Completely remove the struct type 'Config' in putty.h, which stores all PuTTY's settings and includes an arbitrary length limit on every single one of those settings which is stored in string form. In place of it is 'Conf', an opaque data type everywhere outside the new file conf.c, which stores a list of (key, value) pairs in which every key contains an integer identifying a configuration setting, and for some of those integers the key also contains extra parts (so that, for instance, CONF_environmt is a string-to-string mapping). Everywhere that a Config was previously used, a Conf is now; everywhere there was a Config structure copy, conf_copy() is called; every lookup, adjustment, load and save operation on a Config has been rewritten; and there's a mechanism for serialising a Conf into a binary blob and back for use with Duplicate Session. User-visible effects of this change _should_ be minimal, though I don't doubt I've introduced one or two bugs here and there which will eventually be found. The _intended_ visible effects of this change are that all arbitrary limits on configuration strings and lists (e.g. limit on number of port forwardings) should now disappear; that list boxes in the configuration will now be displayed in a sorted order rather than the arbitrary order in which they were added to the list (since the underlying data structure is now a sorted tree234 rather than an ad-hoc comma-separated string); and one more specific change, which is that local and dynamic port forwardings on the same port number are now mutually exclusive in the configuration (putting 'D' in the key rather than the value was a mistake in the first place). One other reorganisation as a result of this is that I've moved all the dialog.c standard handlers (dlg_stdeditbox_handler and friends) out into config.c, because I can't really justify calling them generic any more. When they took a pointer to an arbitrary structure type and the offset of a field within that structure, they were independent of whether that structure was a Config or something completely different, but now they really do expect to talk to a Conf, which can _only_ be used for PuTTY configuration, so I've renamed them all things like conf_editbox_handler and moved them out of the nominally independent dialog-box management module into the PuTTY-specific config.c. [originally from svn r9214]
2011-07-14 18:52:21 +00:00
fontname = read_setting_s(handle, name);
if (!fontname)
return NULL;
Post-release destabilisation! Completely remove the struct type 'Config' in putty.h, which stores all PuTTY's settings and includes an arbitrary length limit on every single one of those settings which is stored in string form. In place of it is 'Conf', an opaque data type everywhere outside the new file conf.c, which stores a list of (key, value) pairs in which every key contains an integer identifying a configuration setting, and for some of those integers the key also contains extra parts (so that, for instance, CONF_environmt is a string-to-string mapping). Everywhere that a Config was previously used, a Conf is now; everywhere there was a Config structure copy, conf_copy() is called; every lookup, adjustment, load and save operation on a Config has been rewritten; and there's a mechanism for serialising a Conf into a binary blob and back for use with Duplicate Session. User-visible effects of this change _should_ be minimal, though I don't doubt I've introduced one or two bugs here and there which will eventually be found. The _intended_ visible effects of this change are that all arbitrary limits on configuration strings and lists (e.g. limit on number of port forwardings) should now disappear; that list boxes in the configuration will now be displayed in a sorted order rather than the arbitrary order in which they were added to the list (since the underlying data structure is now a sorted tree234 rather than an ad-hoc comma-separated string); and one more specific change, which is that local and dynamic port forwardings on the same port number are now mutually exclusive in the configuration (putting 'D' in the key rather than the value was a mistake in the first place). One other reorganisation as a result of this is that I've moved all the dialog.c standard handlers (dlg_stdeditbox_handler and friends) out into config.c, because I can't really justify calling them generic any more. When they took a pointer to an arbitrary structure type and the offset of a field within that structure, they were independent of whether that structure was a Config or something completely different, but now they really do expect to talk to a Conf, which can _only_ be used for PuTTY configuration, so I've renamed them all things like conf_editbox_handler and moved them out of the nominally independent dialog-box management module into the PuTTY-specific config.c. [originally from svn r9214]
2011-07-14 18:52:21 +00:00
settingname = dupcat(name, "IsBold");
isbold = read_setting_i(handle, settingname, -1);
sfree(settingname);
if (isbold == -1) {
sfree(fontname);
return NULL;
}
Post-release destabilisation! Completely remove the struct type 'Config' in putty.h, which stores all PuTTY's settings and includes an arbitrary length limit on every single one of those settings which is stored in string form. In place of it is 'Conf', an opaque data type everywhere outside the new file conf.c, which stores a list of (key, value) pairs in which every key contains an integer identifying a configuration setting, and for some of those integers the key also contains extra parts (so that, for instance, CONF_environmt is a string-to-string mapping). Everywhere that a Config was previously used, a Conf is now; everywhere there was a Config structure copy, conf_copy() is called; every lookup, adjustment, load and save operation on a Config has been rewritten; and there's a mechanism for serialising a Conf into a binary blob and back for use with Duplicate Session. User-visible effects of this change _should_ be minimal, though I don't doubt I've introduced one or two bugs here and there which will eventually be found. The _intended_ visible effects of this change are that all arbitrary limits on configuration strings and lists (e.g. limit on number of port forwardings) should now disappear; that list boxes in the configuration will now be displayed in a sorted order rather than the arbitrary order in which they were added to the list (since the underlying data structure is now a sorted tree234 rather than an ad-hoc comma-separated string); and one more specific change, which is that local and dynamic port forwardings on the same port number are now mutually exclusive in the configuration (putting 'D' in the key rather than the value was a mistake in the first place). One other reorganisation as a result of this is that I've moved all the dialog.c standard handlers (dlg_stdeditbox_handler and friends) out into config.c, because I can't really justify calling them generic any more. When they took a pointer to an arbitrary structure type and the offset of a field within that structure, they were independent of whether that structure was a Config or something completely different, but now they really do expect to talk to a Conf, which can _only_ be used for PuTTY configuration, so I've renamed them all things like conf_editbox_handler and moved them out of the nominally independent dialog-box management module into the PuTTY-specific config.c. [originally from svn r9214]
2011-07-14 18:52:21 +00:00
settingname = dupcat(name, "CharSet");
charset = read_setting_i(handle, settingname, -1);
sfree(settingname);
if (charset == -1) {
sfree(fontname);
return NULL;
}
Post-release destabilisation! Completely remove the struct type 'Config' in putty.h, which stores all PuTTY's settings and includes an arbitrary length limit on every single one of those settings which is stored in string form. In place of it is 'Conf', an opaque data type everywhere outside the new file conf.c, which stores a list of (key, value) pairs in which every key contains an integer identifying a configuration setting, and for some of those integers the key also contains extra parts (so that, for instance, CONF_environmt is a string-to-string mapping). Everywhere that a Config was previously used, a Conf is now; everywhere there was a Config structure copy, conf_copy() is called; every lookup, adjustment, load and save operation on a Config has been rewritten; and there's a mechanism for serialising a Conf into a binary blob and back for use with Duplicate Session. User-visible effects of this change _should_ be minimal, though I don't doubt I've introduced one or two bugs here and there which will eventually be found. The _intended_ visible effects of this change are that all arbitrary limits on configuration strings and lists (e.g. limit on number of port forwardings) should now disappear; that list boxes in the configuration will now be displayed in a sorted order rather than the arbitrary order in which they were added to the list (since the underlying data structure is now a sorted tree234 rather than an ad-hoc comma-separated string); and one more specific change, which is that local and dynamic port forwardings on the same port number are now mutually exclusive in the configuration (putting 'D' in the key rather than the value was a mistake in the first place). One other reorganisation as a result of this is that I've moved all the dialog.c standard handlers (dlg_stdeditbox_handler and friends) out into config.c, because I can't really justify calling them generic any more. When they took a pointer to an arbitrary structure type and the offset of a field within that structure, they were independent of whether that structure was a Config or something completely different, but now they really do expect to talk to a Conf, which can _only_ be used for PuTTY configuration, so I've renamed them all things like conf_editbox_handler and moved them out of the nominally independent dialog-box management module into the PuTTY-specific config.c. [originally from svn r9214]
2011-07-14 18:52:21 +00:00
settingname = dupcat(name, "Height");
height = read_setting_i(handle, settingname, INT_MIN);
sfree(settingname);
if (height == INT_MIN) {
sfree(fontname);
return NULL;
}
ret = fontspec_new(fontname, isbold, height, charset);
sfree(fontname);
return ret;
}
void write_setting_fontspec(settings_w *handle,
const char *name, FontSpec *font)
{
char *settingname;
write_setting_s(handle, name, font->name);
settingname = dupcat(name, "IsBold");
write_setting_i(handle, settingname, font->isbold);
sfree(settingname);
settingname = dupcat(name, "CharSet");
write_setting_i(handle, settingname, font->charset);
sfree(settingname);
settingname = dupcat(name, "Height");
write_setting_i(handle, settingname, font->height);
sfree(settingname);
}
Filename *read_setting_filename(settings_r *handle, const char *name)
{
Post-release destabilisation! Completely remove the struct type 'Config' in putty.h, which stores all PuTTY's settings and includes an arbitrary length limit on every single one of those settings which is stored in string form. In place of it is 'Conf', an opaque data type everywhere outside the new file conf.c, which stores a list of (key, value) pairs in which every key contains an integer identifying a configuration setting, and for some of those integers the key also contains extra parts (so that, for instance, CONF_environmt is a string-to-string mapping). Everywhere that a Config was previously used, a Conf is now; everywhere there was a Config structure copy, conf_copy() is called; every lookup, adjustment, load and save operation on a Config has been rewritten; and there's a mechanism for serialising a Conf into a binary blob and back for use with Duplicate Session. User-visible effects of this change _should_ be minimal, though I don't doubt I've introduced one or two bugs here and there which will eventually be found. The _intended_ visible effects of this change are that all arbitrary limits on configuration strings and lists (e.g. limit on number of port forwardings) should now disappear; that list boxes in the configuration will now be displayed in a sorted order rather than the arbitrary order in which they were added to the list (since the underlying data structure is now a sorted tree234 rather than an ad-hoc comma-separated string); and one more specific change, which is that local and dynamic port forwardings on the same port number are now mutually exclusive in the configuration (putting 'D' in the key rather than the value was a mistake in the first place). One other reorganisation as a result of this is that I've moved all the dialog.c standard handlers (dlg_stdeditbox_handler and friends) out into config.c, because I can't really justify calling them generic any more. When they took a pointer to an arbitrary structure type and the offset of a field within that structure, they were independent of whether that structure was a Config or something completely different, but now they really do expect to talk to a Conf, which can _only_ be used for PuTTY configuration, so I've renamed them all things like conf_editbox_handler and moved them out of the nominally independent dialog-box management module into the PuTTY-specific config.c. [originally from svn r9214]
2011-07-14 18:52:21 +00:00
char *tmp = read_setting_s(handle, name);
if (tmp) {
Filename *ret = filename_from_str(tmp);
sfree(tmp);
return ret;
Post-release destabilisation! Completely remove the struct type 'Config' in putty.h, which stores all PuTTY's settings and includes an arbitrary length limit on every single one of those settings which is stored in string form. In place of it is 'Conf', an opaque data type everywhere outside the new file conf.c, which stores a list of (key, value) pairs in which every key contains an integer identifying a configuration setting, and for some of those integers the key also contains extra parts (so that, for instance, CONF_environmt is a string-to-string mapping). Everywhere that a Config was previously used, a Conf is now; everywhere there was a Config structure copy, conf_copy() is called; every lookup, adjustment, load and save operation on a Config has been rewritten; and there's a mechanism for serialising a Conf into a binary blob and back for use with Duplicate Session. User-visible effects of this change _should_ be minimal, though I don't doubt I've introduced one or two bugs here and there which will eventually be found. The _intended_ visible effects of this change are that all arbitrary limits on configuration strings and lists (e.g. limit on number of port forwardings) should now disappear; that list boxes in the configuration will now be displayed in a sorted order rather than the arbitrary order in which they were added to the list (since the underlying data structure is now a sorted tree234 rather than an ad-hoc comma-separated string); and one more specific change, which is that local and dynamic port forwardings on the same port number are now mutually exclusive in the configuration (putting 'D' in the key rather than the value was a mistake in the first place). One other reorganisation as a result of this is that I've moved all the dialog.c standard handlers (dlg_stdeditbox_handler and friends) out into config.c, because I can't really justify calling them generic any more. When they took a pointer to an arbitrary structure type and the offset of a field within that structure, they were independent of whether that structure was a Config or something completely different, but now they really do expect to talk to a Conf, which can _only_ be used for PuTTY configuration, so I've renamed them all things like conf_editbox_handler and moved them out of the nominally independent dialog-box management module into the PuTTY-specific config.c. [originally from svn r9214]
2011-07-14 18:52:21 +00:00
} else
return NULL;
}
void write_setting_filename(settings_w *handle,
const char *name, Filename *result)
{
Some support for wide-character filenames in Windows. The Windows version of the Filename structure now contains three versions of the pathname, in UTF-16, UTF-8 and the system code page. Callers can use whichever is most convenient. All uses of filenames for actually opening files now use the UTF-16 version, which means they can tolerate 'exotic' filenames, by which I mean those including Unicode characters outside the host system's CP_ACP default code page. Other uses of Filename structures inside the 'windows' subdirectory do something appropriate, e.g. when printing a filename inside a message box or a console message, we use the UTF-8 version of the filename with the UTF-8 version of the appropriate API. There are three remaining pieces to full Unicode filename support: One is that the cross-platform code has many calls to filename_to_str(), embodying the assumption that a file name can be reliably converted into the unspecified current character set; those will all need changing in some way. Another is that write_setting_filename(), in windows/storage.c, still saves filenames to the Registry as an ordinary REG_SZ in the system code page. So even if an exotic filename were stored in a Conf, that Conf couldn't round-trip via the Registry and back without corrupting that filename by coercing it back to a string that fits in CP_ACP and therefore doesn't represent the same file. This can't be fixed without a compatibility break in the storage format, and I don't want to make a minimal change in that area: if we're going to break compatibility, then we should break it good and hard (the Nanny Ogg principle), and devise a completely fresh storage representation that fixes as many other legacy problems as possible at the same time. So that's my plan, not yet started. The final point, much more obviously, is that we're still short of methods to _construct_ any Filename structures using a Unicode input string! It should now work to enter one in the GUI configurer (either by manual text input or via the file selector), but it won't round-trip through a save and load (as discussed above), and there's still no way to specify one on the command line (the groundwork is laid by commit 10e1ac7752de928 but not yet linked up). But this is a start.
2023-05-28 10:30:59 +00:00
/*
* When saving a session involving a Filename, we use the 'cpath'
* member of the Filename structure, because otherwise we break
* backwards compatibility with existing saved sessions.
*
* This means that 'exotic' filenames - those including Unicode
* characters outside the host system's CP_ACP default code page -
* cannot be represented faithfully, and saving and reloading a
* Conf including one will break it.
*
* This can't be fixed without breaking backwards compatibility,
* and if we're going to break compatibility then we should break
* it good and hard (the Nanny Ogg principle), and devise a
* completely fresh storage representation that fixes as many
* other legacy problems as possible at the same time.
*/
write_setting_s(handle, name, result->cpath); /* FIXME */
}
void close_settings_r(settings_r *handle)
{
if (handle) {
close_regkey(handle->sesskey);
sfree(handle);
}
}
void del_settings(const char *sessionname)
{
2022-09-14 15:04:14 +00:00
HKEY rkey = open_regkey_rw(HKEY_CURRENT_USER, puttystr);
if (!rkey)
return;
strbuf *sb = strbuf_new();
escape_registry_key(sessionname, sb);
del_regkey(rkey, sb->s);
strbuf_free(sb);
close_regkey(rkey);
remove_session_from_jumplist(sessionname);
}
struct settings_e {
HKEY key;
int i;
};
settings_e *enum_settings_start(void)
{
2022-09-14 15:04:14 +00:00
HKEY key = open_regkey_ro(HKEY_CURRENT_USER, puttystr);
if (!key)
return NULL;
Rename 'ret' variables passed from allocation to return. I mentioned recently (in commit 9e7d4c53d80b6eb) message that I'm no longer fond of the variable name 'ret', because it's used in two quite different contexts: it's the return value from a subroutine you just called (e.g. 'int ret = read(fd, buf, len);' and then check for error or EOF), or it's the value you're preparing to return from the _containing_ routine (maybe by assigning it a default value and then conditionally modifying it, or by starting at NULL and reallocating, or setting it just before using the 'goto out' cleanup idiom). In the past I've occasionally made mistakes by forgetting which meaning the variable had, or accidentally conflating both uses. If all else fails, I now prefer 'retd' (short for 'returned') in the former situation, and 'toret' (obviously, the value 'to return') in the latter case. But even better is to pick a name that actually says something more specific about what the thing actually is. One particular bad habit throughout this codebase is to have a set of functions that deal with some object type (say 'Foo'), all *but one* of which take a 'Foo *foo' parameter, but the foo_new() function starts with 'Foo *ret = snew(Foo)'. If all the rest of them think the canonical name for the ambient Foo is 'foo', so should foo_new()! So here's a no-brainer start on cutting down on the uses of 'ret': I looked for all the cases where it was being assigned the result of an allocation, and renamed the variable to be a description of the thing being allocated. In the case of a new() function belonging to a family, I picked the same name as the rest of the functions in its own family, for consistency. In other cases I picked something sensible. One case where it _does_ make sense not to use your usual name for the variable type is when you're cloning an existing object. In that case, _neither_ of the Foo objects involved should be called 'foo', because it's ambiguous! They should be named so you can see which is which. In the two cases I found here, I've called them 'orig' and 'copy'. As in the previous refactoring, many thanks to clang-rename for the help.
2022-09-13 13:53:36 +00:00
settings_e *e = snew(settings_e);
if (e) {
e->key = key;
e->i = 0;
}
Rename 'ret' variables passed from allocation to return. I mentioned recently (in commit 9e7d4c53d80b6eb) message that I'm no longer fond of the variable name 'ret', because it's used in two quite different contexts: it's the return value from a subroutine you just called (e.g. 'int ret = read(fd, buf, len);' and then check for error or EOF), or it's the value you're preparing to return from the _containing_ routine (maybe by assigning it a default value and then conditionally modifying it, or by starting at NULL and reallocating, or setting it just before using the 'goto out' cleanup idiom). In the past I've occasionally made mistakes by forgetting which meaning the variable had, or accidentally conflating both uses. If all else fails, I now prefer 'retd' (short for 'returned') in the former situation, and 'toret' (obviously, the value 'to return') in the latter case. But even better is to pick a name that actually says something more specific about what the thing actually is. One particular bad habit throughout this codebase is to have a set of functions that deal with some object type (say 'Foo'), all *but one* of which take a 'Foo *foo' parameter, but the foo_new() function starts with 'Foo *ret = snew(Foo)'. If all the rest of them think the canonical name for the ambient Foo is 'foo', so should foo_new()! So here's a no-brainer start on cutting down on the uses of 'ret': I looked for all the cases where it was being assigned the result of an allocation, and renamed the variable to be a description of the thing being allocated. In the case of a new() function belonging to a family, I picked the same name as the rest of the functions in its own family, for consistency. In other cases I picked something sensible. One case where it _does_ make sense not to use your usual name for the variable type is when you're cloning an existing object. In that case, _neither_ of the Foo objects involved should be called 'foo', because it's ambiguous! They should be named so you can see which is which. In the two cases I found here, I've called them 'orig' and 'copy'. As in the previous refactoring, many thanks to clang-rename for the help.
2022-09-13 13:53:36 +00:00
return e;
}
bool enum_settings_next(settings_e *e, strbuf *sb)
{
char *name = enum_regkey(e->key, e->i);
if (!name)
return false;
unescape_registry_key(name, sb);
sfree(name);
e->i++;
return true;
}
void enum_settings_finish(settings_e *e)
{
close_regkey(e->key);
sfree(e);
}
static void hostkey_regname(strbuf *sb, const char *hostname,
int port, const char *keytype)
{
put_fmt(sb, "%s@%d:", keytype, port);
escape_registry_key(hostname, sb);
}
Reorganise host key checking and confirmation. Previously, checking the host key against the persistent cache managed by the storage.h API was done as part of the seat_verify_ssh_host_key method, i.e. separately by each Seat. Now that check is done by verify_ssh_host_key(), which is a new function in ssh/common.c that centralises all the parts of host key checking that don't need an interactive prompt. It subsumes the previous verify_ssh_manual_host_key() that checked against the Conf, and it does the check against the storage API that each Seat was previously doing separately. If it can't confirm or definitively reject the host key by itself, _then_ it calls out to the Seat, once an interactive prompt is definitely needed. The main point of doing this is so that when SshProxy forwards a Seat call from the proxy SSH connection to the primary Seat, it won't print an announcement of which connection is involved unless it's actually going to do something interactive. (Not that we're printing those announcements _yet_ anyway, but this is a piece of groundwork that works towards doing so.) But while I'm at it, I've also taken the opportunity to clean things up a bit by renaming functions sensibly. Previously we had three very similarly named functions verify_ssh_manual_host_key(), SeatVtable's 'verify_ssh_host_key' method, and verify_host_key() in storage.h. Now the Seat method is called 'confirm' rather than 'verify' (since its job is now always to print an interactive prompt, so it looks more like the other confirm_foo methods), and the storage.h function is called check_stored_host_key(), which goes better with store_host_key and avoids having too many functions with similar names. And the 'manual' function is subsumed into the new centralised code, so there's now just *one* host key function with 'verify' in the name. Several functions are reindented in this commit. Best viewed with whitespace changes ignored.
2021-10-25 17:12:17 +00:00
int check_stored_host_key(const char *hostname, int port,
const char *keytype, const char *key)
{
/*
* Read a saved key in from the registry and see what it says.
*/
strbuf *regname = strbuf_new();
hostkey_regname(regname, hostname, port, keytype);
2022-09-14 15:04:14 +00:00
HKEY rkey = open_regkey_ro(HKEY_CURRENT_USER,
PUTTY_REG_POS "\\SshHostKeys");
if (!rkey) {
strbuf_free(regname);
return 1; /* key does not exist in registry */
}
char *otherstr = get_reg_sz(rkey, regname->s);
if (!otherstr && !strcmp(keytype, "rsa")) {
/*
* Key didn't exist. If the key type is RSA, we'll try
* another trick, which is to look up the _old_ key format
* under just the hostname and translate that.
*/
char *justhost = regname->s + 1 + strcspn(regname->s, ":");
char *oldstyle = get_reg_sz(rkey, justhost);
if (oldstyle) {
/*
* The old format is two old-style bignums separated by
* a slash. An old-style bignum is made of groups of
* four hex digits: digits are ordered in sensible
* (most to least significant) order within each group,
* but groups are ordered in silly (least to most)
* order within the bignum. The new format is two
* ordinary C-format hex numbers (0xABCDEFG...XYZ, with
* A nonzero except in the special case 0x0, which
* doesn't appear anyway in RSA keys) separated by a
* comma. All hex digits are lowercase in both formats.
*/
strbuf *new = strbuf_new();
const char *q = oldstyle;
int i, j;
for (i = 0; i < 2; i++) {
int ndigits, nwords;
put_datapl(new, PTRLEN_LITERAL("0x"));
ndigits = strcspn(q, "/"); /* find / or end of string */
nwords = ndigits / 4;
/* now trim ndigits to remove leading zeros */
while (q[(ndigits - 1) ^ 3] == '0' && ndigits > 1)
ndigits--;
/* now move digits over to new string */
for (j = ndigits; j-- > 0 ;)
put_byte(new, q[j ^ 3]);
q += nwords * 4;
if (*q) {
q++; /* eat the slash */
put_byte(new, ','); /* add a comma */
}
}
/*
* Now _if_ this key matches, we'll enter it in the new
* format. If not, we'll assume something odd went
* wrong, and hyper-cautiously do nothing.
*/
if (!strcmp(new->s, key)) {
put_reg_sz(rkey, regname->s, new->s);
otherstr = strbuf_to_str(new);
} else {
strbuf_free(new);
}
}
sfree(oldstyle);
}
close_regkey(rkey);
int compare = otherstr ? strcmp(otherstr, key) : -1;
sfree(otherstr);
strbuf_free(regname);
if (!otherstr)
return 1; /* key does not exist in registry */
else if (compare)
return 2; /* key is different in registry */
else
return 0; /* key matched OK in registry */
}
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
bool have_ssh_host_key(const char *hostname, int port,
const char *keytype)
{
/*
Reorganise host key checking and confirmation. Previously, checking the host key against the persistent cache managed by the storage.h API was done as part of the seat_verify_ssh_host_key method, i.e. separately by each Seat. Now that check is done by verify_ssh_host_key(), which is a new function in ssh/common.c that centralises all the parts of host key checking that don't need an interactive prompt. It subsumes the previous verify_ssh_manual_host_key() that checked against the Conf, and it does the check against the storage API that each Seat was previously doing separately. If it can't confirm or definitively reject the host key by itself, _then_ it calls out to the Seat, once an interactive prompt is definitely needed. The main point of doing this is so that when SshProxy forwards a Seat call from the proxy SSH connection to the primary Seat, it won't print an announcement of which connection is involved unless it's actually going to do something interactive. (Not that we're printing those announcements _yet_ anyway, but this is a piece of groundwork that works towards doing so.) But while I'm at it, I've also taken the opportunity to clean things up a bit by renaming functions sensibly. Previously we had three very similarly named functions verify_ssh_manual_host_key(), SeatVtable's 'verify_ssh_host_key' method, and verify_host_key() in storage.h. Now the Seat method is called 'confirm' rather than 'verify' (since its job is now always to print an interactive prompt, so it looks more like the other confirm_foo methods), and the storage.h function is called check_stored_host_key(), which goes better with store_host_key and avoids having too many functions with similar names. And the 'manual' function is subsumed into the new centralised code, so there's now just *one* host key function with 'verify' in the name. Several functions are reindented in this commit. Best viewed with whitespace changes ignored.
2021-10-25 17:12:17 +00:00
* If we have a host key, check_stored_host_key will return 0 or 2.
* If we don't have one, it'll return 1.
*/
Reorganise host key checking and confirmation. Previously, checking the host key against the persistent cache managed by the storage.h API was done as part of the seat_verify_ssh_host_key method, i.e. separately by each Seat. Now that check is done by verify_ssh_host_key(), which is a new function in ssh/common.c that centralises all the parts of host key checking that don't need an interactive prompt. It subsumes the previous verify_ssh_manual_host_key() that checked against the Conf, and it does the check against the storage API that each Seat was previously doing separately. If it can't confirm or definitively reject the host key by itself, _then_ it calls out to the Seat, once an interactive prompt is definitely needed. The main point of doing this is so that when SshProxy forwards a Seat call from the proxy SSH connection to the primary Seat, it won't print an announcement of which connection is involved unless it's actually going to do something interactive. (Not that we're printing those announcements _yet_ anyway, but this is a piece of groundwork that works towards doing so.) But while I'm at it, I've also taken the opportunity to clean things up a bit by renaming functions sensibly. Previously we had three very similarly named functions verify_ssh_manual_host_key(), SeatVtable's 'verify_ssh_host_key' method, and verify_host_key() in storage.h. Now the Seat method is called 'confirm' rather than 'verify' (since its job is now always to print an interactive prompt, so it looks more like the other confirm_foo methods), and the storage.h function is called check_stored_host_key(), which goes better with store_host_key and avoids having too many functions with similar names. And the 'manual' function is subsumed into the new centralised code, so there's now just *one* host key function with 'verify' in the name. Several functions are reindented in this commit. Best viewed with whitespace changes ignored.
2021-10-25 17:12:17 +00:00
return check_stored_host_key(hostname, port, keytype, "") != 1;
}
void store_host_key(Seat *seat, const char *hostname, int port,
const char *keytype, const char *key)
{
strbuf *regname = strbuf_new();
hostkey_regname(regname, hostname, port, keytype);
2022-09-14 15:04:14 +00:00
HKEY rkey = create_regkey(HKEY_CURRENT_USER,
PUTTY_REG_POS "\\SshHostKeys");
if (rkey) {
put_reg_sz(rkey, regname->s, key);
close_regkey(rkey);
} /* else key does not exist in registry */
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;
2022-09-14 15:04:14 +00:00
if (!(key = open_regkey_ro(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);
2022-09-14 15:04:14 +00:00
HKEY rkey = open_regkey_ro(HKEY_CURRENT_USER, host_ca_key, sb->s);
strbuf_free(sb);
if (!rkey)
return NULL;
host_ca *hca = host_ca_new();
hca->name = dupstr(name);
DWORD val;
if ((s = get_reg_sz(rkey, "PublicKey")) != NULL)
hca->ca_public_key = base64_decode_sb(ptrlen_from_asciz(s));
if ((s = get_reg_sz(rkey, "Validity")) != NULL) {
hca->validity_expression = strbuf_to_str(
percent_decode_sb(ptrlen_from_asciz(s)));
} else if ((sb = get_reg_multi_sz(rkey, "MatchHosts")) != NULL) {
BinarySource src[1];
BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(sb));
CertExprBuilder *eb = cert_expr_builder_new();
const char *wc;
while (wc = get_asciz(src), !get_err(src))
cert_expr_builder_add(eb, wc);
hca->validity_expression = cert_expr_expression(eb);
cert_expr_builder_free(eb);
}
if (get_reg_dword(rkey, "PermitRSASHA1", &val))
hca->opts.permit_rsa_sha1 = val;
if (get_reg_dword(rkey, "PermitRSASHA256", &val))
hca->opts.permit_rsa_sha256 = val;
if (get_reg_dword(rkey, "PermitRSASHA512", &val))
hca->opts.permit_rsa_sha512 = val;
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);
2022-09-14 15:04:14 +00:00
HKEY rkey = create_regkey(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 *validity = percent_encode_sb(
ptrlen_from_asciz(hca->validity_expression), NULL);
put_reg_sz(rkey, "Validity", validity->s);
strbuf_free(validity);
put_reg_dword(rkey, "PermitRSASHA1", hca->opts.permit_rsa_sha1);
put_reg_dword(rkey, "PermitRSASHA256", hca->opts.permit_rsa_sha256);
put_reg_dword(rkey, "PermitRSASHA512", hca->opts.permit_rsa_sha512);
close_regkey(rkey);
return NULL;
}
char *host_ca_delete(const char *name)
{
2022-09-14 15:04:14 +00:00
HKEY rkey = open_regkey_rw(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.
*/
enum { DEL, OPEN_R, OPEN_W };
Convert a lot of 'int' variables to 'bool'. My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
static bool try_random_seed(char const *path, int action, HANDLE *ret)
{
if (action == DEL) {
if (!DeleteFile(path) && GetLastError() != ERROR_FILE_NOT_FOUND) {
nonfatal("Unable to delete '%s': %s", path,
win_strerror(GetLastError()));
}
*ret = INVALID_HANDLE_VALUE;
return false; /* so we'll do the next ones too */
}
*ret = CreateFile(path,
action == OPEN_W ? GENERIC_WRITE : GENERIC_READ,
action == OPEN_W ? 0 : (FILE_SHARE_READ |
FILE_SHARE_WRITE),
NULL,
action == OPEN_W ? CREATE_ALWAYS : OPEN_EXISTING,
action == OPEN_W ? FILE_ATTRIBUTE_NORMAL : 0,
NULL);
return (*ret != INVALID_HANDLE_VALUE);
}
static bool try_random_seed_and_free(char *path, int action, HANDLE *hout)
{
bool retd = try_random_seed(path, action, hout);
sfree(path);
return retd;
}
static HANDLE access_random_seed(int action)
{
HANDLE rethandle;
/*
* Iterate over a selection of possible random seed paths until
* we find one that works.
*
* We do this iteration separately for reading and writing,
* meaning that we will automatically migrate random seed files
* if a better location becomes available (by reading from the
* best location in which we actually find one, and then
* writing to the best location in which we can _create_ one).
*/
/*
* First, try the location specified by the user in the
* Registry, if any.
*/
{
2022-09-14 15:04:14 +00:00
HKEY rkey = open_regkey_ro(HKEY_CURRENT_USER, PUTTY_REG_POS);
if (rkey) {
char *regpath = get_reg_sz(rkey, "RandSeedFile");
close_regkey(rkey);
if (regpath) {
bool success = try_random_seed(regpath, action, &rethandle);
sfree(regpath);
if (success)
return rethandle;
}
}
}
/*
* Next, try the user's local Application Data directory,
* followed by their non-local one. This is found using the
* SHGetFolderPath function, which won't be present on all
* versions of Windows.
*/
if (!tried_shgetfolderpath) {
/* This is likely only to bear fruit on systems with IE5+
* installed, or WinMe/2K+. There is some faffing with
* SHFOLDER.DLL we could do to try to find an equivalent
* on older versions of Windows if we cared enough.
* However, the invocation below requires IE5+ anyway,
* so stuff that. */
shell32_module = load_system32_dll("shell32.dll");
GET_WINDOWS_FUNCTION(shell32_module, SHGetFolderPathA);
tried_shgetfolderpath = true;
}
if (p_SHGetFolderPathA) {
char profile[MAX_PATH + 1];
if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA,
NULL, SHGFP_TYPE_CURRENT, profile)) &&
try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"),
action, &rethandle))
return rethandle;
if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA,
NULL, SHGFP_TYPE_CURRENT, profile)) &&
try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"),
action, &rethandle))
return rethandle;
}
/*
* Failing that, try %HOMEDRIVE%%HOMEPATH% as a guess at the
* user's home directory.
*/
{
char drv[MAX_PATH], path[MAX_PATH];
DWORD drvlen = GetEnvironmentVariable("HOMEDRIVE", drv, sizeof(drv));
DWORD pathlen = GetEnvironmentVariable("HOMEPATH", path, sizeof(path));
/* We permit %HOMEDRIVE% to expand to an empty string, but if
* %HOMEPATH% does that, we abort the attempt. Same if either
* variable overflows its buffer. */
if (drvlen == 0)
drv[0] = '\0';
if (drvlen < lenof(drv) && pathlen < lenof(path) && pathlen > 0 &&
try_random_seed_and_free(
dupcat(drv, path, "\\PUTTY.RND"), action, &rethandle))
return rethandle;
}
/*
* And finally, fall back to C:\WINDOWS.
*/
{
char windir[MAX_PATH];
DWORD len = GetWindowsDirectory(windir, sizeof(windir));
if (len < lenof(windir) &&
try_random_seed_and_free(
dupcat(windir, "\\PUTTY.RND"), action, &rethandle))
return rethandle;
}
/*
* If even that failed, give up.
*/
return INVALID_HANDLE_VALUE;
}
void read_random_seed(noise_consumer_t consumer)
{
HANDLE seedf = access_random_seed(OPEN_R);
if (seedf != INVALID_HANDLE_VALUE) {
while (1) {
char buf[1024];
DWORD len;
if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)
consumer(buf, len);
else
break;
}
CloseHandle(seedf);
}
}
void write_random_seed(void *data, int len)
{
HANDLE seedf = access_random_seed(OPEN_W);
if (seedf != INVALID_HANDLE_VALUE) {
DWORD lenwritten;
WriteFile(seedf, data, len, &lenwritten, NULL);
CloseHandle(seedf);
}
}
/*
* Internal function supporting the jump list registry code. All the
* functions to add, remove and read the list have substantially
* similar content, so this is a generalisation of all of them which
* transforms the list in the registry by prepending 'add' (if
* non-null), removing 'rem' from what's left (if non-null), and
* returning the resulting concatenated list of strings in 'out' (if
* non-null).
*/
static int transform_jumplist_registry(
const char *add, const char *rem, char **out)
{
2022-09-14 15:04:14 +00:00
HKEY rkey = create_regkey(HKEY_CURRENT_USER, reg_jumplist_key);
if (!rkey)
return JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE;
/* Get current list of saved sessions in the registry. */
strbuf *oldlist = get_reg_multi_sz(rkey, reg_jumplist_value);
if (!oldlist) {
/* Start again with the empty list. */
oldlist = strbuf_new();
put_data(oldlist, "\0\0", 2);
}
/*
* Modify the list, if we're modifying.
*/
bool write_failure = false;
if (add || rem) {
BinarySource src[1];
BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(oldlist));
strbuf *newlist = strbuf_new();
/* First add the new item to the beginning of the list. */
if (add)
put_asciz(newlist, add);
/* Now add the existing list, taking care to leave out the removed
* item, if it was already in the existing list. */
while (true) {
const char *olditem = get_asciz(src);
if (get_err(src))
break;
if (!rem || strcmp(olditem, rem) != 0) {
/* Check if this is a valid session, otherwise don't add. */
settings_r *psettings_tmp = open_settings_r(olditem);
if (psettings_tmp != NULL) {
close_settings_r(psettings_tmp);
put_asciz(newlist, olditem);
}
}
}
/* Save the new list to the registry. */
write_failure = !put_reg_multi_sz(rkey, reg_jumplist_value, newlist);
strbuf_free(oldlist);
oldlist = newlist;
}
close_regkey(rkey);
if (out && !write_failure)
*out = strbuf_to_str(oldlist);
else
strbuf_free(oldlist);
if (write_failure)
return JUMPLISTREG_ERROR_VALUEWRITE_FAILURE;
else
return JUMPLISTREG_OK;
}
/* Adds a new entry to the jumplist entries in the registry. */
int add_to_jumplist_registry(const char *item)
{
return transform_jumplist_registry(item, item, NULL);
}
/* Removes an item from the jumplist entries in the registry. */
int remove_from_jumplist_registry(const char *item)
{
return transform_jumplist_registry(NULL, item, NULL);
}
/* Returns the jumplist entries from the registry. Caller must free
* the returned pointer. */
char *get_jumplist_registry_entries (void)
{
char *list_value;
if (transform_jumplist_registry(NULL,NULL,&list_value) != JUMPLISTREG_OK) {
list_value = snewn(2, char);
*list_value = '\0';
*(list_value + 1) = '\0';
}
return list_value;
}
/*
* Recursively delete a registry key and everything under it.
*/
static void registry_recursive_remove(HKEY key)
{
char *name;
DWORD i = 0;
while ((name = enum_regkey(key, i)) != NULL) {
2022-09-14 15:04:14 +00:00
HKEY subkey = open_regkey_rw(key, name);
if (subkey) {
registry_recursive_remove(subkey);
close_regkey(subkey);
}
del_regkey(key, name);
sfree(name);
}
}
void cleanup_all(void)
{
/* ------------------------------------------------------------
* Wipe out the random seed file, in all of its possible
* locations.
*/
access_random_seed(DEL);
/* ------------------------------------------------------------
* Ask Windows to delete any jump list information associated
* with this installation of PuTTY.
*/
clear_jumplist();
/* ------------------------------------------------------------
* Destroy all registry information associated with PuTTY.
*/
/*
* Open the main PuTTY registry key and remove everything in it.
*/
2022-09-14 15:04:14 +00:00
HKEY key = open_regkey_rw(HKEY_CURRENT_USER, PUTTY_REG_POS);
if (key) {
registry_recursive_remove(key);
close_regkey(key);
}
/*
* Now open the parent key and remove the PuTTY main key. Once
* we've done that, see if the parent key has any other
* children.
*/
2022-09-14 15:04:14 +00:00
if ((key = open_regkey_rw(HKEY_CURRENT_USER, PUTTY_REG_PARENT)) != NULL) {
del_regkey(key, PUTTY_REG_PARENT_CHILD);
char *name = enum_regkey(key, 0);
close_regkey(key);
/*
* If the parent key had no other children, we must delete
* it in its turn. That means opening the _grandparent_
* key.
*/
if (name) {
sfree(name);
} else {
2022-09-14 15:04:14 +00:00
if ((key = open_regkey_rw(HKEY_CURRENT_USER,
PUTTY_REG_GPARENT)) != NULL) {
del_regkey(key, PUTTY_REG_GPARENT_CHILD);
close_regkey(key);
}
}
}
/*
* Now we're done.
*/
}