1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00

Rework mungestr() and unmungestr().

For a start, they now have different names on Windows and Unix,
reflecting their different roles: on Windows they apply escaping to
any string that's going to be used as a registry key (be it a session
name, or a host name for host key storage), whereas on Unix they're
for constructing saved-session file names in particular (and also
handle the special case of filling in "Default Settings" for NULL).

Also, they now produce output by writing to a strbuf, which simplifies
a lot of the call sites. In particular, the strbuf output idiom is
passed on to enum_settings_next, which is especially nice because its
only actual caller was doing an ad-hoc realloc loop that I can now get
rid of completely.

Thirdly, on Windows they're centralised into winmisc.c instead of
living in winstore.c, because that way Pageant can use the unescape
function too. (It was spotting the duplication there that made me
think of doing this in the first place, but once I'd started, I had to
keep unravelling the thread...)
This commit is contained in:
Simon Tatham 2018-11-03 08:58:41 +00:00
parent 9248f5c994
commit c089827ea0
7 changed files with 123 additions and 173 deletions

View File

@ -1268,32 +1268,20 @@ static int sessioncmp(const void *av, const void *bv)
void get_sesslist(struct sesslist *list, bool allocate)
{
char otherbuf[2048];
int buflen, bufsize, i;
char *p, *ret;
int i;
char *p;
settings_e *handle;
if (allocate) {
strbuf *sb = strbuf_new();
buflen = bufsize = 0;
list->buffer = NULL;
if ((handle = enum_settings_start()) != NULL) {
do {
ret = enum_settings_next(handle, otherbuf, sizeof(otherbuf));
if (ret) {
int len = strlen(otherbuf) + 1;
if (bufsize < buflen + len) {
bufsize = buflen + len + 2048;
list->buffer = sresize(list->buffer, bufsize, char);
}
strcpy(list->buffer + buflen, otherbuf);
buflen += strlen(list->buffer + buflen) + 1;
}
} while (ret);
while (enum_settings_next(handle, sb))
put_byte(sb, '\0');
enum_settings_finish(handle);
}
list->buffer = sresize(list->buffer, buflen + 1, char);
list->buffer[buflen] = '\0';
put_byte(sb, '\0');
list->buffer = strbuf_to_str(sb);
/*
* Now set up the list of sessions. Note that "Default

View File

@ -69,7 +69,7 @@ void del_settings(const char *sessionname);
* Enumerate all saved sessions.
*/
settings_e *enum_settings_start(void);
char *enum_settings_next(settings_e *handle, char *buffer, int buflen);
bool enum_settings_next(settings_e *handle, strbuf *out);
void enum_settings_finish(settings_e *handle);
/* ----------------------------------------------------------------------

View File

@ -33,15 +33,11 @@ enum {
static const char hex[16] = "0123456789ABCDEF";
static char *mungestr(const char *in)
static void make_session_filename(const char *in, strbuf *out)
{
char *out, *ret;
if (!in || !*in)
in = "Default Settings";
ret = out = snewn(3*strlen(in)+1, char);
while (*in) {
/*
* There are remarkably few punctuation characters that
@ -54,21 +50,17 @@ static char *mungestr(const char *in)
!(*in >= '0' && *in <= '9') &&
!(*in >= 'A' && *in <= 'Z') &&
!(*in >= 'a' && *in <= 'z')) {
*out++ = '%';
*out++ = hex[((unsigned char) *in) >> 4];
*out++ = hex[((unsigned char) *in) & 15];
put_byte(out, '%');
put_byte(out, hex[((unsigned char) *in) >> 4]);
put_byte(out, hex[((unsigned char) *in) & 15]);
} else
*out++ = *in;
put_byte(out, *in);
in++;
}
*out = '\0';
return ret;
}
static char *unmungestr(const char *in)
static void decode_session_filename(const char *in, strbuf *out)
{
char *out, *ret;
out = ret = snewn(strlen(in)+1, char);
while (*in) {
if (*in == '%' && in[1] && in[2]) {
int i, j;
@ -78,14 +70,12 @@ static char *unmungestr(const char *in)
j = in[2] - '0';
j -= (j > 9 ? 7 : 0);
*out++ = (i << 4) + j;
put_byte(out, (i << 4) + j);
in += 3;
} else {
*out++ = *in++;
put_byte(out, *in++);
}
}
*out = '\0';
return ret;
}
static char *make_filename(int index, const char *subname)
@ -181,12 +171,12 @@ static char *make_filename(int index, const char *subname)
return ret;
}
if (index == INDEX_SESSION) {
char *munged = mungestr(subname);
strbuf *sb = strbuf_new();
tmp = make_filename(INDEX_SESSIONDIR, NULL);
ret = dupprintf("%s/%s", tmp, munged);
strbuf_catf(sb, "%s/", tmp);
sfree(tmp);
sfree(munged);
return ret;
make_session_filename(subname, sb);
return strbuf_to_str(sb);
}
if (index == INDEX_HOSTKEYS) {
env = getenv("PUTTYSSHHOSTKEYS");
@ -554,13 +544,12 @@ settings_e *enum_settings_start(void)
return toret;
}
char *enum_settings_next(settings_e *handle, char *buffer, int buflen)
bool enum_settings_next(settings_e *handle, strbuf *out)
{
struct dirent *de;
struct stat st;
char *fullpath;
int maxlen, thislen, len;
char *unmunged;
if (!handle->dp)
return NULL;
@ -581,16 +570,13 @@ char *enum_settings_next(settings_e *handle, char *buffer, int buflen)
if (stat(fullpath, &st) < 0 || !S_ISREG(st.st_mode))
continue; /* try another one */
unmunged = unmungestr(de->d_name);
strncpy(buffer, unmunged, buflen);
buffer[buflen-1] = '\0';
sfree(unmunged);
decode_session_filename(de->d_name, out);
sfree(fullpath);
return buffer;
return true;
}
sfree(fullpath);
return NULL;
return false;
}
void enum_settings_finish(settings_e *handle)

View File

@ -625,3 +625,41 @@ bool open_for_write_would_lose_data(const Filename *fn)
}
return true;
}
void escape_registry_key(const char *in, strbuf *out)
{
bool candot = false;
static const char hex[16] = "0123456789ABCDEF";
while (*in) {
if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
*in == '%' || *in < ' ' || *in > '~' || (*in == '.'
&& !candot)) {
put_byte(out, '%');
put_byte(out, hex[((unsigned char) *in) >> 4]);
put_byte(out, hex[((unsigned char) *in) & 15]);
} else
put_byte(out, *in);
in++;
candot = true;
}
}
void unescape_registry_key(const char *in, strbuf *out)
{
while (*in) {
if (*in == '%' && in[1] && in[2]) {
int i, j;
i = in[1] - '0';
i -= (i > 9 ? 7 : 0);
j = in[2] - '0';
j -= (j > 9 ? 7 : 0);
put_byte(out, (i << 4) + j);
in += 3;
} else {
put_byte(out, *in++);
}
}
}

View File

@ -87,32 +87,6 @@ void modalfatalbox(const char *fmt, ...)
exit(1);
}
/* Un-munge session names out of the registry. */
static void unmungestr(char *in, char *out, int outlen)
{
while (*in) {
if (*in == '%' && in[1] && in[2]) {
int i, j;
i = in[1] - '0';
i -= (i > 9 ? 7 : 0);
j = in[2] - '0';
j -= (j > 9 ? 7 : 0);
*out++ = (i << 4) + j;
if (!--outlen)
return;
in += 3;
} else {
*out++ = *in++;
if (!--outlen)
return;
}
}
*out = '\0';
return;
}
/* Stubs needed to link against misc.c */
void queue_idempotent_callback(IdempotentCallback *ic) { assert(0); }
@ -687,6 +661,7 @@ static void update_sessions(void)
HKEY hkey;
TCHAR buf[MAX_PATH + 1];
MENUITEMINFO mii;
strbuf *sb;
int index_key, index_menu;
@ -704,22 +679,25 @@ static void update_sessions(void)
index_key = 0;
index_menu = 0;
sb = strbuf_new();
while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
TCHAR session_name[MAX_PATH + 1];
unmungestr(buf, session_name, MAX_PATH);
if(strcmp(buf, PUTTY_DEFAULT) != 0) {
sb->len = 0;
unescape_registry_key(buf, sb);
memset(&mii, 0, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
mii.fType = MFT_STRING;
mii.fState = MFS_ENABLED;
mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
mii.dwTypeData = session_name;
mii.dwTypeData = sb->s;
InsertMenuItem(session_menu, index_menu, true, &mii);
index_menu++;
}
index_key++;
}
strbuf_free(sb);
RegCloseKey(hkey);

View File

@ -22,58 +22,11 @@ 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 hex[16] = "0123456789ABCDEF";
static bool tried_shgetfolderpath = false;
static HMODULE shell32_module = NULL;
DECL_WINDOWS_FUNCTION(static, HRESULT, SHGetFolderPathA,
(HWND, int, HANDLE, DWORD, LPSTR));
static void mungestr(const char *in, char *out)
{
bool candot = false;
while (*in) {
if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
*in == '%' || *in < ' ' || *in > '~' || (*in == '.'
&& !candot)) {
*out++ = '%';
*out++ = hex[((unsigned char) *in) >> 4];
*out++ = hex[((unsigned char) *in) & 15];
} else
*out++ = *in;
in++;
candot = true;
}
*out = '\0';
return;
}
static void unmungestr(const char *in, char *out, int outlen)
{
while (*in) {
if (*in == '%' && in[1] && in[2]) {
int i, j;
i = in[1] - '0';
i -= (i > 9 ? 7 : 0);
j = in[2] - '0';
j -= (j > 9 ? 7 : 0);
*out++ = (i << 4) + j;
if (!--outlen)
return;
in += 3;
} else {
*out++ = *in++;
if (!--outlen)
return;
}
}
*out = '\0';
return;
}
struct settings_w {
HKEY sesskey;
};
@ -82,32 +35,32 @@ settings_w *open_settings_w(const char *sessionname, char **errmsg)
{
HKEY subkey1, sesskey;
int ret;
char *p;
strbuf *sb;
*errmsg = NULL;
if (!sessionname || !*sessionname)
sessionname = "Default Settings";
p = snewn(3 * strlen(sessionname) + 1, char);
mungestr(sessionname, p);
sb = strbuf_new();
escape_registry_key(sessionname, sb);
ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1);
if (ret != ERROR_SUCCESS) {
sfree(p);
strbuf_free(sb);
*errmsg = dupprintf("Unable to create registry key\n"
"HKEY_CURRENT_USER\\%s", puttystr);
return NULL;
}
ret = RegCreateKey(subkey1, p, &sesskey);
ret = RegCreateKey(subkey1, sb->s, &sesskey);
RegCloseKey(subkey1);
if (ret != ERROR_SUCCESS) {
*errmsg = dupprintf("Unable to create registry key\n"
"HKEY_CURRENT_USER\\%s\\%s", puttystr, p);
sfree(p);
"HKEY_CURRENT_USER\\%s\\%s", puttystr, sb->s);
strbuf_free(sb);
return NULL;
}
sfree(p);
strbuf_free(sb);
settings_w *toret = snew(settings_w);
toret->sesskey = sesskey;
@ -141,24 +94,24 @@ struct settings_r {
settings_r *open_settings_r(const char *sessionname)
{
HKEY subkey1, sesskey;
char *p;
strbuf *sb;
if (!sessionname || !*sessionname)
sessionname = "Default Settings";
p = snewn(3 * strlen(sessionname) + 1, char);
mungestr(sessionname, p);
sb = strbuf_new();
escape_registry_key(sessionname, sb);
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) {
sesskey = NULL;
} else {
if (RegOpenKey(subkey1, p, &sesskey) != ERROR_SUCCESS) {
if (RegOpenKey(subkey1, sb->s, &sesskey) != ERROR_SUCCESS) {
sesskey = NULL;
}
RegCloseKey(subkey1);
}
sfree(p);
strbuf_free(sb);
settings_r *toret = snew(settings_r);
toret->sesskey = sesskey;
@ -291,15 +244,15 @@ void close_settings_r(settings_r *handle)
void del_settings(const char *sessionname)
{
HKEY subkey1;
char *p;
strbuf *sb;
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS)
return;
p = snewn(3 * strlen(sessionname) + 1, char);
mungestr(sessionname, p);
RegDeleteKey(subkey1, p);
sfree(p);
sb = strbuf_new();
escape_registry_key(sessionname, sb);
RegDeleteKey(subkey1, sb->s);
strbuf_free(sb);
RegCloseKey(subkey1);
@ -328,18 +281,27 @@ settings_e *enum_settings_start(void)
return ret;
}
char *enum_settings_next(settings_e *e, char *buffer, int buflen)
bool enum_settings_next(settings_e *e, strbuf *sb)
{
char *otherbuf;
otherbuf = snewn(3 * buflen, char);
if (RegEnumKey(e->key, e->i++, otherbuf, 3 * buflen) == ERROR_SUCCESS) {
unmungestr(otherbuf, buffer, buflen);
sfree(otherbuf);
return buffer;
} else {
sfree(otherbuf);
return NULL;
size_t regbuf_size = 256;
char *regbuf = snewn(regbuf_size, char);
bool success;
while (1) {
DWORD retd = RegEnumKey(e->key, e->i++, regbuf, regbuf_size);
if (retd != ERROR_MORE_DATA) {
success = (retd == ERROR_SUCCESS);
break;
}
regbuf_size = regbuf_size * 5 / 4 + 256;
regbuf = sresize(regbuf, regbuf_size, char);
}
if (success)
unescape_registry_key(regbuf, sb);
sfree(regbuf);
return success;
}
void enum_settings_finish(settings_e *e)
@ -348,21 +310,18 @@ void enum_settings_finish(settings_e *e)
sfree(e);
}
static void hostkey_regname(char *buffer, const char *hostname,
static void hostkey_regname(strbuf *sb, const char *hostname,
int port, const char *keytype)
{
int len;
strcpy(buffer, keytype);
strcat(buffer, "@");
len = strlen(buffer);
len += sprintf(buffer + len, "%d:", port);
mungestr(hostname, buffer + strlen(buffer));
strbuf_catf(sb, "%s@%d:", keytype, port);
escape_registry_key(hostname, sb);
}
int verify_host_key(const char *hostname, int port,
const char *keytype, const char *key)
{
char *otherstr, *regname;
char *otherstr;
strbuf *regname;
int len;
HKEY rkey;
DWORD readlen;
@ -375,19 +334,18 @@ int verify_host_key(const char *hostname, int port,
* Now read a saved key in from the registry and see what it
* says.
*/
regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char);
regname = strbuf_new();
hostkey_regname(regname, hostname, port, keytype);
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
&rkey) != ERROR_SUCCESS) {
sfree(regname);
strbuf_free(regname);
return 1; /* key does not exist in registry */
}
readlen = len;
otherstr = snewn(len, char);
ret = RegQueryValueEx(rkey, regname, NULL,
ret = RegQueryValueEx(rkey, regname->s, NULL,
&type, (BYTE *)otherstr, &readlen);
if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&
@ -397,7 +355,7 @@ int verify_host_key(const char *hostname, int port,
* another trick, which is to look up the _old_ key format
* under just the hostname and translate that.
*/
char *justhost = regname + 1 + strcspn(regname, ":");
char *justhost = regname->s + 1 + strcspn(regname->s, ":");
char *oldstyle = snewn(len + 10, char); /* safety margin */
readlen = len;
ret = RegQueryValueEx(rkey, justhost, NULL, &type,
@ -447,7 +405,7 @@ int verify_host_key(const char *hostname, int port,
* wrong, and hyper-cautiously do nothing.
*/
if (!strcmp(otherstr, key))
RegSetValueEx(rkey, regname, 0, REG_SZ, (BYTE *)otherstr,
RegSetValueEx(rkey, regname->s, 0, REG_SZ, (BYTE *)otherstr,
strlen(otherstr) + 1);
}
@ -459,7 +417,7 @@ int verify_host_key(const char *hostname, int port,
compare = strcmp(otherstr, key);
sfree(otherstr);
sfree(regname);
strbuf_free(regname);
if (ret == ERROR_MORE_DATA ||
(ret == ERROR_SUCCESS && type == REG_SZ && compare))
@ -483,16 +441,16 @@ bool have_ssh_host_key(const char *hostname, int port,
void store_host_key(const char *hostname, int port,
const char *keytype, const char *key)
{
char *regname;
strbuf *regname;
HKEY rkey;
regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char);
regname = strbuf_new();
hostkey_regname(regname, hostname, port, keytype);
if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
&rkey) == ERROR_SUCCESS) {
RegSetValueEx(rkey, regname, 0, REG_SZ, (BYTE *)key, strlen(key) + 1);
RegSetValueEx(rkey, regname->s, 0, REG_SZ,
(BYTE *)key, strlen(key) + 1);
RegCloseKey(rkey);
} /* else key does not exist in registry */

View File

@ -544,6 +544,8 @@ HMODULE load_system32_dll(const char *libname);
const char *win_strerror(int error);
void restrict_process_acl(void);
GLOBAL bool restricted_acl;
void escape_registry_key(const char *in, strbuf *out);
void unescape_registry_key(const char *in, strbuf *out);
/* A few pieces of up-to-date Windows API definition needed for older
* compilers. */