From 802b4edf4d59a04e903fc8ef5342e25b6a566dc6 Mon Sep 17 00:00:00 2001
From: Christopher Odenbach
Date: Mon, 3 Apr 2017 21:30:18 +0200
Subject: [PATCH 001/607] Fixed GSSAPI authentication.
gssapi32.dll from MIT Kerberos as well as from Heimdal both load
further DLLs from their installation directories.
[SGT: I polished the original patch a bit, in particular replacing
manual memory allocation with dup_mb_to_wc. This required a Recipe
change to link miscucs.c and winucs.c into more of the tools.]
---
Recipe | 6 +++---
windows/wingss.c | 51 +++++++++++++++++++++++++++++++++++++++++++---
windows/winmisc.c | 6 ++++--
windows/winstuff.h | 15 ++++++++++++++
4 files changed, 70 insertions(+), 8 deletions(-)
diff --git a/Recipe b/Recipe
index e39faa1b..54e00636 100644
--- a/Recipe
+++ b/Recipe
@@ -235,8 +235,8 @@ TERMINAL = terminal wcwidth ldiscucs logging tree234 minibidi
+ config dialog conf
# GUI front end and terminal emulator (putty, puttytel).
-GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint
- + winutils wincfg sercfg winhelp winjump miscucs
+GUITERM = TERMINAL window windlg winctrls sizetip winprint winutils
+ + wincfg sercfg winhelp winjump
# Same thing on Unix.
UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing callback miscucs
@@ -262,7 +262,7 @@ SFTP = sftp int64 logging
# Pageant or PuTTYgen).
MISC = timing callback misc version settings tree234 proxy conf be_misc
WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy
- + wintime winhsock errsock winsecur
+ + wintime winhsock errsock winsecur winucs miscucs
UXMISC = MISC uxstore uxsel uxnet uxpeer cmdline uxmisc uxproxy time
# import.c and dependencies, for PuTTYgen-like utilities that have to
diff --git a/windows/wingss.c b/windows/wingss.c
index 6aaa20cf..f7cc43eb 100644
--- a/windows/wingss.c
+++ b/windows/wingss.c
@@ -49,6 +49,9 @@ DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
MakeSignature,
(PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
+DECL_WINDOWS_FUNCTION(static, DLL_DIRECTORY_COOKIE,
+ AddDllDirectory,
+ (PCWSTR));
typedef struct winSsh_gss_ctx {
unsigned long maj_stat;
@@ -72,6 +75,11 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
HKEY regkey;
struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);
char *path;
+ static HMODULE kernel32_module;
+ if (!kernel32_module) {
+ kernel32_module = load_system32_dll("kernel32.dll");
+ }
+ GET_WINDOWS_FUNCTION(kernel32_module, AddDllDirectory);
list->libraries = snewn(3, struct ssh_gss_library);
list->nlibraries = 0;
@@ -93,8 +101,20 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
ret = RegQueryValueEx(regkey, "InstallDir", NULL,
&type, (LPBYTE)buffer, &size);
if (ret == ERROR_SUCCESS && type == REG_SZ) {
- strcat(buffer, "\\bin\\gssapi32.dll");
- module = LoadLibrary(buffer);
+ strcat (buffer, "\\bin");
+ if(p_AddDllDirectory) {
+ /* Add MIT Kerberos' path to the DLL search path,
+ * it loads its own DLLs further down the road */
+ wchar_t *dllPath =
+ dup_mb_to_wc(DEFAULT_CODEPAGE, 0, buffer);
+ p_AddDllDirectory(dllPath);
+ sfree(dllPath);
+ }
+ strcat (buffer, "\\gssapi32.dll");
+ module = LoadLibraryEx (buffer, NULL,
+ LOAD_LIBRARY_SEARCH_SYSTEM32 |
+ LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
+ LOAD_LIBRARY_SEARCH_USER_DIRS);
}
sfree(buffer);
}
@@ -152,7 +172,32 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
module = NULL;
path = conf_get_filename(conf, CONF_ssh_gss_custom)->path;
if (*path) {
- module = LoadLibrary(path);
+ if(p_AddDllDirectory) {
+ /* Add the custom directory as well in case it chainloads
+ * some other DLLs (e.g a non-installed MIT Kerberos
+ * instance) */
+ int pathlen = strlen(path);
+
+ while (pathlen > 0 && path[pathlen-1] != ':' &&
+ path[pathlen-1] != '\\')
+ pathlen--;
+
+ if (pathlen > 0 && path[pathlen-1] != '\\')
+ pathlen--;
+
+ if (pathlen > 0) {
+ char *dirpath = dupprintf("%.*s", pathlen, path);
+ wchar_t *dllPath = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, dirpath);
+ p_AddDllDirectory(dllPath);
+ sfree(dllPath);
+ sfree(dirpath);
+ }
+ }
+
+ module = LoadLibraryEx(path, NULL,
+ LOAD_LIBRARY_SEARCH_SYSTEM32 |
+ LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
+ LOAD_LIBRARY_SEARCH_USER_DIRS);
}
if (module) {
struct ssh_gss_library *lib =
diff --git a/windows/winmisc.c b/windows/winmisc.c
index 11e2ca0f..384dc5ee 100644
--- a/windows/winmisc.c
+++ b/windows/winmisc.c
@@ -176,8 +176,10 @@ void dll_hijacking_protection(void)
}
if (p_SetDefaultDllDirectories) {
- /* LOAD_LIBRARY_SEARCH_SYSTEM32 only */
- p_SetDefaultDllDirectories(0x800);
+ /* LOAD_LIBRARY_SEARCH_SYSTEM32 and explicitly specified
+ * directories only */
+ p_SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 |
+ LOAD_LIBRARY_SEARCH_USER_DIRS);
}
}
diff --git a/windows/winstuff.h b/windows/winstuff.h
index c1918d4a..87706f32 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -512,6 +512,21 @@ const char *win_strerror(int error);
void restrict_process_acl(void);
GLOBAL int restricted_acl;
+/* A few pieces of up-to-date Windows API definition needed for older
+ * compilers. */
+#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
+#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
+#endif
+#ifndef LOAD_LIBRARY_SEARCH_USER_DIRS
+#define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400
+#endif
+#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
+#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
+#endif
+#if _MSC_VER < 1400
+typedef PVOID DLL_DIRECTORY_COOKIE;
+#endif
+
/*
* Exports from sizetip.c.
*/
From 3ff3be38822d9735f065bc0fb41fecc72721e78c Mon Sep 17 00:00:00 2001
From: Christopher Odenbach
Date: Tue, 11 Apr 2017 14:04:00 +0200
Subject: [PATCH 002/607] Fix loading of SSPICLI.DLL by SECUR32.DLL.
If MIT Kerberos is installed, then using GetProcAddress to extract
GetUserNameExA() from secur32.dll causes Windows to implicitly load
sspicli.dll in turn - and it does it in a search-path-unclean way.
If we load it in our own way before that happens, then Windows doesn't
need to load it again and won't do so wrongly.
[SGT: tidied up commit message from original patch]
---
windows/winmisc.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/windows/winmisc.c b/windows/winmisc.c
index 384dc5ee..308f0ea5 100644
--- a/windows/winmisc.c
+++ b/windows/winmisc.c
@@ -101,6 +101,11 @@ char *get_username(void)
if (!tried_usernameex) {
/* Not available on Win9x, so load dynamically */
HMODULE secur32 = load_system32_dll("secur32.dll");
+ /* If MIT Kerberos is installed, the following call to
+ GET_WINDOWS_FUNCTION makes Windows implicitly load
+ sspicli.dll WITHOUT proper path sanitizing, so better
+ load it properly before */
+ HMODULE sspicli = load_system32_dll("sspicli.dll");
GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
tried_usernameex = TRUE;
}
From 49fb598b0e78d09d6a2a42679ee0649df482090e Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 11 Apr 2017 18:56:55 +0100
Subject: [PATCH 003/607] Add automatic type-checking to GET_WINDOWS_FUNCTION.
This gives me an extra safety-check against having mistyped one of the
function prototypes that we load at run time from DLLs: we verify that
the typedef we defined based on the prototype in our source code
matches the type of the real function as declared in the Windows
headers.
This was an idea I had while adding a pile of further functions using
this mechanism. It didn't catch any errors (either in the new
functions or in the existing collection), but that's no reason not to
keep it anyway now that I've thought of it!
In VS2015, this automated type-check works for most functions, but a
couple manage to break it. SetCurrentProcessExplicitAppUserModelID in
winjump.c can't be type-checked, because including where
that function is declared would also bring in a load of other stuff
that conflicts with the painful manual COM declarations in winjump.c.
(That stuff could probably be removed now we're on an up-to-date
Visual Studio, on the other hand, but that's a separate chore.) And
gai_strerror, used in winnet.c, does _have_ an implementation in a
DLL, but the header files like to provide an inline version with a
different calling convention, which defeats this error-checking trick.
And in the older VS2003 that we still precautionarily build with,
several more type-checks have to be #ifdeffed out because the
functions they check against just aren't there at all.
---
windows/wingss.c | 5 +++++
windows/winhsock.c | 10 +++++++++-
windows/winjump.c | 8 +++++++-
windows/winmisc.c | 7 +++++++
windows/winnet.c | 14 ++++++++++++--
windows/winstuff.h | 21 +++++++++++++++------
6 files changed, 55 insertions(+), 10 deletions(-)
diff --git a/windows/wingss.c b/windows/wingss.c
index f7cc43eb..ef056167 100644
--- a/windows/wingss.c
+++ b/windows/wingss.c
@@ -79,7 +79,12 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
}
+#if defined _MSC_VER && _MSC_VER < 1900
+ /* Omit the type-check because older MSVCs don't have this function */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, AddDllDirectory);
+#else
GET_WINDOWS_FUNCTION(kernel32_module, AddDllDirectory);
+#endif
list->libraries = snewn(3, struct ssh_gss_library);
list->nlibraries = 0;
diff --git a/windows/winhsock.c b/windows/winhsock.c
index 1a4ee4d7..e5f0fa4f 100644
--- a/windows/winhsock.c
+++ b/windows/winhsock.c
@@ -282,7 +282,15 @@ static char *sk_handle_peer_info(Socket s)
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
- GET_WINDOWS_FUNCTION(kernel32_module, GetNamedPipeClientProcessId);
+#if defined _MSC_VER && _MSC_VER < 1900
+ /* For older Visual Studio, this function isn't available in
+ * the header files to type-check */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(
+ kernel32_module, GetNamedPipeClientProcessId);
+#else
+ GET_WINDOWS_FUNCTION(
+ kernel32_module, GetNamedPipeClientProcessId);
+#endif
}
/*
diff --git a/windows/winjump.c b/windows/winjump.c
index 85963a32..d0dea863 100644
--- a/windows/winjump.c
+++ b/windows/winjump.c
@@ -728,7 +728,13 @@ BOOL set_explicit_app_user_model_id()
if (!shell32_module)
{
shell32_module = load_system32_dll("Shell32.dll");
- GET_WINDOWS_FUNCTION(shell32_module, SetCurrentProcessExplicitAppUserModelID);
+ /*
+ * We can't typecheck this function here, because it's defined
+ * in , which we're not including due to clashes
+ * with all the manual-COM machinery above.
+ */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(
+ shell32_module, SetCurrentProcessExplicitAppUserModelID);
}
if (p_SetCurrentProcessExplicitAppUserModelID)
diff --git a/windows/winmisc.c b/windows/winmisc.c
index 308f0ea5..85fa3c95 100644
--- a/windows/winmisc.c
+++ b/windows/winmisc.c
@@ -177,7 +177,14 @@ void dll_hijacking_protection(void)
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
+#if defined _MSC_VER && _MSC_VER < 1900
+ /* For older Visual Studio, this function isn't available in
+ * the header files to type-check */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(
+ kernel32_module, SetDefaultDllDirectories);
+#else
GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
+#endif
}
if (p_SetDefaultDllDirectories) {
diff --git a/windows/winnet.c b/windows/winnet.c
index 98753237..7ceca486 100644
--- a/windows/winnet.c
+++ b/windows/winnet.c
@@ -268,7 +268,10 @@ void sk_init(void)
GET_WINDOWS_FUNCTION(winsock_module, getaddrinfo);
GET_WINDOWS_FUNCTION(winsock_module, freeaddrinfo);
GET_WINDOWS_FUNCTION(winsock_module, getnameinfo);
- GET_WINDOWS_FUNCTION(winsock_module, gai_strerror);
+ /* This function would fail its type-check if we did one,
+ * because the VS header file provides an inline definition
+ * which is __cdecl instead of WINAPI. */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gai_strerror);
} else {
/* Fall back to wship6.dll for Windows 2000 */
wship6_module = load_system32_dll("wship6.dll");
@@ -279,7 +282,8 @@ void sk_init(void)
GET_WINDOWS_FUNCTION(wship6_module, getaddrinfo);
GET_WINDOWS_FUNCTION(wship6_module, freeaddrinfo);
GET_WINDOWS_FUNCTION(wship6_module, getnameinfo);
- GET_WINDOWS_FUNCTION(wship6_module, gai_strerror);
+ /* See comment above about type check */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gai_strerror);
} else {
#ifdef NET_SETUP_DIAGNOSTICS
logevent(NULL, "No IPv6 support detected");
@@ -310,7 +314,13 @@ void sk_init(void)
GET_WINDOWS_FUNCTION(winsock_module, getservbyname);
GET_WINDOWS_FUNCTION(winsock_module, inet_addr);
GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa);
+#if defined _MSC_VER && _MSC_VER < 1900
+ /* Older Visual Studio doesn't know about this function at all, so
+ * can't type-check it */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, inet_ntop);
+#else
GET_WINDOWS_FUNCTION(winsock_module, inet_ntop);
+#endif
GET_WINDOWS_FUNCTION(winsock_module, connect);
GET_WINDOWS_FUNCTION(winsock_module, bind);
GET_WINDOWS_FUNCTION(winsock_module, setsockopt);
diff --git a/windows/winstuff.h b/windows/winstuff.h
index 87706f32..007889e4 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -128,15 +128,24 @@ struct FontSpec *fontspec_new(const char *name,
*
* (DECL_WINDOWS_FUNCTION works with both these variants.)
*/
-#define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \
- typedef rettype (WINAPI *t_##name) params; \
+#define TYPECHECK(to_check, to_return) \
+ (sizeof(to_check) ? to_return : to_return)
+#define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \
+ typedef rettype (WINAPI *t_##name) params; \
linkage t_##name p_##name
#define STR1(x) #x
#define STR(x) STR1(x)
-#define GET_WINDOWS_FUNCTION_PP(module, name) \
- (p_##name = module ? (t_##name) GetProcAddress(module, STR(name)) : NULL)
-#define GET_WINDOWS_FUNCTION(module, name) \
- (p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL)
+#define GET_WINDOWS_FUNCTION_PP(module, name) \
+ TYPECHECK((t_##name)NULL == name, \
+ (p_##name = module ? \
+ (t_##name) GetProcAddress(module, STR(name)) : NULL))
+#define GET_WINDOWS_FUNCTION(module, name) \
+ TYPECHECK((t_##name)NULL == name, \
+ (p_##name = module ? \
+ (t_##name) GetProcAddress(module, #name) : NULL))
+#define GET_WINDOWS_FUNCTION_NO_TYPECHECK(module, name) \
+ (p_##name = module ? \
+ (t_##name) GetProcAddress(module, #name) : NULL)
/*
* Global variables. Most modules declare these `extern', but
From 89fff90de738025d338fa5d06b6ff3b589c33c9c Mon Sep 17 00:00:00 2001
From: klemens
Date: Sat, 15 Apr 2017 10:06:22 +0200
Subject: [PATCH 004/607] Spelling fixes (just in comments).
As found by a bot ( http://www.misfix.org,
https://github.com/ka7/misspell_fixer ).
---
doc/config.but | 2 +-
minibidi.c | 4 ++--
mkfiles.pl | 2 +-
pageant.c | 2 +-
settings.c | 2 +-
terminal.c | 2 +-
windows/window.c | 4 ++--
7 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/doc/config.but b/doc/config.but
index eb96fc17..269dbdc1 100644
--- a/doc/config.but
+++ b/doc/config.but
@@ -2507,7 +2507,7 @@ used:
Disabling data-based rekeys entirely is a bad idea. The \i{integrity},
and to a lesser extent, \i{confidentiality} of the SSH-2 protocol depend
-in part on rekeys occuring before a 32-bit packet sequence number
+in part on rekeys occurring before a 32-bit packet sequence number
wraps around. Unlike time-based rekeys, data-based rekeys won't occur
when the SSH connection is idle, so they shouldn't cause the same
problems. The SSH-1 protocol, incidentally, has even weaker integrity
diff --git a/minibidi.c b/minibidi.c
index 6c062116..8d78594d 100644
--- a/minibidi.c
+++ b/minibidi.c
@@ -3,7 +3,7 @@
* ------------
* Description:
* ------------
- * This is an implemention of Unicode's Bidirectional Algorithm
+ * This is an implementation of Unicode's Bidirectional Algorithm
* (known as UAX #9).
*
* http://www.unicode.org/reports/tr9/
@@ -89,7 +89,7 @@ enum {
/* Shaping Types */
enum {
- SL, /* Left-Joining, doesnt exist in U+0600 - U+06FF */
+ SL, /* Left-Joining, doesn't exist in U+0600 - U+06FF */
SR, /* Right-Joining, ie has Isolated, Final */
SD, /* Dual-Joining, ie has Isolated, Final, Initial, Medial */
SU, /* Non-Joining */
diff --git a/mkfiles.pl b/mkfiles.pl
index ae15ac48..a19cec5d 100755
--- a/mkfiles.pl
+++ b/mkfiles.pl
@@ -1966,7 +1966,7 @@ sub manpages {
"# ** DO NOT EDIT **\r\n".
"\r\n".
# No difference between DEBUG and RELEASE here as in 'vcproj', because
- # Dev-C++ does not support mutiple compilation profiles in one single project.
+ # Dev-C++ does not support multiple compilation profiles in one single project.
# (At least I can say this for Dev-C++ 5 Beta)
"[Project]\r\n".
"FileName=$windows_project.dev\r\n".
diff --git a/pageant.c b/pageant.c
index 2d9a7402..36671725 100644
--- a/pageant.c
+++ b/pageant.c
@@ -1469,7 +1469,7 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase,
}
/*
- * If we get here, we've succesfully loaded the key into
+ * If we get here, we've successfully loaded the key into
* rkey/skey, but not yet added it to the agent.
*/
diff --git a/settings.c b/settings.c
index f810d3f9..00c01c54 100644
--- a/settings.c
+++ b/settings.c
@@ -892,7 +892,7 @@ void load_open_settings(void *sesskey, Conf *conf)
{
/* SSH-2 only by default */
int sshprot = gppi_raw(sesskey, "SshProt", 3);
- /* Old sessions may contain the values correponding to the fallbacks
+ /* Old sessions may contain the values corresponding to the fallbacks
* we used to allow; migrate them */
if (sshprot == 1) sshprot = 0; /* => "SSH-1 only" */
else if (sshprot == 2) sshprot = 3; /* => "SSH-2 only" */
diff --git a/terminal.c b/terminal.c
index c79944cd..f47fe1bd 100644
--- a/terminal.c
+++ b/terminal.c
@@ -2437,7 +2437,7 @@ static void erase_lots(Terminal *term,
/* After an erase of lines from the top of the screen, we shouldn't
* bring the lines back again if the terminal enlarges (since the user or
- * application has explictly thrown them away). */
+ * application has explicitly thrown them away). */
if (erasing_lines_from_top && !(term->alt_which))
term->tempsblines = 0;
}
diff --git a/windows/window.c b/windows/window.c
index 004eb4f8..38c0e3cd 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -1700,7 +1700,7 @@ void request_resize(void *frontend, int w, int h)
{
int width, height;
- /* If the window is maximized supress resizing attempts */
+ /* If the window is maximized suppress resizing attempts */
if (IsZoomed(hwnd)) {
if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM)
return;
@@ -4777,7 +4777,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
return p - output;
}
- /* If we're definitly not building up an ALT-54321 then clear it */
+ /* If we're definitely not building up an ALT-54321 then clear it */
if (!left_alt)
keys_unicode[0] = 0;
/* If we will be using alt_sum fix the 256s */
From b189df947d2625499cf508fd1fae7e18b03b9247 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 15 Apr 2017 18:13:47 +0100
Subject: [PATCH 005/607] Condition out some API type-checks in the MinGW
build.
A couple of the functions for which I was already turning off the type
check for old Visual Studio turn out to also need it turning off for
MinGW.
---
windows/winhsock.c | 7 ++++---
windows/winnet.c | 6 +++---
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/windows/winhsock.c b/windows/winhsock.c
index e5f0fa4f..c8cb46f6 100644
--- a/windows/winhsock.c
+++ b/windows/winhsock.c
@@ -282,9 +282,10 @@ static char *sk_handle_peer_info(Socket s)
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
-#if defined _MSC_VER && _MSC_VER < 1900
- /* For older Visual Studio, this function isn't available in
- * the header files to type-check */
+#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__
+ /* For older Visual Studio, and MinGW too (at least as of
+ * Ubuntu 16.04), this function isn't available in the header
+ * files to type-check */
GET_WINDOWS_FUNCTION_NO_TYPECHECK(
kernel32_module, GetNamedPipeClientProcessId);
#else
diff --git a/windows/winnet.c b/windows/winnet.c
index 7ceca486..fc26c3e5 100644
--- a/windows/winnet.c
+++ b/windows/winnet.c
@@ -314,9 +314,9 @@ void sk_init(void)
GET_WINDOWS_FUNCTION(winsock_module, getservbyname);
GET_WINDOWS_FUNCTION(winsock_module, inet_addr);
GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa);
-#if defined _MSC_VER && _MSC_VER < 1900
- /* Older Visual Studio doesn't know about this function at all, so
- * can't type-check it */
+#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__
+ /* Older Visual Studio, and MinGW as of Ubuntu 16.04, don't know
+ * about this function at all, so can't type-check it */
GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, inet_ntop);
#else
GET_WINDOWS_FUNCTION(winsock_module, inet_ntop);
From 73039b7831aa863fabba1e6ff06471643303ae09 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 13 Mar 2017 21:24:06 +0000
Subject: [PATCH 006/607] Load winmm.dll (for PlaySound()) at run time.
It's not on the default list of important system 'known DLLs' stored
at HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs (see
https://isc.sans.edu/forums/diary/DLL+hijacking+vulnerabilities/9445/ )
which apparently makes it exempt from Windows's standard DLL hijacking
defence, i.e. if an executable links against it in the normal way then
that executable will be vulnerable to DLL hijacking from a file called
winmm.dll in the same directory as it.
The solution is to load it dynamically _after_ we've locked down our
DLL search path, which fortunately PuTTY's code base is well used to
doing already for other DLLs.
---
Recipe | 2 +-
windows/window.c | 8 +++++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/Recipe b/Recipe
index 54e00636..e2cce9ae 100644
--- a/Recipe
+++ b/Recipe
@@ -274,7 +274,7 @@ CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc
# Standard libraries.
LIBS = advapi32.lib user32.lib gdi32.lib comctl32.lib comdlg32.lib
- + shell32.lib winmm.lib imm32.lib winspool.lib ole32.lib
+ + shell32.lib imm32.lib winspool.lib ole32.lib
# Network backend sets. This also brings in the relevant attachment
# to proxy.c depending on whether we're crypto-avoidant or not.
diff --git a/windows/window.c b/windows/window.c
index 38c0e3cd..cb42addf 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -3949,12 +3949,14 @@ int char_width(Context ctx, int uc) {
DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO));
DECL_WINDOWS_FUNCTION(static, BOOL, ToUnicodeEx,
(UINT, UINT, const BYTE *, LPWSTR, int, UINT, HKL));
+DECL_WINDOWS_FUNCTION(static, BOOL, PlaySound, (LPCTSTR, HMODULE, DWORD));
static void init_winfuncs(void)
{
HMODULE user32_module = load_system32_dll("user32.dll");
+ HMODULE winmm_module = load_system32_dll("winmm.dll");
GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx);
- GET_WINDOWS_FUNCTION(user32_module, ToUnicodeEx);
+ GET_WINDOWS_FUNCTION_PP(winmm_module, PlaySound);
}
/*
@@ -5540,8 +5542,8 @@ void do_beep(void *frontend, int mode)
lastbeep = GetTickCount();
} else if (mode == BELL_WAVEFILE) {
Filename *bell_wavefile = conf_get_filename(conf, CONF_bell_wavefile);
- if (!PlaySound(bell_wavefile->path, NULL,
- SND_ASYNC | SND_FILENAME)) {
+ if (!p_PlaySound || !p_PlaySound(bell_wavefile->path, NULL,
+ SND_ASYNC | SND_FILENAME)) {
char buf[sizeof(bell_wavefile->path) + 80];
char otherbuf[100];
sprintf(buf, "Unable to play sound file\n%s\n"
From 793ac872757667a87df4636b5b3eed61302dd837 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 13 Mar 2017 21:28:36 +0000
Subject: [PATCH 007/607] Load the Windows printing subsystem at run time.
The printing functions are split between winspool.drv and spoolss.dll
in a really weird way (who would have guessed that OpenPrinter and
ClosePrinter don't live in the same dynamic library?!), but _neither_
of those counts as a system 'known DLL', so linking against either one
of these at load time is again a potential DLL hijacking vector.
---
Recipe | 2 +-
windows/winprint.c | 65 ++++++++++++++++++++++++++++++++++++----------
2 files changed, 52 insertions(+), 15 deletions(-)
diff --git a/Recipe b/Recipe
index e2cce9ae..0fb7bbb7 100644
--- a/Recipe
+++ b/Recipe
@@ -274,7 +274,7 @@ CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc
# Standard libraries.
LIBS = advapi32.lib user32.lib gdi32.lib comctl32.lib comdlg32.lib
- + shell32.lib imm32.lib winspool.lib ole32.lib
+ + shell32.lib imm32.lib ole32.lib
# Network backend sets. This also brings in the relevant attachment
# to proxy.c depending on whether we're crypto-avoidant or not.
diff --git a/windows/winprint.c b/windows/winprint.c
index c190e5fb..11587273 100644
--- a/windows/winprint.c
+++ b/windows/winprint.c
@@ -18,11 +18,46 @@ struct printer_job_tag {
HANDLE hprinter;
};
+DECL_WINDOWS_FUNCTION(static, BOOL, EnumPrinters,
+ (DWORD, LPTSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD));
+DECL_WINDOWS_FUNCTION(static, BOOL, OpenPrinter,
+ (LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS));
+DECL_WINDOWS_FUNCTION(static, BOOL, ClosePrinter, (HANDLE));
+DECL_WINDOWS_FUNCTION(static, BOOL, StartDocPrinter, (HANDLE, DWORD, LPBYTE));
+DECL_WINDOWS_FUNCTION(static, BOOL, EndDocPrinter, (HANDLE));
+DECL_WINDOWS_FUNCTION(static, BOOL, StartPagePrinter, (HANDLE));
+DECL_WINDOWS_FUNCTION(static, BOOL, EndPagePrinter, (HANDLE));
+DECL_WINDOWS_FUNCTION(static, BOOL, WritePrinter,
+ (HANDLE, LPVOID, DWORD, LPDWORD));
+
+static void init_winfuncs(void)
+{
+ static int initialised = FALSE;
+ char buf[4096];
+ if (initialised)
+ return;
+ {
+ HMODULE winspool_module = load_system32_dll("winspool.drv");
+ HMODULE spoolss_module = load_system32_dll("spoolss.dll");
+ GET_WINDOWS_FUNCTION_PP(winspool_module, EnumPrinters);
+ GET_WINDOWS_FUNCTION_PP(winspool_module, OpenPrinter);
+ GET_WINDOWS_FUNCTION_PP(spoolss_module, ClosePrinter);
+ GET_WINDOWS_FUNCTION_PP(winspool_module, StartDocPrinter);
+ GET_WINDOWS_FUNCTION_PP(spoolss_module, EndDocPrinter);
+ GET_WINDOWS_FUNCTION_PP(spoolss_module, StartPagePrinter);
+ GET_WINDOWS_FUNCTION_PP(spoolss_module, EndPagePrinter);
+ GET_WINDOWS_FUNCTION_PP(spoolss_module, WritePrinter);
+ }
+ initialised = TRUE;
+}
+
static int printer_add_enum(int param, DWORD level, char **buffer,
int offset, int *nprinters_ptr)
{
DWORD needed = 0, nprinters = 0;
+ init_winfuncs();
+
*buffer = sresize(*buffer, offset+512, char);
/*
@@ -30,16 +65,16 @@ static int printer_add_enum(int param, DWORD level, char **buffer,
* we'll need for the output. Discard the return value since it
* will almost certainly be a failure due to lack of space.
*/
- EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), 512,
- &needed, &nprinters);
+ p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), 512,
+ &needed, &nprinters);
if (needed < 512)
needed = 512;
*buffer = sresize(*buffer, offset+needed, char);
- if (EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset),
- needed, &needed, &nprinters) == 0)
+ if (p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset),
+ needed, &needed, &nprinters) == 0)
return FALSE;
*nprinters_ptr += nprinters;
@@ -131,19 +166,21 @@ printer_job *printer_start_job(char *printer)
DOC_INFO_1 docinfo;
int jobstarted = 0, pagestarted = 0;
+ init_winfuncs();
+
ret->hprinter = NULL;
- if (!OpenPrinter(printer, &ret->hprinter, NULL))
+ if (!p_OpenPrinter(printer, &ret->hprinter, NULL))
goto error;
docinfo.pDocName = "PuTTY remote printer output";
docinfo.pOutputFile = NULL;
docinfo.pDatatype = "RAW";
- if (!StartDocPrinter(ret->hprinter, 1, (LPBYTE)&docinfo))
+ if (!p_StartDocPrinter(ret->hprinter, 1, (LPBYTE)&docinfo))
goto error;
jobstarted = 1;
- if (!StartPagePrinter(ret->hprinter))
+ if (!p_StartPagePrinter(ret->hprinter))
goto error;
pagestarted = 1;
@@ -151,11 +188,11 @@ printer_job *printer_start_job(char *printer)
error:
if (pagestarted)
- EndPagePrinter(ret->hprinter);
+ p_EndPagePrinter(ret->hprinter);
if (jobstarted)
- EndDocPrinter(ret->hprinter);
+ p_EndDocPrinter(ret->hprinter);
if (ret->hprinter)
- ClosePrinter(ret->hprinter);
+ p_ClosePrinter(ret->hprinter);
sfree(ret);
return NULL;
}
@@ -167,7 +204,7 @@ void printer_job_data(printer_job *pj, void *data, int len)
if (!pj)
return;
- WritePrinter(pj->hprinter, data, len, &written);
+ p_WritePrinter(pj->hprinter, data, len, &written);
}
void printer_finish_job(printer_job *pj)
@@ -175,8 +212,8 @@ void printer_finish_job(printer_job *pj)
if (!pj)
return;
- EndPagePrinter(pj->hprinter);
- EndDocPrinter(pj->hprinter);
- ClosePrinter(pj->hprinter);
+ p_EndPagePrinter(pj->hprinter);
+ p_EndDocPrinter(pj->hprinter);
+ p_ClosePrinter(pj->hprinter);
sfree(pj);
}
From f77ee39e8cae2eaaea3a8fdd1eaffaf484cada08 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 13 Mar 2017 21:42:44 +0000
Subject: [PATCH 008/607] Load comctl32.dll (for drag lists) at run time.
This too is not in the list of known DLLs on Windows 10. I don't know
of any actual viable hijacking attack based on it, which according to
my reading of MSDN (specifically, a rather vague hint in
https://msdn.microsoft.com/library/ff919712) _may_ be because we
mention the common controls assembly in our application manifest; but
better safe than sorry.
Now the entire list of remaining DLLs that PuTTY links against at load
time is a subset of the Win10 known DLLs list, so that _should_ mean
that everything we load before we've deployed our own defence
(SetDefaultDllDirectories) is defended against for us by Windows
itself.
---
Recipe | 2 +-
windows/winctrls.c | 33 ++++++++++++++++++++++++---------
windows/window.c | 2 +-
windows/winpgen.c | 2 +-
windows/winstuff.h | 1 +
5 files changed, 28 insertions(+), 12 deletions(-)
diff --git a/Recipe b/Recipe
index 0fb7bbb7..f5458122 100644
--- a/Recipe
+++ b/Recipe
@@ -273,7 +273,7 @@ IMPORT = import sshbcrypt sshblowf
CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc
# Standard libraries.
-LIBS = advapi32.lib user32.lib gdi32.lib comctl32.lib comdlg32.lib
+LIBS = advapi32.lib user32.lib gdi32.lib comdlg32.lib
+ shell32.lib imm32.lib ole32.lib
# Network backend sets. This also brings in the relevant attachment
diff --git a/windows/winctrls.c b/windows/winctrls.c
index a03967e6..737018e4 100644
--- a/windows/winctrls.c
+++ b/windows/winctrls.c
@@ -38,6 +38,21 @@
#define PUSHBTNHEIGHT 14
#define PROGBARHEIGHT 14
+DECL_WINDOWS_FUNCTION(static, void, InitCommonControls, (void));
+DECL_WINDOWS_FUNCTION(static, BOOL, MakeDragList, (HWND));
+DECL_WINDOWS_FUNCTION(static, int, LBItemFromPt, (HWND, POINT, BOOL));
+DECL_WINDOWS_FUNCTION(static, int, DrawInsert, (HWND, HWND, int));
+
+void init_common_controls(void)
+{
+ HMODULE comctl32_module = load_system32_dll("comctl32.dll");
+ GET_WINDOWS_FUNCTION(comctl32_module, InitCommonControls);
+ GET_WINDOWS_FUNCTION(comctl32_module, MakeDragList);
+ GET_WINDOWS_FUNCTION(comctl32_module, LBItemFromPt);
+ GET_WINDOWS_FUNCTION(comctl32_module, DrawInsert);
+ p_InitCommonControls();
+}
+
void ctlposinit(struct ctlpos *cp, HWND hwnd,
int leftborder, int rightborder, int topborder)
{
@@ -921,7 +936,7 @@ void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines,
WS_VSCROLL | LBS_HASSTRINGS | LBS_USETABSTOPS,
WS_EX_CLIENTEDGE,
"", listid);
- MakeDragList(ctl);
+ p_MakeDragList(ctl);
}
break;
@@ -996,17 +1011,17 @@ int pl_itemfrompt(HWND hwnd, POINT cursor, BOOL scroll)
* current item if the upper edge is closer than
* the lower edge, or _below_ it if vice versa.
*/
- ret = LBItemFromPt(hwnd, cursor, scroll);
+ ret = p_LBItemFromPt(hwnd, cursor, scroll);
if (ret == -1)
return ret;
- ret = LBItemFromPt(hwnd, cursor, FALSE);
+ ret = p_LBItemFromPt(hwnd, cursor, FALSE);
updist = downdist = 0;
for (i = 1; i < 4096 && (!updist || !downdist); i++) {
uppoint = downpoint = cursor;
uppoint.y -= i;
downpoint.y += i;
- upitem = LBItemFromPt(hwnd, uppoint, FALSE);
- downitem = LBItemFromPt(hwnd, downpoint, FALSE);
+ upitem = p_LBItemFromPt(hwnd, uppoint, FALSE);
+ downitem = p_LBItemFromPt(hwnd, downpoint, FALSE);
if (!updist && upitem != ret)
updist = i;
if (!downdist && downitem != ret)
@@ -1047,13 +1062,13 @@ int handle_prefslist(struct prefslist *hdl,
SendDlgItemMessage(hwnd, hdl->listid,
LB_ADDSTRING, 0, (LPARAM) "");
- hdl->srcitem = LBItemFromPt(dlm->hWnd, dlm->ptCursor, TRUE);
+ hdl->srcitem = p_LBItemFromPt(dlm->hWnd, dlm->ptCursor, TRUE);
hdl->dragging = 0;
/* XXX hack Q183115 */
SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
ret |= 1; break;
case DL_CANCELDRAG:
- DrawInsert(hwnd, dlm->hWnd, -1); /* Clear arrow */
+ p_DrawInsert(hwnd, dlm->hWnd, -1); /* Clear arrow */
SendDlgItemMessage(hwnd, hdl->listid,
LB_DELETESTRING, hdl->dummyitem, 0);
hdl->dragging = 0;
@@ -1062,7 +1077,7 @@ int handle_prefslist(struct prefslist *hdl,
hdl->dragging = 1;
dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
if (dest > hdl->dummyitem) dest = hdl->dummyitem;
- DrawInsert (hwnd, dlm->hWnd, dest);
+ p_DrawInsert (hwnd, dlm->hWnd, dest);
if (dest >= 0)
SetWindowLongPtr(hwnd, DWLP_MSGRESULT, DL_MOVECURSOR);
else
@@ -1072,7 +1087,7 @@ int handle_prefslist(struct prefslist *hdl,
if (hdl->dragging) {
dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
if (dest > hdl->dummyitem) dest = hdl->dummyitem;
- DrawInsert (hwnd, dlm->hWnd, -1);
+ p_DrawInsert (hwnd, dlm->hWnd, -1);
}
SendDlgItemMessage(hwnd, hdl->listid,
LB_DELETESTRING, hdl->dummyitem, 0);
diff --git a/windows/window.c b/windows/window.c
index cb42addf..e01a3c32 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -359,7 +359,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
sk_init();
- InitCommonControls();
+ init_common_controls();
/* Set Explicit App User Model Id so that jump lists don't cause
PuTTY to hang on to removable media. */
diff --git a/windows/winpgen.c b/windows/winpgen.c
index c4f4de45..2507d37e 100644
--- a/windows/winpgen.c
+++ b/windows/winpgen.c
@@ -1529,7 +1529,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
dll_hijacking_protection();
- InitCommonControls();
+ init_common_controls();
hinst = inst;
hwnd = NULL;
diff --git a/windows/winstuff.h b/windows/winstuff.h
index 007889e4..f8c4243f 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -329,6 +329,7 @@ struct ctlpos {
int boxystart, boxid;
char *boxtext;
};
+void init_common_controls(void); /* also does some DLL-loading */
/*
* Exports from winutils.c.
From b1829b81b5c0d12dcc91f6b50b0b4d83c3df6a8e Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 24 Apr 2017 14:45:52 +0100
Subject: [PATCH 009/607] Update version number for 0.69 release.
---
Buildscr | 2 +-
LATEST.VER | 2 +-
doc/plink.but | 2 +-
doc/pscp.but | 2 +-
windows/putty.iss | 8 ++++----
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/Buildscr b/Buildscr
index 7ca4eb7f..086800ac 100644
--- a/Buildscr
+++ b/Buildscr
@@ -35,7 +35,7 @@ module putty
ifeq "$(RELEASE)" "" set Ndate $(!builddate)
ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -pe 's/(....)(..)(..)/$$1-$$2-$$3/' > date
ifneq "$(Ndate)" "" read Date date
-set Epoch 16214 # update this at every release
+set Epoch 16280 # update this at every release
ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -ne 'use Time::Local; /(....)(..)(..)/ and print timegm(0,0,0,$$3,$$2-1,$$1) / 86400 - $(Epoch)' > days
ifneq "$(Ndate)" "" read Days days
diff --git a/LATEST.VER b/LATEST.VER
index db1ed30c..b04c6474 100644
--- a/LATEST.VER
+++ b/LATEST.VER
@@ -1 +1 @@
-0.68
+0.69
diff --git a/doc/plink.but b/doc/plink.but
index 351e13ea..153982e0 100644
--- a/doc/plink.but
+++ b/doc/plink.but
@@ -41,7 +41,7 @@ use Plink:
\c Z:\sysosd>plink
\c Plink: command-line connection utility
-\c Release 0.68
+\c Release 0.69
\c Usage: plink [options] [user@]host [command]
\c ("host" can also be a PuTTY saved session name)
\c Options:
diff --git a/doc/pscp.but b/doc/pscp.but
index 27643a46..30a47f83 100644
--- a/doc/pscp.but
+++ b/doc/pscp.but
@@ -39,7 +39,7 @@ use PSCP:
\c Z:\owendadmin>pscp
\c PuTTY Secure Copy client
-\c Release 0.68
+\c Release 0.69
\c Usage: pscp [options] [user@]host:source target
\c pscp [options] source [source...] [user@]host:target
\c pscp [options] -ls [user@]host:filespec
diff --git a/windows/putty.iss b/windows/putty.iss
index dda68f63..b3deb76c 100644
--- a/windows/putty.iss
+++ b/windows/putty.iss
@@ -14,10 +14,10 @@
[Setup]
AppName=PuTTY
-AppVerName=PuTTY version 0.68
-VersionInfoTextVersion=Release 0.68
-AppVersion=0.68
-VersionInfoVersion=0.68.0.0
+AppVerName=PuTTY version 0.69
+VersionInfoTextVersion=Release 0.69
+AppVersion=0.69
+VersionInfoVersion=0.69.0.0
AppPublisher=Simon Tatham
AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/putty/
AppReadmeFile={app}\README.txt
From d6d10932ac1f9333b06ee8027dcfad231f17ed04 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 29 Apr 2017 10:23:31 +0100
Subject: [PATCH 010/607] Release checklist update: no @releases array!
The rewritten bugs2html.py in the wishlist repository no longer needs
me to manually maintain a mapping between releases and version control
- and the one thing I forgot was to remove the reminder in the release
checklist telling me to keep that mapping up to date :-)
---
CHECKLST.txt | 1 -
1 file changed, 1 deletion(-)
diff --git a/CHECKLST.txt b/CHECKLST.txt
index 0499d780..8d55ac41 100644
--- a/CHECKLST.txt
+++ b/CHECKLST.txt
@@ -96,7 +96,6 @@ for it:
branch (so that the wishlist mechanism can't automatically mark
them as fixed in the new release), add appropriate Fixed-in
headers for those.
- * Add an entry to the @releases array in control/bugs2html.
- Make a release-candidate build from the release tag, and put the
build.out and build.log dfiles somewhere safe. Normally I store
From 5a576e0c891ddc745ea1f67bcca10f1386ff1849 Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Sat, 29 Apr 2017 12:07:37 +0100
Subject: [PATCH 011/607] Reinstate use of ToUnicodeEx().
This was accidentally disabled by 73039b783, causing a regression in
ability to type characters outside of the current Windows code page.
---
windows/window.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/windows/window.c b/windows/window.c
index e01a3c32..89ddb863 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -3956,6 +3956,7 @@ static void init_winfuncs(void)
HMODULE user32_module = load_system32_dll("user32.dll");
HMODULE winmm_module = load_system32_dll("winmm.dll");
GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx);
+ GET_WINDOWS_FUNCTION(user32_module, ToUnicodeEx);
GET_WINDOWS_FUNCTION_PP(winmm_module, PlaySound);
}
From ed600ab23f87fd1785d100937e992e244ba82ed5 Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Sat, 29 Apr 2017 14:24:17 +0100
Subject: [PATCH 012/607] Fix double negative in TTY mode docs.
---
doc/config.but | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/config.but b/doc/config.but
index 269dbdc1..a7d45689 100644
--- a/doc/config.but
+++ b/doc/config.but
@@ -2959,7 +2959,7 @@ modes from the local terminal, if any.
}
-\b If \q{Nothing} is selected, no value for the mode will not be
+\b If \q{Nothing} is selected, no value for the mode will be
specified to the server under any circumstances.
\b If a value is specified, it will be sent to the server under all
From fb023da0fdcbfca104b39c2315a79186d7254c99 Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Sun, 30 Apr 2017 10:42:02 +0100
Subject: [PATCH 013/607] Be less vague in the description of IUTF8.
---
doc/config.but | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/doc/config.but b/doc/config.but
index a7d45689..bb18b794 100644
--- a/doc/config.but
+++ b/doc/config.but
@@ -3006,18 +3006,19 @@ PuTTY in a variety of ways, such as \cw{true}/\cw{false},
\cw{no} is different from not sending the mode at all.)
\b The boolean mode \I{IUTF8 terminal mode}\cw{IUTF8} signals to the
-server whether the terminal character set is \i{UTF-8} or not.
-If this is set incorrectly, keys like backspace may do the wrong thing
-in some circumstances. However, setting this is not usually
-sufficient to cause servers to expect the terminal to be in UTF-8 mode;
-POSIX servers will generally require the locale to be set (by some
-server-dependent means), although many default to UTF-8. Also,
-since this mode was added to the SSH protocol much later than the
-others, \#{circa 2016} many servers (particularly older servers) do
-not honour this mode sent over SSH; indeed, a few poorly-written
-servers object to its mere presence, so you may find you need to set
-it to not be sent at all. When set to \q{Auto}, this follows the local
-configured character set (see \k{config-charset}).
+server whether the terminal character set is \i{UTF-8} or not, for
+purposes such as basic line editing; if this is set incorrectly,
+the backspace key may erase the wrong amount of text, for instance.
+However, simply setting this is not usually sufficient for the server
+to use UTF-8; POSIX servers will generally also require the locale to
+be set (by some server-dependent means), although many newer
+installations default to UTF-8. Also, since this mode was added to the
+SSH protocol much later than the others, \#{circa 2016} many servers
+(particularly older servers) do not honour this mode sent over SSH;
+indeed, a few poorly-written servers object to its mere presence, so
+you may find you need to set it to not be sent at all. When set to
+\q{Auto}, this follows the local configured character set (see
+\k{config-charset}).
\b Terminal speeds are configured elsewhere; see \k{config-termspeed}.
From 230f7d56284a703c65c4fecf7e6f23b791043f81 Mon Sep 17 00:00:00 2001
From: Zero King
Date: Sun, 30 Apr 2017 11:01:13 +0100
Subject: [PATCH 014/607] Fix thinko introduced in 8833634f4.
This prevented compilation with Gtk 2.
---
unix/gtkwin.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 69e33509..bca6daa4 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -2188,11 +2188,11 @@ void set_gtk_widget_background(GtkWidget *widget, const GdkColor *col)
free(data);
free(col_css);
#else
- if (gtk_widget_get_window(win)) {
+ if (gtk_widget_get_window(widget)) {
/* For GTK1, which doesn't have a 'const' on
* gdk_window_set_background's second parameter type. */
GdkColor col_mutable = *col;
- gdk_window_set_background(gtk_widget_get_window(win), &col_mutable);
+ gdk_window_set_background(gtk_widget_get_window(widget), &col_mutable);
}
#endif
}
From b566c5f125efb9933303a61303ab17e2af0a859b Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Sun, 30 Apr 2017 11:39:07 +0100
Subject: [PATCH 015/607] Add a cast to fix a warning.
This fixes compilation with Gtk 2 with -Werror. Problem introduced by
64221972c.
---
unix/gtkwin.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index bca6daa4..67cfcac3 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -1935,7 +1935,7 @@ gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data)
event_button->x_root = event->x_root;
event_button->y_root = event->y_root;
ret = button_internal(inst, event_button);
- gdk_event_free(event_button);
+ gdk_event_free((GdkEvent *)event_button);
return ret;
#endif
}
From ad694a494158d44ff3dce86d20f4f2afb1dc142e Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 1 May 2017 06:53:06 +0100
Subject: [PATCH 016/607] mkfiles.pl: fix regex syntax error.
Thanks to Brian K. White for spotting this straight-up syntax error of
a missing ), in the regex handling the special case of &splitlines
when it findss a word in its input string too long to fit in the
specified output line width. Apparently in all my own uses of
&splitline I'd never exercised that special-case code path before.
---
mkfiles.pl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mkfiles.pl b/mkfiles.pl
index a19cec5d..8a63c65d 100755
--- a/mkfiles.pl
+++ b/mkfiles.pl
@@ -364,7 +364,7 @@ sub splitline {
$len = (defined $width ? $width : 76);
$splitchar = (defined $splitchar ? $splitchar : '\\');
while (length $line > $len) {
- $line =~ /^(.{0,$len})\s(.*)$/ or $line =~ /^(.{$len,}?\s(.*)$/;
+ $line =~ /^(.{0,$len})\s(.*)$/ or $line =~ /^(.{$len,})?\s(.*)$/;
$result .= $1;
$result .= " ${splitchar}\n\t\t" if $2 ne '';
$line = $2;
From 6ea9d36ae94736493216591fb18d20efe79b5216 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 7 May 2017 16:29:01 +0100
Subject: [PATCH 017/607] Switch chiark URLs to https.
---
Buildscr | 4 ++--
README | 4 ++--
contrib/cygtermd/README | 2 +-
doc/blurb.but | 2 +-
doc/faq.but | 18 +++++++++---------
doc/feedback.but | 18 +++++++++---------
doc/man-pl.but | 2 +-
doc/man-pscp.but | 2 +-
doc/man-psft.but | 2 +-
doc/man-ptel.but | 2 +-
doc/man-putt.but | 2 +-
doc/pgpkeys.but | 20 ++++++++++----------
doc/udp.but | 2 +-
ssh.c | 2 +-
windows/README-msi.txt | 2 +-
windows/README.txt | 2 +-
windows/putty.iss | 2 +-
windows/website.url | Bin 103 -> 104 bytes
windows/windlg.c | 2 +-
windows/winpgen.c | 2 +-
windows/winpgnt.c | 2 +-
21 files changed, 47 insertions(+), 47 deletions(-)
diff --git a/Buildscr b/Buildscr
index 086800ac..afcc2766 100644
--- a/Buildscr
+++ b/Buildscr
@@ -172,7 +172,7 @@ delegate windows
# provide a 'more info' URL, and an optional -n option to provide a
# program name, and that it can take multiple .exe filename
# arguments and sign them all in place.
- ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i http://www.chiark.greenend.org.uk/~sgtatham/putty/ build*/*.exe
+ ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ build*/*.exe
# Ignore exit code from hhc, in favour of seeing whether the .chm
# file was created. (Yuck; but hhc appears to return non-zero
@@ -187,7 +187,7 @@ delegate windows
in putty/windows with innosetup do/win iscc putty.iss
# Sign the installers.
- ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i http://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi Output/installer.exe
+ ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi Output/installer.exe
# Finished Windows builds.
return putty/windows/buildold/*.exe
diff --git a/README b/README
index ef931dd1..4d73a995 100644
--- a/README
+++ b/README
@@ -127,11 +127,11 @@ Documentation (in various formats including Windows Help and Unix
`man' pages) is built from the Halibut (`.but') files in the `doc'
subdirectory using `doc/Makefile'. If you aren't using one of our
source snapshots, you'll need to do this yourself. Halibut can be
-found at .
+found at .
The PuTTY home web site is
- http://www.chiark.greenend.org.uk/~sgtatham/putty/
+ https://www.chiark.greenend.org.uk/~sgtatham/putty/
If you want to send bug reports or feature requests, please read the
Feedback section of the web site before doing so. Sending one-line
diff --git a/contrib/cygtermd/README b/contrib/cygtermd/README
index ebfdfdd7..a722fe10 100644
--- a/contrib/cygtermd/README
+++ b/contrib/cygtermd/README
@@ -8,4 +8,4 @@ install it in Cygwin's /bin, and configure PuTTY to use it as a local
proxy process. For detailed instructions, see the PuTTY Wishlist page
at
-http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/cygwin-terminal-window.html
+https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/cygwin-terminal-window.html
diff --git a/doc/blurb.but b/doc/blurb.but
index 227300f4..64e8ab74 100644
--- a/doc/blurb.but
+++ b/doc/blurb.but
@@ -7,7 +7,7 @@
\cfg{xhtml-leaf-contains-contents}{true}
\cfg{xhtml-body-end}{
If you want to provide feedback on this manual
or on the PuTTY tools themselves, see the
-Feedback
+Feedback
page.
}
\cfg{html-template-fragment}{%k}{%b}
diff --git a/doc/faq.but b/doc/faq.but
index 42f965b2..80cf9d63 100644
--- a/doc/faq.but
+++ b/doc/faq.but
@@ -27,18 +27,18 @@ else.
\I{supported features}In general, if you want to know if PuTTY supports
a particular feature, you should look for it on the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}{PuTTY web site}.
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}{PuTTY web site}.
In particular:
\b try the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{changes
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{changes
page}, and see if you can find the feature on there. If a feature is
listed there, it's been implemented. If it's listed as a change made
\e{since} the latest version, it should be available in the
development snapshots, in which case testing will be very welcome.
\b try the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist
page}, and see if you can find the feature there. If it's on there,
and not in the \q{Recently fixed} section, it probably \e{hasn't} been
implemented.
@@ -54,7 +54,7 @@ version 0.52.
\cw{ssh.com} SSH-2 private key files?
PuTTY doesn't support this natively (see
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/key-formats-natively.html}{the wishlist entry}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/key-formats-natively.html}{the wishlist entry}
for reasons why not), but as of 0.53
PuTTYgen can convert both OpenSSH and \cw{ssh.com} private key
files into PuTTY's format.
@@ -236,7 +236,7 @@ port, or any other port of PuTTY, they were mistaken. We don't.
There are some third-party ports to various platforms, mentioned
on the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/links.html}{Links page of our website}.
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/links.html}{Links page of our website}.
\S{faq-unix}{Question} \I{Unix version}Is there a port to Unix?
@@ -323,7 +323,7 @@ for, it might be a long time before any of us get round to learning
a new system and doing the port for that.
However, some of the work has been done by other people; see the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/links.html}{Links page of our website}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/links.html}{Links page of our website}
for various third-party ports.
\S{faq-iphone}{Question} Will there be a port to the iPhone?
@@ -351,7 +351,7 @@ Most of the code cleanup work would be a good thing to happen in
general, so if anyone feels like helping, we wouldn't say no.
See also
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/dll-frontend.html}{the wishlist entry}.
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/dll-frontend.html}{the wishlist entry}.
\S{faq-vb}{Question} Is the SSH or Telnet code available as a Visual
Basic component?
@@ -891,7 +891,7 @@ us \q{I wanted the F1 key to send \c{^[[11~}, but instead it's
sending \c{^[OP}, can this be done?}, or something similar.
You should still read the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/feedback.html}{Feedback
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/feedback.html}{Feedback
page} on the PuTTY website (also provided as \k{feedback} in the
manual), and follow the guidelines contained in that.
@@ -1060,7 +1060,7 @@ still. We do not recommend it.)
This is caused by a bug in certain versions of \i{Windows XP} which
is triggered by PuTTY 0.58. This was fixed in 0.59. The
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/xp-wont-run}{\q{xp-wont-run}}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/xp-wont-run}{\q{xp-wont-run}}
entry in PuTTY's wishlist has more details.
\S{faq-system32}{Question} When I put 32-bit PuTTY in
diff --git a/doc/feedback.but b/doc/feedback.but
index e0854fc5..b8428e41 100644
--- a/doc/feedback.but
+++ b/doc/feedback.but
@@ -112,7 +112,7 @@ If you think you have found a bug in PuTTY, your first steps should
be:
\b Check the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist
page} on the PuTTY website, and see if we already know about the
problem. If we do, it is almost certainly not necessary to mail us
about it, unless you think you have extra information that might be
@@ -121,12 +121,12 @@ specific extra information about a particular bug, the Wishlist page
will say so.)
\b Check the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{Change
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{Change
Log} on the PuTTY website, and see if we have already fixed the bug
in the \i{development snapshots}.
\b Check the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/faq.html}{FAQ}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/faq.html}{FAQ}
on the PuTTY website (also provided as \k{faq} in the manual), and
see if it answers your question. The FAQ lists the most common
things which people think are bugs, but which aren't bugs.
@@ -188,7 +188,7 @@ you haven't supplied us with full information about the actual bug,
then we won't be able to find a better solution.
\b
-\W{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html}\cw{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/bugs.html}\cw{https://www.chiark.greenend.org.uk/~sgtatham/bugs.html}
is an article on how to report bugs effectively in general. If your
bug report is \e{particularly} unclear, we may ask you to go away,
read this article, and then report the bug again.
@@ -224,14 +224,14 @@ If you want to request a new feature in PuTTY, the very first things
you should do are:
\b Check the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist
page} on the PuTTY website, and see if your feature is already on
the list. If it is, it probably won't achieve very much to repeat
the request. (But see \k{feedback-feature-priority} if you want to
persuade us to give your particular feature higher priority.)
\b Check the Wishlist and
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{Change
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{Change
Log} on the PuTTY website, and see if we have already added your
feature in the development snapshots. If it isn't clear, download
the latest development snapshot and see if the feature is present.
@@ -350,7 +350,7 @@ Of course, if the web site has some other error (Connection Refused,
If you want to report a problem with our web site, check that you're
looking at our \e{real} web site and not a mirror. The real web site
is at
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\c{http://www.chiark.greenend.org.uk/~sgtatham/putty/};
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\c{https://www.chiark.greenend.org.uk/~sgtatham/putty/};
if that's not where you're reading this, then don't report the
problem to us until you've checked that it's really a problem with
the main site. If it's only a problem with the mirror, you should
@@ -399,7 +399,7 @@ setting up a mirror. You already have permission.
If the mirror is in a country where we don't already have plenty of
mirrors, we may be willing to add it to the list on our
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html}{mirrors
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html}{mirrors
page}. Read the guidelines on that page, make sure your mirror
works, and email us the information listed at the bottom of the
page.
@@ -414,7 +414,7 @@ to be a cheap way to gain search rankings.
If you have technical questions about the process of mirroring, then
you might want to mail us before setting up the mirror (see also the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html#guidelines}{guidelines on the Mirrors page});
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html#guidelines}{guidelines on the Mirrors page});
but if you just want to ask for permission, you don't need to. You
already have permission.
diff --git a/doc/man-pl.but b/doc/man-pl.but
index a46e6a19..9f411871 100644
--- a/doc/man-pl.but
+++ b/doc/man-pl.but
@@ -260,7 +260,7 @@ exists, nonzero otherwise.
For more information on plink, it's probably best to go and look at
the manual on the PuTTY web page:
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/}
\S{plink-manpage-bugs} BUGS
diff --git a/doc/man-pscp.but b/doc/man-pscp.but
index 05e5a23c..6c703e13 100644
--- a/doc/man-pscp.but
+++ b/doc/man-pscp.but
@@ -174,7 +174,7 @@ encrypted packet data.
For more information on \cw{pscp} it's probably best to go and look at
the manual on the PuTTY web page:
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/}
\S{pscp-manpage-bugs} BUGS
diff --git a/doc/man-psft.but b/doc/man-psft.but
index 80d86ecf..51f30d3a 100644
--- a/doc/man-psft.but
+++ b/doc/man-psft.but
@@ -159,7 +159,7 @@ at the \cw{psftp>} prompt.
For more information on \cw{psftp} it's probably best to go and look at
the manual on the PuTTY web page:
-\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}
+\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/}
\S{psftp-manpage-bugs} BUGS
diff --git a/doc/man-ptel.but b/doc/man-ptel.but
index a3b79405..73b85ecc 100644
--- a/doc/man-ptel.but
+++ b/doc/man-ptel.but
@@ -208,7 +208,7 @@ your home directory.
For more information on PuTTY and PuTTYtel, it's probably best to go
and look at the manual on the web page:
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/}
\S{puttytel-manpage-bugs} BUGS
diff --git a/doc/man-putt.but b/doc/man-putt.but
index df7b9e1f..cb7cca47 100644
--- a/doc/man-putt.but
+++ b/doc/man-putt.but
@@ -321,7 +321,7 @@ your home directory.
For more information on PuTTY, it's probably best to go and look at
the manual on the web page:
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/}
\S{putty-manpage-bugs} BUGS
diff --git a/doc/pgpkeys.but b/doc/pgpkeys.but
index 9ec90066..71143af2 100644
--- a/doc/pgpkeys.but
+++ b/doc/pgpkeys.but
@@ -53,19 +53,19 @@ The current issue of those keys are available for download from the
PuTTY website, and are also available on PGP keyservers using the key
IDs listed below.
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2015.asc}{\s{Master Key}}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2015.asc}{\s{Master Key}}
\dd RSA, 4096-bit. Key ID: \cw{4096R/04676F7C} (long version:
\cw{4096R/AB585DC604676F7C}). Fingerprint:
\cw{440D\_E3B5\_B7A1\_CA85\_B3CC\_\_1718\_AB58\_5DC6\_0467\_6F7C}
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2015.asc}{\s{Release Key}}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2015.asc}{\s{Release Key}}
\dd RSA, 2048-bit. Key ID: \cw{2048R/B43434E4} (long version:
\cw{2048R/9DFE2648B43434E4}). Fingerprint:
\cw{0054\_DDAA\_8ADA\_15D2\_768A\_\_6DE7\_9DFE\_2648\_B434\_34E4}
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2016.asc}{\s{Secure Contact Key}}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2016.asc}{\s{Secure Contact Key}}
\dd RSA, 2048-bit. Main key ID: \cw{2048R/8A0AF00B} (long version:
\cw{2048R/C4FCAAD08A0AF00B}). Encryption subkey ID:
@@ -73,7 +73,7 @@ IDs listed below.
Fingerprint:
\cw{8A26\_250E\_763F\_E359\_75F3\_\_118F\_C4FC\_AAD0\_8A0A\_F00B}
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2015.asc}{\s{Snapshot Key}}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2015.asc}{\s{Snapshot Key}}
\dd RSA, 2048-bit. Key ID: \cw{2048R/D15F7E8A} (long version:
\cw{2048R/EEF20295D15F7E8A}). Fingerprint:
@@ -179,37 +179,37 @@ Releases prior to the rollover are signed with the old Release Keys.
For completeness, those old keys are given here:
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-rsa.asc}{\s{Master Key} (original RSA)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-rsa.asc}{\s{Master Key} (original RSA)}
\dd RSA, 1024-bit. Key ID: \cw{1024R/1E34AC41} (long version:
\cw{1024R/9D5877BF1E34AC41}). Fingerprint:
\cw{8F\_15\_97\_DA\_25\_30\_AB\_0D\_\_88\_D1\_92\_54\_11\_CF\_0C\_4C}
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-dsa.asc}{\s{Master Key} (original DSA)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-dsa.asc}{\s{Master Key} (original DSA)}
\dd DSA, 1024-bit. Key ID: \cw{1024D/6A93B34E} (long version:
\cw{1024D/4F5E6DF56A93B34E}). Fingerprint:
\cw{313C\_3E76\_4B74\_C2C5\_F2AE\_\_83A8\_4F5E\_6DF5\_6A93\_B34E}
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-rsa.asc}{\s{Release Key} (original RSA)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-rsa.asc}{\s{Release Key} (original RSA)}
\dd RSA, 1024-bit. Key ID: \cw{1024R/B41CAE29} (long version:
\cw{1024R/EF39CCC0B41CAE29}). Fingerprint:
\cw{AE\_65\_D3\_F7\_85\_D3\_18\_E0\_\_3B\_0C\_9B\_02\_FF\_3A\_81\_FE}
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-dsa.asc}{\s{Release Key} (original DSA)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-dsa.asc}{\s{Release Key} (original DSA)}
\dd DSA, 1024-bit. Key ID: \cw{1024D/08B0A90B} (long version:
\cw{1024D/FECD6F3F08B0A90B}). Fingerprint:
\cw{00B1\_1009\_38E6\_9800\_6518\_\_F0AB\_FECD\_6F3F\_08B0\_A90B}
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-rsa.asc}{\s{Snapshot Key} (original RSA)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-rsa.asc}{\s{Snapshot Key} (original RSA)}
\dd RSA, 1024-bit. Key ID: \cw{1024R/32B903A9} (long version:
\cw{1024R/FAAED21532B903A9}). Fingerprint:
\cw{86\_8B\_1F\_79\_9C\_F4\_7F\_BD\_\_8B\_1B\_D7\_8E\_C6\_4E\_4C\_03}
-\dt \W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-dsa.asc}{\s{Snapshot Key} (original DSA)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-dsa.asc}{\s{Snapshot Key} (original DSA)}
\dd DSA, 1024-bit. Key ID: \cw{1024D/7D3E4A00} (long version:
\cw{1024D/165E56F77D3E4A00}). Fingerprint:
diff --git a/doc/udp.but b/doc/udp.but
index c50464ee..9ca8ed3f 100644
--- a/doc/udp.but
+++ b/doc/udp.but
@@ -331,7 +331,7 @@ local state structures \c{s} or \c{st} in each function, or the
backend-wide structure \c{ssh}.
See
-\W{http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html}\c{http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html}
+\W{https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html}\c{https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html}
for a more in-depth discussion of what these macros are for and how
they work.
diff --git a/ssh.c b/ssh.c
index 693f52d8..994e93ae 100644
--- a/ssh.c
+++ b/ssh.c
@@ -303,7 +303,7 @@ enum {
* macros look impenetrable to you, you might find it helpful to
* read
*
- * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+ * https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
*
* which explains the theory behind these macros.
*
diff --git a/windows/README-msi.txt b/windows/README-msi.txt
index a6c2707d..14aac092 100644
--- a/windows/README-msi.txt
+++ b/windows/README-msi.txt
@@ -35,7 +35,7 @@ What do I do if it doesn't work?
The PuTTY home web site is
- http://www.chiark.greenend.org.uk/~sgtatham/putty/
+ https://www.chiark.greenend.org.uk/~sgtatham/putty/
Here you will find our list of known bugs and pending feature
requests. If your problem is not listed in there, or in the FAQ, or
diff --git a/windows/README.txt b/windows/README.txt
index fa153c62..d929df90 100644
--- a/windows/README.txt
+++ b/windows/README.txt
@@ -29,7 +29,7 @@ What do I do if it doesn't work?
The PuTTY home web site is
- http://www.chiark.greenend.org.uk/~sgtatham/putty/
+ https://www.chiark.greenend.org.uk/~sgtatham/putty/
Here you will find our list of known bugs and pending feature
requests. If your problem is not listed in there, or in the FAQ, or
diff --git a/windows/putty.iss b/windows/putty.iss
index b3deb76c..3fadcb92 100644
--- a/windows/putty.iss
+++ b/windows/putty.iss
@@ -19,7 +19,7 @@ VersionInfoTextVersion=Release 0.69
AppVersion=0.69
VersionInfoVersion=0.69.0.0
AppPublisher=Simon Tatham
-AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/putty/
+AppPublisherURL=https://www.chiark.greenend.org.uk/~sgtatham/putty/
AppReadmeFile={app}\README.txt
DefaultDirName={pf}\PuTTY
DefaultGroupName=PuTTY
diff --git a/windows/website.url b/windows/website.url
index 4b50369cec07713e189e28b5e0724a8dee278eba..4f6d47d1f7136fed66f8bd3d36f26e7315f5496a 100644
GIT binary patch
delta 9
QcmYe#m>|tqJW<*Q01toy=Kufz
delta 7
Ocmc~upCCO^#s>fjyaIFp
diff --git a/windows/windlg.c b/windows/windlg.c
index e29f1291..8bd02d85 100644
--- a/windows/windlg.c
+++ b/windows/windlg.c
@@ -227,7 +227,7 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
case IDA_WEB:
/* Load web browser */
ShellExecute(hwnd, "open",
- "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
+ "https://www.chiark.greenend.org.uk/~sgtatham/putty/",
0, 0, SW_SHOWDEFAULT);
return 0;
}
diff --git a/windows/winpgen.c b/windows/winpgen.c
index 2507d37e..7903c1cf 100644
--- a/windows/winpgen.c
+++ b/windows/winpgen.c
@@ -322,7 +322,7 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
case 102:
/* Load web browser */
ShellExecute(hwnd, "open",
- "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
+ "https://www.chiark.greenend.org.uk/~sgtatham/putty/",
0, 0, SW_SHOWDEFAULT);
return 0;
}
diff --git a/windows/winpgnt.c b/windows/winpgnt.c
index 70f3d2ca..ebb6c6ac 100644
--- a/windows/winpgnt.c
+++ b/windows/winpgnt.c
@@ -178,7 +178,7 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
case 102:
/* Load web browser */
ShellExecute(hwnd, "open",
- "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
+ "https://www.chiark.greenend.org.uk/~sgtatham/putty/",
0, 0, SW_SHOWDEFAULT);
return 0;
}
From ce050c5b72d17559ef79f07b2013b35e2dd22467 Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Mon, 8 May 2017 21:33:03 +0100
Subject: [PATCH 018/607] Fix a luking mention of Win32 in README.
---
README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README b/README
index 4d73a995..50314ca6 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-This is the README for the source archive of PuTTY, a free Win32
+This is the README for the source archive of PuTTY, a free Windows
and Unix Telnet and SSH client.
If you want to rebuild PuTTY from source, we provide a variety of
From 93931b0a568c8f39801ffcb0c67cc001dac4f9b3 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 13 May 2017 17:10:36 +0100
Subject: [PATCH 019/607] Switch to using Halibut's new direct .CHM generation.
This allows me to remove HTML Help Workshop completely from my build
dependencies, and good riddance!
---
Buildscr | 9 +--------
doc/Makefile | 10 ++++------
doc/blurb.but | 6 ++++++
doc/chm.but | 22 ----------------------
4 files changed, 11 insertions(+), 36 deletions(-)
delete mode 100644 doc/chm.but
diff --git a/Buildscr b/Buildscr
index afcc2766..de7f41b5 100644
--- a/Buildscr
+++ b/Buildscr
@@ -139,8 +139,7 @@ ifneq "$(MAKEARGS)" "" set Makeargs $(Makeargs) $(MAKEARGS)
in putty do ./mksrcarc.sh
in putty do ./mkunxarc.sh '$(Autoconfver)' '$(Uxarcsuffix)' $(Docmakever)
in putty do perl mkfiles.pl
-in putty/doc do make $(Docmakever) putty.hlp
-in putty/doc do make $(Docmakever) chm
+in putty/doc do make $(Docmakever) putty.hlp putty.chm
# Munge the installer script locally so that it reports the version
# we're really building.
@@ -174,11 +173,6 @@ delegate windows
# arguments and sign them all in place.
ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ build*/*.exe
- # Ignore exit code from hhc, in favour of seeing whether the .chm
- # file was created. (Yuck; but hhc appears to return non-zero
- # exit codes on whim.)
- in putty/doc with htmlhelp do/win hhc putty.hhp & type putty.chm >nul
-
# Build a WiX MSI installer, for each of build32 and build64.
in putty/windows with wix do/win candle -arch x86 -dWin64=no -dBuilddir=build32\ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer32.msi
in putty/windows with wix do/win candle -arch x64 -dWin64=yes -dBuilddir=build64\ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer64.msi
@@ -196,7 +190,6 @@ delegate windows
return putty/windows/build32/*.map
return putty/windows/build64/*.exe
return putty/windows/build64/*.map
- return putty/doc/putty.chm
return putty/windows/installer32.msi
return putty/windows/installer64.msi
return putty/windows/Output/installer.exe
diff --git a/doc/Makefile b/doc/Makefile
index e7bf287e..cb079fb5 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -44,19 +44,17 @@ INPUTS = $(patsubst %,%.but,$(CHAPTERS))
HALIBUT = halibut
index.html: $(INPUTS)
- $(HALIBUT) --text --html --winhelp $(INPUTS)
+ $(HALIBUT) --text --html --winhelp --chm $(INPUTS)
-# During formal builds it's useful to be able to build this one alone.
+# During formal builds it's useful to be able to build these ones alone.
putty.hlp: $(INPUTS)
$(HALIBUT) --winhelp $(INPUTS)
+putty.chm: $(INPUTS)
+ $(HALIBUT) --chm $(INPUTS)
putty.info: $(INPUTS)
$(HALIBUT) --info $(INPUTS)
-chm: putty.hhp
-putty.hhp: $(INPUTS) chm.but
- $(HALIBUT) --html $(INPUTS) chm.but
-
MKMAN = $(HALIBUT) --man=$@ mancfg.but $<
MANPAGES = putty.1 puttygen.1 plink.1 pscp.1 psftp.1 puttytel.1 pterm.1 \
pageant.1
diff --git a/doc/blurb.but b/doc/blurb.but
index 64e8ab74..e5e03a60 100644
--- a/doc/blurb.but
+++ b/doc/blurb.but
@@ -14,10 +14,16 @@ page.
}
\cfg{info-max-file-size}{0}
+\cfg{chm-contents-filename}{index.html}
+\cfg{chm-template-filename}{%k.html}
+\cfg{chm-head-end}{}
+\cfg{chm-extra-file}{chm.css}
+
\cfg{xhtml-contents-filename}{index.html}
\cfg{text-filename}{puttydoc.txt}
\cfg{winhelp-filename}{putty.hlp}
\cfg{info-filename}{putty.info}
+\cfg{chm-filename}{putty.chm}
PuTTY is a free (MIT-licensed) Windows Telnet and SSH client. This
manual documents PuTTY, and its companion utilities PSCP, PSFTP,
diff --git a/doc/chm.but b/doc/chm.but
deleted file mode 100644
index 44d1dca3..00000000
--- a/doc/chm.but
+++ /dev/null
@@ -1,22 +0,0 @@
-\# File containing the magic HTML configuration directives to create
-\# an MS HTML Help project. We put this on the end of the PuTTY
-\# docs build command line to build the HHP and friends.
-
-\cfg{html-leaf-level}{infinite}
-\cfg{html-leaf-contains-contents}{false}
-\cfg{html-suppress-navlinks}{true}
-\cfg{html-suppress-address}{true}
-
-\cfg{html-contents-filename}{index.html}
-\cfg{html-template-filename}{%k.html}
-\cfg{html-template-fragment}{%k}
-
-\cfg{html-mshtmlhelp-chm}{putty.chm}
-\cfg{html-mshtmlhelp-project}{putty.hhp}
-\cfg{html-mshtmlhelp-contents}{putty.hhc}
-\cfg{html-mshtmlhelp-index}{putty.hhk}
-
-\cfg{html-body-end}{}
-
-\cfg{html-head-end}{}
-
From 95f81227a29a79eeafb6395db159c376cafd6bc2 Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Mon, 30 May 2016 20:40:29 +0100
Subject: [PATCH 020/607] uxplink: remove the "connopen" variable.
It had the constant value 1 everywhere that it was read.
---
unix/uxplink.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/unix/uxplink.c b/unix/uxplink.c
index 2d4259b9..e891d66a 100644
--- a/unix/uxplink.c
+++ b/unix/uxplink.c
@@ -616,7 +616,6 @@ int main(int argc, char **argv)
int *fdlist;
int fd;
int i, fdcount, fdsize, fdstate;
- int connopen;
int exitcode;
int errors;
int use_subsystem = 0;
@@ -1027,7 +1026,6 @@ int main(int argc, char **argv)
ldisc_create(conf, NULL, back, backhandle, NULL);
sfree(realhost);
}
- connopen = 1;
/*
* Set up the initial console mode. We don't care if this call
@@ -1054,7 +1052,7 @@ int main(int argc, char **argv)
FD_SET_MAX(signalpipe[0], maxfd, rset);
- if (connopen && !sending &&
+ if (!sending &&
back->connected(backhandle) &&
back->sendok(backhandle) &&
back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {
@@ -1165,7 +1163,7 @@ int main(int argc, char **argv)
char buf[4096];
int ret;
- if (connopen && back->connected(backhandle)) {
+ if (back->connected(backhandle)) {
ret = read(STDIN_FILENO, buf, sizeof(buf));
if (ret < 0) {
perror("stdin: read");
@@ -1192,7 +1190,7 @@ int main(int argc, char **argv)
run_toplevel_callbacks();
- if ((!connopen || !back->connected(backhandle)) &&
+ if (!back->connected(backhandle) &&
bufchain_size(&stdout_data) == 0 &&
bufchain_size(&stderr_data) == 0)
break; /* we closed the connection */
From 30cdaa7ca8bfa03d90e170c2260d71a5819996b2 Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Mon, 30 May 2016 22:37:32 +0100
Subject: [PATCH 021/607] unix: make select_result() return void.
Nothing was using its return value anyway.
---
unix/unix.h | 2 +-
unix/uxsel.c | 6 ++----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/unix/unix.h b/unix/unix.h
index 7ab2ca24..68f749ac 100644
--- a/unix/unix.h
+++ b/unix/unix.h
@@ -183,7 +183,7 @@ void uxsel_init(void);
typedef int (*uxsel_callback_fn)(int fd, int event);
void uxsel_set(int fd, int rwx, uxsel_callback_fn callback);
void uxsel_del(int fd);
-int select_result(int fd, int event);
+void select_result(int fd, int event);
int first_fd(int *state, int *rwx);
int next_fd(int *state, int *rwx);
/* The following are expected to be provided _to_ uxsel.c by the frontend */
diff --git a/unix/uxsel.c b/unix/uxsel.c
index ef25cdb5..52f346a4 100644
--- a/unix/uxsel.c
+++ b/unix/uxsel.c
@@ -111,7 +111,7 @@ int first_fd(int *state, int *rwx)
return next_fd(state, rwx);
}
-int select_result(int fd, int event)
+void select_result(int fd, int event)
{
struct fd *fdstruct = find234(fds, &fd, uxsel_fd_findcmp);
/*
@@ -120,7 +120,5 @@ int select_result(int fd, int event)
* fd I've stopped being interested in. Sigh.
*/
if (fdstruct)
- return fdstruct->callback(fd, event);
- else
- return 1;
+ fdstruct->callback(fd, event);
}
From d56496c31c85767b805f261f01fc87cc8a468dec Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Mon, 30 May 2016 22:52:30 +0100
Subject: [PATCH 022/607] unix: make uxsel callback functions return void.
Nothing used their return values anyway. At least, not after the
previous commit.
---
unix/unix.h | 2 +-
unix/uxagentc.c | 7 +++----
unix/uxnet.c | 24 +++++++++++-------------
unix/uxproxy.c | 15 ++++++---------
unix/uxpty.c | 13 ++++---------
unix/uxser.c | 12 +++++-------
6 files changed, 30 insertions(+), 43 deletions(-)
diff --git a/unix/unix.h b/unix/unix.h
index 68f749ac..f21d23ff 100644
--- a/unix/unix.h
+++ b/unix/unix.h
@@ -180,7 +180,7 @@ void postmsg(struct termios *);
/* The interface used by uxsel.c */
typedef struct uxsel_id uxsel_id;
void uxsel_init(void);
-typedef int (*uxsel_callback_fn)(int fd, int event);
+typedef void (*uxsel_callback_fn)(int fd, int event);
void uxsel_set(int fd, int rwx, uxsel_callback_fn callback);
void uxsel_del(int fd);
void select_result(int fd, int event);
diff --git a/unix/uxagentc.c b/unix/uxagentc.c
index ffc5879c..51f9a1eb 100644
--- a/unix/uxagentc.c
+++ b/unix/uxagentc.c
@@ -98,7 +98,7 @@ void agent_cancel_query(agent_pending_query *conn)
sfree(conn);
}
-static int agent_select_result(int fd, int event)
+static void agent_select_result(int fd, int event)
{
agent_pending_query *conn;
@@ -107,11 +107,11 @@ static int agent_select_result(int fd, int event)
conn = find234(agent_pending_queries, &fd, agent_connfind);
if (!conn) {
uxsel_del(fd);
- return 1;
+ return;
}
if (!agent_try_read(conn))
- return 0; /* more data to come */
+ return; /* more data to come */
/*
* We have now completed the agent query. Do the callback, and
@@ -120,7 +120,6 @@ static int agent_select_result(int fd, int event)
*/
conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen);
agent_cancel_query(conn);
- return 0;
}
agent_pending_query *agent_query(
diff --git a/unix/uxnet.c b/unix/uxnet.c
index 79f4fbce..ddcd9228 100644
--- a/unix/uxnet.c
+++ b/unix/uxnet.c
@@ -1266,7 +1266,7 @@ static void sk_tcp_write_eof(Socket sock)
uxsel_tell(s);
}
-static int net_select_result(int fd, int event)
+static void net_select_result(int fd, int event)
{
int ret;
char buf[20480]; /* nice big buffer for plenty of speed */
@@ -1276,7 +1276,7 @@ static int net_select_result(int fd, int event)
/* Find the Socket structure */
s = find234(sktree, &fd, cmpforsearch);
if (!s)
- return 1; /* boggle */
+ return; /* boggle */
noise_ultralight(event);
@@ -1292,9 +1292,9 @@ static int net_select_result(int fd, int event)
ret = recv(s->s, buf, sizeof(buf), MSG_OOB);
noise_ultralight(ret);
if (ret <= 0) {
- return plug_closing(s->plug,
- ret == 0 ? "Internal networking trouble" :
- strerror(errno), errno, 0);
+ plug_closing(s->plug,
+ ret == 0 ? "Internal networking trouble" :
+ strerror(errno), errno, 0);
} else {
/*
* Receiving actual data on a socket means we can
@@ -1305,7 +1305,7 @@ static int net_select_result(int fd, int event)
sk_addr_free(s->addr);
s->addr = NULL;
}
- return plug_receive(s->plug, 2, buf, ret);
+ plug_receive(s->plug, 2, buf, ret);
}
break;
}
@@ -1377,11 +1377,11 @@ static int net_select_result(int fd, int event)
}
}
if (ret < 0) {
- return plug_closing(s->plug, strerror(errno), errno, 0);
+ plug_closing(s->plug, strerror(errno), errno, 0);
} else if (0 == ret) {
s->incomingeof = TRUE; /* stop trying to read now */
uxsel_tell(s);
- return plug_closing(s->plug, NULL, 0, 0);
+ plug_closing(s->plug, NULL, 0, 0);
} else {
/*
* Receiving actual data on a socket means we can
@@ -1392,7 +1392,7 @@ static int net_select_result(int fd, int event)
sk_addr_free(s->addr);
s->addr = NULL;
}
- return plug_receive(s->plug, atmark ? 0 : 1, buf, ret);
+ plug_receive(s->plug, atmark ? 0 : 1, buf, ret);
}
break;
case 2: /* writable */
@@ -1430,9 +1430,9 @@ static int net_select_result(int fd, int event)
err = try_connect(s);
}
if (err)
- return plug_closing(s->plug, strerror(err), err, 0);
+ plug_closing(s->plug, strerror(err), err, 0);
if (!s->connected)
- return 0; /* another async attempt in progress */
+ return; /* another async attempt in progress */
}
}
@@ -1456,8 +1456,6 @@ static int net_select_result(int fd, int event)
}
break;
}
-
- return 1;
}
/*
diff --git a/unix/uxproxy.c b/unix/uxproxy.c
index 3df4cebe..d3a82fa4 100644
--- a/unix/uxproxy.c
+++ b/unix/uxproxy.c
@@ -33,7 +33,7 @@ struct Socket_localproxy_tag {
enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
};
-static int localproxy_select_result(int fd, int event);
+static void localproxy_select_result(int fd, int event);
/*
* Trees to look up the pipe fds in.
@@ -229,7 +229,7 @@ static const char * sk_localproxy_socket_error (Socket s)
return ps->error;
}
-static int localproxy_select_result(int fd, int event)
+static void localproxy_select_result(int fd, int event)
{
Local_Proxy_Socket s;
char buf[20480];
@@ -238,7 +238,7 @@ static int localproxy_select_result(int fd, int event)
if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&
!(s = find234(localproxy_by_fromfd, &fd, localproxy_errfd_find)) &&
!(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )
- return 1; /* boggle */
+ return; /* boggle */
if (event == 1) {
if (fd == s->cmd_err) {
@@ -249,21 +249,18 @@ static int localproxy_select_result(int fd, int event)
assert(fd == s->from_cmd);
ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
- return plug_closing(s->plug, strerror(errno), errno, 0);
+ plug_closing(s->plug, strerror(errno), errno, 0);
} else if (ret == 0) {
- return plug_closing(s->plug, NULL, 0, 0);
+ plug_closing(s->plug, NULL, 0, 0);
} else {
- return plug_receive(s->plug, 0, buf, ret);
+ plug_receive(s->plug, 0, buf, ret);
}
}
} else if (event == 2) {
assert(fd == s->to_cmd);
if (localproxy_try_send(s))
plug_sent(s->plug, bufchain_size(&s->pending_output_data));
- return 1;
}
-
- return 1;
}
Socket platform_new_connection(SockAddr addr, const char *hostname,
diff --git a/unix/uxpty.c b/unix/uxpty.c
index 39f96f12..618fe9bd 100644
--- a/unix/uxpty.c
+++ b/unix/uxpty.c
@@ -572,7 +572,7 @@ void pty_pre_init(void)
}
-int pty_real_select_result(Pty pty, int event, int status)
+void pty_real_select_result(Pty pty, int event, int status)
{
char buf[4096];
int ret;
@@ -672,13 +672,10 @@ int pty_real_select_result(Pty pty, int event, int status)
notify_remote_exit(pty->frontend);
}
-
- return !finished;
}
-int pty_select_result(int fd, int event)
+void pty_select_result(int fd, int event)
{
- int ret = TRUE;
Pty pty;
if (fd == pty_signal_pipe[0]) {
@@ -696,16 +693,14 @@ int pty_select_result(int fd, int event)
pty = find234(ptys_by_pid, &pid, pty_find_by_pid);
if (pty)
- ret = ret && pty_real_select_result(pty, -1, status);
+ pty_real_select_result(pty, -1, status);
} while (pid > 0);
} else {
pty = find234(ptys_by_fd, &fd, pty_find_by_fd);
if (pty)
- ret = ret && pty_real_select_result(pty, event, 0);
+ pty_real_select_result(pty, event, 0);
}
-
- return ret;
}
static void pty_uxsel_setup(Pty pty)
diff --git a/unix/uxser.c b/unix/uxser.c
index 41beaf0e..e77f797a 100644
--- a/unix/uxser.c
+++ b/unix/uxser.c
@@ -56,7 +56,7 @@ static int serial_find_by_fd(void *av, void *bv)
static tree234 *serial_by_fd = NULL;
-static int serial_select_result(int fd, int event);
+static void serial_select_result(int fd, int event);
static void serial_uxsel_setup(Serial serial);
static void serial_try_write(Serial serial);
@@ -366,7 +366,7 @@ static void serial_reconfig(void *handle, Conf *conf)
serial_configure(serial, conf);
}
-static int serial_select_result(int fd, int event)
+static void serial_select_result(int fd, int event)
{
Serial serial;
char buf[4096];
@@ -376,7 +376,7 @@ static int serial_select_result(int fd, int event)
serial = find234(serial_by_fd, &fd, serial_find_by_fd);
if (!serial)
- return 1; /* spurious event; keep going */
+ return; /* spurious event; keep going */
if (event == 1) {
ret = read(serial->fd, buf, sizeof(buf));
@@ -391,11 +391,11 @@ static int serial_select_result(int fd, int event)
} else if (ret < 0) {
#ifdef EAGAIN
if (errno == EAGAIN)
- return 1; /* spurious */
+ return; /* spurious */
#endif
#ifdef EWOULDBLOCK
if (errno == EWOULDBLOCK)
- return 1; /* spurious */
+ return; /* spurious */
#endif
perror("read serial port");
exit(1);
@@ -417,8 +417,6 @@ static int serial_select_result(int fd, int event)
notify_remote_exit(serial->frontend);
}
-
- return !finished;
}
static void serial_uxsel_setup(Serial serial)
From f65c31667e0dee8d416df41dac66198a80c3314b Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Mon, 30 May 2016 20:47:04 +0100
Subject: [PATCH 023/607] winplink: remove "connopen" variable.
It's redundant with back->connected(): only the SSH backend has a
receive function that can ever return 0, and whenever ssh_receive
returns 0 it has called ssh_do_close, which will cause future calls
to ssh_connected also to return 0. Similarly, all backend closing
functions ensure that future calls to their connected function will
return 0.
---
windows/winplink.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/windows/winplink.c b/windows/winplink.c
index 47470777..a1a8dda0 100644
--- a/windows/winplink.c
+++ b/windows/winplink.c
@@ -87,7 +87,6 @@ void cmdline_error(const char *p, ...)
HANDLE inhandle, outhandle, errhandle;
struct handle *stdin_handle, *stdout_handle, *stderr_handle;
DWORD orig_console_mode;
-int connopen;
WSAEVENT netevent;
@@ -267,7 +266,7 @@ int stdin_gotdata(struct handle *h, void *data, int len)
cleanup_exit(0);
}
noise_ultralight(len);
- if (connopen && back->connected(backhandle)) {
+ if (back->connected(backhandle)) {
if (len > 0) {
return back->send(backhandle, data, len);
} else {
@@ -294,7 +293,7 @@ void stdouterr_sent(struct handle *h, int new_backlog)
(h == stdout_handle ? "output" : "error"), buf);
cleanup_exit(0);
}
- if (connopen && back->connected(backhandle)) {
+ if (back->connected(backhandle)) {
back->unthrottle(backhandle, (handle_backlog(stdout_handle) +
handle_backlog(stderr_handle)));
}
@@ -662,7 +661,6 @@ int main(int argc, char **argv)
back->provide_logctx(backhandle, logctx);
sfree(realhost);
}
- connopen = 1;
inhandle = GetStdHandle(STD_INPUT_HANDLE);
outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -782,7 +780,7 @@ int main(int argc, char **argv)
LPARAM lp;
int err = things.iErrorCode[eventtypes[e].bit];
lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);
- connopen &= select_result(wp, lp);
+ select_result(wp, lp);
}
}
}
@@ -810,7 +808,7 @@ int main(int argc, char **argv)
if (sending)
handle_unthrottle(stdin_handle, back->sendbuffer(backhandle));
- if ((!connopen || !back->connected(backhandle)) &&
+ if (!back->connected(backhandle) &&
handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0)
break; /* we closed the connection */
}
From 70e2e140f054f5cd894d0fd7f2c31a23cd7f7377 Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Thu, 2 Jun 2016 22:37:36 +0100
Subject: [PATCH 024/607] windows: Remove spurious redeclarations of
select_result().
---
windows/winplink.c | 1 -
windows/winsftp.c | 2 --
2 files changed, 3 deletions(-)
diff --git a/windows/winplink.c b/windows/winplink.c
index a1a8dda0..ea417501 100644
--- a/windows/winplink.c
+++ b/windows/winplink.c
@@ -727,7 +727,6 @@ int main(int argc, char **argv)
WSANETWORKEVENTS things;
SOCKET socket;
extern SOCKET first_socket(int *), next_socket(int *);
- extern int select_result(WPARAM, LPARAM);
int i, socketstate;
/*
diff --git a/windows/winsftp.c b/windows/winsftp.c
index e25d7e06..4558e5a9 100644
--- a/windows/winsftp.c
+++ b/windows/winsftp.c
@@ -498,7 +498,6 @@ char *do_select(SOCKET skt, int startup)
}
return NULL;
}
-extern int select_result(WPARAM, LPARAM);
int do_eventsel_loop(HANDLE other_event)
{
@@ -547,7 +546,6 @@ int do_eventsel_loop(HANDLE other_event)
WSANETWORKEVENTS things;
SOCKET socket;
extern SOCKET first_socket(int *), next_socket(int *);
- extern int select_result(WPARAM, LPARAM);
int i, socketstate;
/*
From a2fb1d96ef4660f94c2bed6cad1749c10013d351 Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Thu, 2 Jun 2016 22:38:36 +0100
Subject: [PATCH 025/607] windows: Make select_result() return void.
Nothing now uses its return value anyway.
---
windows/winnet.c | 33 +++++++++++++--------------------
windows/winstuff.h | 2 +-
2 files changed, 14 insertions(+), 21 deletions(-)
diff --git a/windows/winnet.c b/windows/winnet.c
index fc26c3e5..0ec64a55 100644
--- a/windows/winnet.c
+++ b/windows/winnet.c
@@ -1625,9 +1625,9 @@ static void sk_tcp_write_eof(Socket sock)
try_send(s);
}
-int select_result(WPARAM wParam, LPARAM lParam)
+void select_result(WPARAM wParam, LPARAM lParam)
{
- int ret, open;
+ int ret;
DWORD err;
char buf[20480]; /* nice big buffer for plenty of speed */
Actual_Socket s;
@@ -1636,11 +1636,11 @@ int select_result(WPARAM wParam, LPARAM lParam)
/* wParam is the socket itself */
if (wParam == 0)
- return 1; /* boggle */
+ return; /* boggle */
s = find234(sktree, (void *) wParam, cmpforsearch);
if (!s)
- return 1; /* boggle */
+ return; /* boggle */
if ((err = WSAGETSELECTERROR(lParam)) != 0) {
/*
@@ -1657,9 +1657,8 @@ int select_result(WPARAM wParam, LPARAM lParam)
}
}
if (err != 0)
- return plug_closing(s->plug, winsock_error_string(err), err, 0);
- else
- return 1;
+ plug_closing(s->plug, winsock_error_string(err), err, 0);
+ return;
}
noise_ultralight(lParam);
@@ -1712,12 +1711,11 @@ int select_result(WPARAM wParam, LPARAM lParam)
}
}
if (ret < 0) {
- return plug_closing(s->plug, winsock_error_string(err), err,
- 0);
+ plug_closing(s->plug, winsock_error_string(err), err, 0);
} else if (0 == ret) {
- return plug_closing(s->plug, NULL, 0, 0);
+ plug_closing(s->plug, NULL, 0, 0);
} else {
- return plug_receive(s->plug, atmark ? 0 : 1, buf, ret);
+ plug_receive(s->plug, atmark ? 0 : 1, buf, ret);
}
break;
case FD_OOB:
@@ -1737,7 +1735,7 @@ int select_result(WPARAM wParam, LPARAM lParam)
logevent(NULL, str);
fatalbox("%s", str);
} else {
- return plug_receive(s->plug, 2, buf, ret);
+ plug_receive(s->plug, 2, buf, ret);
}
break;
case FD_WRITE:
@@ -1753,23 +1751,20 @@ int select_result(WPARAM wParam, LPARAM lParam)
break;
case FD_CLOSE:
/* Signal a close on the socket. First read any outstanding data. */
- open = 1;
do {
ret = p_recv(s->s, buf, sizeof(buf), 0);
if (ret < 0) {
err = p_WSAGetLastError();
if (err == WSAEWOULDBLOCK)
break;
- return plug_closing(s->plug, winsock_error_string(err),
- err, 0);
+ plug_closing(s->plug, winsock_error_string(err), err, 0);
} else {
if (ret)
- open &= plug_receive(s->plug, 0, buf, ret);
+ plug_receive(s->plug, 0, buf, ret);
else
- open &= plug_closing(s->plug, NULL, 0, 0);
+ plug_closing(s->plug, NULL, 0, 0);
}
} while (ret > 0);
- return open;
case FD_ACCEPT:
{
#ifdef NO_IPV6
@@ -1807,8 +1802,6 @@ int select_result(WPARAM wParam, LPARAM lParam)
}
}
}
-
- return 1;
}
/*
diff --git a/windows/winstuff.h b/windows/winstuff.h
index f8c4243f..dfa032f4 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -284,7 +284,7 @@ GLOBAL void *logctx;
/*
* Exports from winnet.c.
*/
-extern int select_result(WPARAM, LPARAM);
+extern void select_result(WPARAM, LPARAM);
/*
* winnet.c dynamically loads WinSock 2 or WinSock 1 depending on
From 0d9c7d82e8506d9b6da557cfaf8a08bc3bfeb299 Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Sat, 4 Jun 2016 15:42:06 +0100
Subject: [PATCH 026/607] Don't treat plug_closing() and plug_receive() as
returning backlog.
plug_receive() and plug_closing() return 0 or 1 depending on whether
they think the main connection has closed. It is not appropriate, as
handle_gotdata and handle_socket_unfreeze did, to treat them as
returning a backlog. In fact, plugs are unusual in PuTTY in not
reporting a backlog, but just calling into the socket to freeze and
unfreeze it as required.
---
windows/winhsock.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/windows/winhsock.c b/windows/winhsock.c
index c8cb46f6..799fd28d 100644
--- a/windows/winhsock.c
+++ b/windows/winhsock.c
@@ -54,10 +54,11 @@ static int handle_gotdata(struct handle *h, void *data, int len)
Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
if (len < 0) {
- return plug_closing(ps->plug, "Read error from handle",
- 0, 0);
+ plug_closing(ps->plug, "Read error from handle", 0, 0);
+ return 0;
} else if (len == 0) {
- return plug_closing(ps->plug, NULL, 0, 0);
+ plug_closing(ps->plug, NULL, 0, 0);
+ return 0;
} else {
assert(ps->frozen != FROZEN && ps->frozen != THAWING);
if (ps->frozen == FREEZING) {
@@ -76,7 +77,8 @@ static int handle_gotdata(struct handle *h, void *data, int len)
*/
return INT_MAX;
} else {
- return plug_receive(ps->plug, 0, data, len);
+ plug_receive(ps->plug, 0, data, len);
+ return 0;
}
}
}
@@ -168,7 +170,7 @@ static void handle_socket_unfreeze(void *psv)
{
Handle_Socket ps = (Handle_Socket) psv;
void *data;
- int len, new_backlog;
+ int len;
/*
* If we've been put into a state other than THAWING since the
@@ -188,7 +190,7 @@ static void handle_socket_unfreeze(void *psv)
* have the effect of trying to close this socket.
*/
ps->defer_close = TRUE;
- new_backlog = plug_receive(ps->plug, 0, data, len);
+ plug_receive(ps->plug, 0, data, len);
bufchain_consume(&ps->inputdata, len);
ps->defer_close = FALSE;
if (ps->deferred_close) {
@@ -207,7 +209,7 @@ static void handle_socket_unfreeze(void *psv)
* Otherwise, we've successfully thawed!
*/
ps->frozen = UNFROZEN;
- handle_unthrottle(ps->recv_h, new_backlog);
+ handle_unthrottle(ps->recv_h, 0);
}
}
From 0d57b8a4d9c8f487c6a8806384cb2c485161d19c Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Thu, 2 Jun 2016 23:03:24 +0100
Subject: [PATCH 027/607] Make plug receive and closing functions return void
instead of int.
Nothing was paying attention to their return values any more anyway.
---
network.h | 4 ++--
pageant.c | 18 ++++++++----------
portfwd.c | 25 ++++++++++---------------
proxy.c | 41 +++++++++++++++++++++--------------------
raw.c | 8 +++-----
rlogin.c | 8 +++-----
ssh.c | 9 +++------
sshshare.c | 25 +++++++++++--------------
telnet.c | 8 +++-----
unix/uxpgnt.c | 7 +++----
x11fwd.c | 18 ++++++------------
11 files changed, 73 insertions(+), 98 deletions(-)
diff --git a/network.h b/network.h
index d58635b6..0941f721 100644
--- a/network.h
+++ b/network.h
@@ -64,12 +64,12 @@ struct plug_function_table {
* proxy command, so the receiver should probably prefix it to
* indicate this.
*/
- int (*closing)
+ void (*closing)
(Plug p, const char *error_msg, int error_code, int calling_back);
/* error_msg is NULL iff it is not an error (ie it closed normally) */
/* calling_back != 0 iff there is a Plug function */
/* currently running (would cure the fixme in try_send()) */
- int (*receive) (Plug p, int urgent, char *data, int len);
+ void (*receive) (Plug p, int urgent, char *data, int len);
/*
* - urgent==0. `data' points to `len' bytes of perfectly
* ordinary data.
diff --git a/pageant.c b/pageant.c
index 36671725..a168e522 100644
--- a/pageant.c
+++ b/pageant.c
@@ -964,11 +964,11 @@ int pageant_delete_ssh2_key(struct ssh2_userkey *skey)
* Coroutine macros similar to, but simplified from, those in ssh.c.
*/
#define crBegin(v) { int *crLine = &v; switch(v) { case 0:;
-#define crFinish(z) } *crLine = 0; return (z); }
+#define crFinishV } *crLine = 0; return; }
#define crGetChar(c) do \
{ \
while (len == 0) { \
- *crLine =__LINE__; return 1; case __LINE__:; \
+ *crLine =__LINE__; return; case __LINE__:; \
} \
len--; \
(c) = (unsigned char)*data++; \
@@ -987,8 +987,8 @@ struct pageant_conn_state {
int crLine; /* for coroutine in pageant_conn_receive */
};
-static int pageant_conn_closing(Plug plug, const char *error_msg,
- int error_code, int calling_back)
+static void pageant_conn_closing(Plug plug, const char *error_msg,
+ int error_code, int calling_back)
{
struct pageant_conn_state *pc = (struct pageant_conn_state *)plug;
if (error_msg)
@@ -997,7 +997,6 @@ static int pageant_conn_closing(Plug plug, const char *error_msg,
plog(pc->logctx, pc->logfn, "%p: connection closed", pc);
sk_close(pc->connsock);
sfree(pc);
- return 1;
}
static void pageant_conn_sent(Plug plug, int bufsize)
@@ -1021,7 +1020,7 @@ static void pageant_conn_log(void *logctx, const char *fmt, va_list ap)
sfree(formatted);
}
-static int pageant_conn_receive(Plug plug, int urgent, char *data, int len)
+static void pageant_conn_receive(Plug plug, int urgent, char *data, int len)
{
struct pageant_conn_state *pc = (struct pageant_conn_state *)plug;
char c;
@@ -1065,7 +1064,7 @@ static int pageant_conn_receive(Plug plug, int urgent, char *data, int len)
}
}
- crFinish(1);
+ crFinishV;
}
struct pageant_listen_state {
@@ -1077,15 +1076,14 @@ struct pageant_listen_state {
pageant_logfn_t logfn;
};
-static int pageant_listen_closing(Plug plug, const char *error_msg,
- int error_code, int calling_back)
+static void pageant_listen_closing(Plug plug, const char *error_msg,
+ int error_code, int calling_back)
{
struct pageant_listen_state *pl = (struct pageant_listen_state *)plug;
if (error_msg)
plog(pl->logctx, pl->logfn, "listening socket: error: %s", error_msg);
sk_close(pl->listensock);
pl->listensock = NULL;
- return 1;
}
static int pageant_listen_accepting(Plug plug,
diff --git a/portfwd.c b/portfwd.c
index 8a73a182..febd8af9 100644
--- a/portfwd.c
+++ b/portfwd.c
@@ -117,8 +117,8 @@ static void pfl_log(Plug plug, int type, SockAddr addr, int port,
/* we have to dump these since we have no interface to logging.c */
}
-static int pfd_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back)
+static void pfd_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back)
{
struct PortForwarding *pf = (struct PortForwarding *) plug;
@@ -145,16 +145,13 @@ static int pfd_closing(Plug plug, const char *error_msg, int error_code,
if (pf->c)
sshfwd_write_eof(pf->c);
}
-
- return 1;
}
-static int pfl_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back)
+static void pfl_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back)
{
struct PortListener *pl = (struct PortListener *) plug;
pfl_terminate(pl);
- return 1;
}
static void wrap_send_port_open(void *channel, const char *hostname, int port,
@@ -172,7 +169,7 @@ static void wrap_send_port_open(void *channel, const char *hostname, int port,
sfree(description);
}
-static int pfd_receive(Plug plug, int urgent, char *data, int len)
+static void pfd_receive(Plug plug, int urgent, char *data, int len)
{
struct PortForwarding *pf = (struct PortForwarding *) plug;
if (pf->dynamic) {
@@ -204,7 +201,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
data[1] = 91; /* generic `request rejected' */
sk_write(pf->s, data, 8);
pfd_close(pf);
- return 1;
+ return;
}
if (pf->sockslen <= 8)
continue; /* haven't started user/hostname */
@@ -320,7 +317,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
reply[1] = 1; /* generic failure */
sk_write(pf->s, (char *) reply, lenof(reply));
pfd_close(pf);
- return 1;
+ return;
}
/*
* Now we have a viable connect request. Switch
@@ -350,7 +347,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
reply[1] = 8; /* atype not supported */
sk_write(pf->s, (char *) reply, lenof(reply));
pfd_close(pf);
- return 1;
+ return;
}
}
}
@@ -362,9 +359,8 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
* close the connection rudely.
*/
pfd_close(pf);
- return 1;
}
- return 1;
+ return;
/*
* We come here when we're ready to make an actual
@@ -383,7 +379,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
pf->c = new_sock_channel(pf->backhandle, pf);
if (pf->c == NULL) {
pfd_close(pf);
- return 1;
+ return;
} else {
/* asks to forward to the specified host/port for this */
wrap_send_port_open(pf->c, pf->hostname, pf->port, pf->s);
@@ -406,7 +402,6 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
sk_set_frozen(pf->s, 1);
}
}
- return 1;
}
static void pfd_sent(Plug plug, int bufsize)
diff --git a/proxy.c b/proxy.c
index 52006794..68b7e9a0 100644
--- a/proxy.c
+++ b/proxy.c
@@ -201,8 +201,8 @@ static void plug_proxy_log(Plug plug, int type, SockAddr addr, int port,
plug_log(ps->plug, type, addr, port, error_msg, error_code);
}
-static int plug_proxy_closing (Plug p, const char *error_msg,
- int error_code, int calling_back)
+static void plug_proxy_closing (Plug p, const char *error_msg,
+ int error_code, int calling_back)
{
Proxy_Plug pp = (Proxy_Plug) p;
Proxy_Socket ps = pp->proxy_socket;
@@ -211,13 +211,13 @@ static int plug_proxy_closing (Plug p, const char *error_msg,
ps->closing_error_msg = error_msg;
ps->closing_error_code = error_code;
ps->closing_calling_back = calling_back;
- return ps->negotiate(ps, PROXY_CHANGE_CLOSING);
+ ps->negotiate(ps, PROXY_CHANGE_CLOSING);
+ } else {
+ plug_closing(ps->plug, error_msg, error_code, calling_back);
}
- return plug_closing(ps->plug, error_msg,
- error_code, calling_back);
}
-static int plug_proxy_receive (Plug p, int urgent, char *data, int len)
+static void plug_proxy_receive (Plug p, int urgent, char *data, int len)
{
Proxy_Plug pp = (Proxy_Plug) p;
Proxy_Socket ps = pp->proxy_socket;
@@ -231,9 +231,10 @@ static int plug_proxy_receive (Plug p, int urgent, char *data, int len)
ps->receive_urgent = urgent;
ps->receive_data = data;
ps->receive_len = len;
- return ps->negotiate(ps, PROXY_CHANGE_RECEIVE);
+ ps->negotiate(ps, PROXY_CHANGE_RECEIVE);
+ } else {
+ plug_receive(ps->plug, urgent, data, len);
}
- return plug_receive(ps->plug, urgent, data, len);
}
static void plug_proxy_sent (Plug p, int bufsize)
@@ -644,9 +645,9 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
* a socket close, then some error must have occurred. we'll
* just pass those errors up to the backend.
*/
- return plug_closing(p->plug, p->closing_error_msg,
- p->closing_error_code,
- p->closing_calling_back);
+ plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,
+ p->closing_calling_back);
+ return 0; /* ignored */
}
if (change == PROXY_CHANGE_SENT) {
@@ -847,9 +848,9 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
* a socket close, then some error must have occurred. we'll
* just pass those errors up to the backend.
*/
- return plug_closing(p->plug, p->closing_error_msg,
- p->closing_error_code,
- p->closing_calling_back);
+ plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,
+ p->closing_calling_back);
+ return 0; /* ignored */
}
if (change == PROXY_CHANGE_SENT) {
@@ -987,9 +988,9 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
* a socket close, then some error must have occurred. we'll
* just pass those errors up to the backend.
*/
- return plug_closing(p->plug, p->closing_error_msg,
- p->closing_error_code,
- p->closing_calling_back);
+ plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,
+ p->closing_calling_back);
+ return 0; /* ignored */
}
if (change == PROXY_CHANGE_SENT) {
@@ -1561,9 +1562,9 @@ int proxy_telnet_negotiate (Proxy_Socket p, int change)
* a socket close, then some error must have occurred. we'll
* just pass those errors up to the backend.
*/
- return plug_closing(p->plug, p->closing_error_msg,
- p->closing_error_code,
- p->closing_calling_back);
+ plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,
+ p->closing_calling_back);
+ return 0; /* ignored */
}
if (change == PROXY_CHANGE_SENT) {
diff --git a/raw.c b/raw.c
index 0c5445ad..fbc9018d 100644
--- a/raw.c
+++ b/raw.c
@@ -61,8 +61,8 @@ static void raw_check_close(Raw raw)
}
}
-static int raw_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back)
+static void raw_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back)
{
Raw raw = (Raw) plug;
@@ -92,17 +92,15 @@ static int raw_closing(Plug plug, const char *error_msg, int error_code,
raw->sent_console_eof = TRUE;
raw_check_close(raw);
}
- return 0;
}
-static int raw_receive(Plug plug, int urgent, char *data, int len)
+static void raw_receive(Plug plug, int urgent, char *data, int len)
{
Raw raw = (Raw) plug;
c_write(raw, data, len);
/* We count 'session start', for proxy logging purposes, as being
* when data is received from the network and printed. */
raw->session_started = TRUE;
- return 1;
}
static void raw_sent(Plug plug, int bufsize)
diff --git a/rlogin.c b/rlogin.c
index eba468da..ff2c0a8f 100644
--- a/rlogin.c
+++ b/rlogin.c
@@ -53,8 +53,8 @@ static void rlogin_log(Plug plug, int type, SockAddr addr, int port,
rlogin->conf, !rlogin->firstbyte);
}
-static int rlogin_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back)
+static void rlogin_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back)
{
Rlogin rlogin = (Rlogin) plug;
@@ -76,10 +76,9 @@ static int rlogin_closing(Plug plug, const char *error_msg, int error_code,
logevent(rlogin->frontend, error_msg);
connection_fatal(rlogin->frontend, "%s", error_msg);
} /* Otherwise, the remote side closed the connection normally. */
- return 0;
}
-static int rlogin_receive(Plug plug, int urgent, char *data, int len)
+static void rlogin_receive(Plug plug, int urgent, char *data, int len)
{
Rlogin rlogin = (Rlogin) plug;
if (urgent == 2) {
@@ -113,7 +112,6 @@ static int rlogin_receive(Plug plug, int urgent, char *data, int len)
if (len > 0)
c_write(rlogin, data, len);
}
- return 1;
}
static void rlogin_sent(Plug plug, int bufsize)
diff --git a/ssh.c b/ssh.c
index 994e93ae..b6298b03 100644
--- a/ssh.c
+++ b/ssh.c
@@ -3560,8 +3560,8 @@ void ssh_connshare_log(Ssh ssh, int event, const char *logtext,
}
}
-static int ssh_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back)
+static void ssh_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back)
{
Ssh ssh = (Ssh) plug;
int need_notify = ssh_do_close(ssh, FALSE);
@@ -3583,18 +3583,15 @@ static int ssh_closing(Plug plug, const char *error_msg, int error_code,
logevent(error_msg);
if (!ssh->close_expected || !ssh->clean_exit)
connection_fatal(ssh->frontend, "%s", error_msg);
- return 0;
}
-static int ssh_receive(Plug plug, int urgent, char *data, int len)
+static void ssh_receive(Plug plug, int urgent, char *data, int len)
{
Ssh ssh = (Ssh) plug;
ssh_gotdata(ssh, (unsigned char *)data, len);
if (ssh->state == SSH_STATE_CLOSED) {
ssh_do_close(ssh, TRUE);
- return 0;
}
- return 1;
}
static void ssh_sent(Plug plug, int bufsize)
diff --git a/sshshare.c b/sshshare.c
index 82c4bd31..59cdad45 100644
--- a/sshshare.c
+++ b/sshshare.c
@@ -911,8 +911,8 @@ static void share_disconnect(struct ssh_sharing_connstate *cs,
share_begin_cleanup(cs);
}
-static int share_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back)
+static void share_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back)
{
struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)plug;
@@ -935,7 +935,6 @@ static int share_closing(Plug plug, const char *error_msg, int error_code,
"Socket error: %s", error_msg);
}
share_begin_cleanup(cs);
- return 1;
}
static int getstring_inner(const void *vdata, int datalen,
@@ -1775,17 +1774,17 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* Coroutine macros similar to, but simplified from, those in ssh.c.
*/
#define crBegin(v) { int *crLine = &v; switch(v) { case 0:;
-#define crFinish(z) } *crLine = 0; return (z); }
+#define crFinishV } *crLine = 0; return; }
#define crGetChar(c) do \
{ \
while (len == 0) { \
- *crLine =__LINE__; return 1; case __LINE__:; \
+ *crLine =__LINE__; return; case __LINE__:; \
} \
len--; \
(c) = (unsigned char)*data++; \
} while (0)
-static int share_receive(Plug plug, int urgent, char *data, int len)
+static void share_receive(Plug plug, int urgent, char *data, int len)
{
struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)plug;
static const char expected_verstring_prefix[] =
@@ -1858,7 +1857,7 @@ static int share_receive(Plug plug, int urgent, char *data, int len)
}
dead:;
- crFinish(1);
+ crFinishV;
}
static void share_sent(Plug plug, int bufsize)
@@ -1875,8 +1874,8 @@ static void share_sent(Plug plug, int bufsize)
*/
}
-static int share_listen_closing(Plug plug, const char *error_msg,
- int error_code, int calling_back)
+static void share_listen_closing(Plug plug, const char *error_msg,
+ int error_code, int calling_back)
{
struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)plug;
if (error_msg)
@@ -1884,7 +1883,6 @@ static int share_listen_closing(Plug plug, const char *error_msg,
"listening socket: %s", error_msg);
sk_close(sharestate->listensock);
sharestate->listensock = NULL;
- return 1;
}
static void share_send_verstring(struct ssh_sharing_connstate *cs)
@@ -2047,10 +2045,9 @@ char *ssh_share_sockname(const char *host, int port, Conf *conf)
static void nullplug_socket_log(Plug plug, int type, SockAddr addr, int port,
const char *error_msg, int error_code) {}
-static int nullplug_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back) { return 0; }
-static int nullplug_receive(Plug plug, int urgent, char *data,
- int len) { return 0; }
+static void nullplug_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back) {}
+static void nullplug_receive(Plug plug, int urgent, char *data, int len) {}
static void nullplug_sent(Plug plug, int bufsize) {}
int ssh_share_test_for_upstream(const char *host, int port, Conf *conf)
diff --git a/telnet.c b/telnet.c
index c4b04132..8d03cb7b 100644
--- a/telnet.c
+++ b/telnet.c
@@ -658,8 +658,8 @@ static void telnet_log(Plug plug, int type, SockAddr addr, int port,
telnet->session_started);
}
-static int telnet_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back)
+static void telnet_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back)
{
Telnet telnet = (Telnet) plug;
@@ -681,17 +681,15 @@ static int telnet_closing(Plug plug, const char *error_msg, int error_code,
connection_fatal(telnet->frontend, "%s", error_msg);
}
/* Otherwise, the remote side closed the connection normally. */
- return 0;
}
-static int telnet_receive(Plug plug, int urgent, char *data, int len)
+static void telnet_receive(Plug plug, int urgent, char *data, int len)
{
Telnet telnet = (Telnet) plug;
if (urgent)
telnet->in_synch = TRUE;
telnet->session_started = TRUE;
do_telnet_read(telnet, data, len);
- return 1;
}
static void telnet_sent(Plug plug, int bufsize)
diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
index 41efcbc2..cb85b160 100644
--- a/unix/uxpgnt.c
+++ b/unix/uxpgnt.c
@@ -183,13 +183,12 @@ void sshfwd_x11_is_local(struct ssh_channel *c) {}
*/
static void x11_log(Plug p, int type, SockAddr addr, int port,
const char *error_msg, int error_code) {}
-static int x11_receive(Plug plug, int urgent, char *data, int len) {return 0;}
+static void x11_receive(Plug plug, int urgent, char *data, int len) {}
static void x11_sent(Plug plug, int bufsize) {}
-static int x11_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back)
+static void x11_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back)
{
time_to_die = TRUE;
- return 1;
}
struct X11Connection {
const struct plug_function_table *fn;
diff --git a/x11fwd.c b/x11fwd.c
index 584116aa..9d0a58de 100644
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -58,11 +58,9 @@ static int xdmseen_cmp(void *a, void *b)
* independent network.c or something */
static void dummy_plug_log(Plug p, int type, SockAddr addr, int port,
const char *error_msg, int error_code) { }
-static int dummy_plug_closing
- (Plug p, const char *error_msg, int error_code, int calling_back)
-{ return 1; }
-static int dummy_plug_receive(Plug p, int urgent, char *data, int len)
-{ return 1; }
+static void dummy_plug_closing
+ (Plug p, const char *error_msg, int error_code, int calling_back) { }
+static void dummy_plug_receive(Plug p, int urgent, char *data, int len) { }
static void dummy_plug_sent(Plug p, int bufsize) { }
static int dummy_plug_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx) { return 1; }
static const struct plug_function_table dummy_plug = {
@@ -616,8 +614,8 @@ static void x11_log(Plug p, int type, SockAddr addr, int port,
static void x11_send_init_error(struct X11Connection *conn,
const char *err_message);
-static int x11_closing(Plug plug, const char *error_msg, int error_code,
- int calling_back)
+static void x11_closing(Plug plug, const char *error_msg, int error_code,
+ int calling_back)
{
struct X11Connection *xconn = (struct X11Connection *) plug;
@@ -646,11 +644,9 @@ static int x11_closing(Plug plug, const char *error_msg, int error_code,
if (xconn->c)
sshfwd_write_eof(xconn->c);
}
-
- return 1;
}
-static int x11_receive(Plug plug, int urgent, char *data, int len)
+static void x11_receive(Plug plug, int urgent, char *data, int len)
{
struct X11Connection *xconn = (struct X11Connection *) plug;
@@ -659,8 +655,6 @@ static int x11_receive(Plug plug, int urgent, char *data, int len)
xconn->no_data_sent_to_x_client = FALSE;
sk_set_frozen(xconn->s, 1);
}
-
- return 1;
}
static void x11_sent(Plug plug, int bufsize)
From c7b9f846d9d4062dea9eeaca7b15c0a8caf0c73d Mon Sep 17 00:00:00 2001
From: Ben Harris
Date: Wed, 17 May 2017 23:02:14 +0100
Subject: [PATCH 028/607] windows: Fix control-flow error in select_result().
When making select_result() return void (a2fb1d9), I removed a "return"
at the end of the FD_CLOSE case, causing a fallthrough into FD_ACCEPT
with hilarious (segfaulting) consequences. Re-instate the "return".
---
windows/winnet.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/windows/winnet.c b/windows/winnet.c
index 0ec64a55..ea950bba 100644
--- a/windows/winnet.c
+++ b/windows/winnet.c
@@ -1765,6 +1765,7 @@ void select_result(WPARAM wParam, LPARAM lParam)
plug_closing(s->plug, NULL, 0, 0);
}
} while (ret > 0);
+ return;
case FD_ACCEPT:
{
#ifdef NO_IPV6
From 12bd5a6c722152aa27f24598785593e72b3284ea Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Sat, 20 May 2017 12:44:56 +0100
Subject: [PATCH 029/607] Stop Gtk2 builds exploding on scroll wheel events.
More fallout from 64221972c.
---
unix/gtkwin.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 67cfcac3..f54289a3 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -1923,7 +1923,7 @@ gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data)
return FALSE;
event_button = (GdkEventButton *)gdk_event_new(GDK_BUTTON_PRESS);
- event_button->window = event->window;
+ event_button->window = g_object_ref(event->window);
event_button->send_event = event->send_event;
event_button->time = event->time;
event_button->x = event->x;
@@ -1931,7 +1931,7 @@ gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data)
event_button->axes = NULL;
event_button->state = event->state;
event_button->button = button;
- event_button->device = event->device;
+ event_button->device = g_object_ref(event->device);
event_button->x_root = event->x_root;
event_button->y_root = event->y_root;
ret = button_internal(inst, event_button);
From 22cf2823d1c5615c259eadf8a3ebb199aba6f2d6 Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Tue, 23 May 2017 23:13:17 +0100
Subject: [PATCH 030/607] Remove some ancient cruft from the FAQ.
- I haven't heard of OpenSSH/OpenSSL mismatches being a common problem
for a long time. Specific advice about OpenSSH 3.1/3.4 seems unlikely
to be useful these days.
- "Incorrect MAC received on packet" doesn't seem to be a common
problem these days, and if anyone encounters it, the words in the
"Errors" bit of the docs seem adequate without a FAQ entry as well.
---
doc/errors.but | 18 +++++++--------
doc/faq.but | 63 --------------------------------------------------
2 files changed, 9 insertions(+), 72 deletions(-)
diff --git a/doc/errors.but b/doc/errors.but
index fdbdd861..8e353fb9 100644
--- a/doc/errors.but
+++ b/doc/errors.but
@@ -122,9 +122,7 @@ ridiculous amount of memory, and will terminate with an \q{Out of
memory} error.
This can happen in SSH-2, if PuTTY and the server have not enabled
-encryption in the same way (see \k{faq-outofmem} in the FAQ). Some
-versions of \i{OpenSSH} have a known problem with this: see
-\k{faq-openssh-bad-openssl}.
+encryption in the same way (see \k{faq-outofmem} in the FAQ).
This can also happen in PSCP or PSFTP, if your \i{login scripts} on the
server generate output: the client program will be expecting an SFTP
@@ -233,8 +231,13 @@ protection (such as HTTP) will manifest in more subtle failures (such
as misdisplayed text or images in a web browser) which may not be
noticed.
-A known server problem which can cause this error is described in
-\k{faq-openssh-bad-openssl} in the FAQ.
+Occasionally this has been caused by server bugs. An example is the
+bug described at \k{config-ssh-bug-hmac2}, although you're very
+unlikely to encounter that one these days.
+
+In this context MAC stands for \ii{Message Authentication Code}. It's a
+cryptographic term, and it has nothing at all to do with Ethernet
+MAC (Media Access Control) addresses, or with the Apple computer.
\H{errors-garbled} \q{Incoming packet was garbled on decryption}
@@ -247,10 +250,7 @@ in the server, or in between.
If you get this error, one thing you could try would be to fiddle with
the setting of \q{Miscomputes SSH-2 encryption keys} (see
\k{config-ssh-bug-derivekey2}) or \q{Ignores SSH-2 maximum packet
-size} (see \k{config-ssh-bug-maxpkt2}) on the Bugs panel .
-
-Another known server problem which can cause this error is described
-in \k{faq-openssh-bad-openssl} in the FAQ.
+size} (see \k{config-ssh-bug-maxpkt2}) on the Bugs panel.
\H{errors-x11-proxy} \q{PuTTY X11 proxy: \e{various errors}}
diff --git a/doc/faq.but b/doc/faq.but
index 80cf9d63..dbc3fcc9 100644
--- a/doc/faq.but
+++ b/doc/faq.but
@@ -601,34 +601,6 @@ documentation.)
\H{faq-trouble} Troubleshooting
-\S{faq-incorrect-mac}{Question} Why do I see \q{Incorrect MAC
-received on packet}?
-
-One possible cause of this that used to be common is a bug in old
-SSH-2 servers distributed by \cw{ssh.com}. (This is not the only
-possible cause; see \k{errors-crc} in the documentation.)
-Version 2.3.0 and below of their SSH-2 server
-constructs Message Authentication Codes in the wrong way, and
-expects the client to construct them in the same wrong way. PuTTY
-constructs the MACs correctly by default, and hence these old
-servers will fail to work with it.
-
-If you are using PuTTY version 0.52 or better, this should work
-automatically: PuTTY should detect the buggy servers from their
-version number announcement, and automatically start to construct
-its MACs in the same incorrect manner as they do, so it will be able
-to work with them.
-
-If you are using PuTTY version 0.51 or below, you can enable the
-workaround by going to the SSH panel and ticking the box labelled
-\q{Imitate SSH2 MAC bug}. It's possible that you might have to do
-this with 0.52 as well, if a buggy server exists that PuTTY doesn't
-know about.
-
-In this context MAC stands for \ii{Message Authentication Code}. It's a
-cryptographic term, and it has nothing at all to do with Ethernet
-MAC (Media Access Control) addresses.
-
\S{faq-pscp-protocol}{Question} Why do I see \q{Fatal: Protocol
error: Expected control record} in PSCP?
@@ -895,41 +867,6 @@ You should still read the
page} on the PuTTY website (also provided as \k{feedback} in the
manual), and follow the guidelines contained in that.
-\S{faq-openssh-bad-openssl}{Question} Since my SSH server was upgraded
-to \i{OpenSSH} 3.1p1/3.4p1, I can no longer connect with PuTTY.
-
-There is a known problem when OpenSSH has been built against an
-incorrect version of OpenSSL; the quick workaround is to configure
-PuTTY to use SSH protocol 2 and the Blowfish cipher.
-
-For more details and OpenSSH patches, see
-\W{http://bugzilla.mindrot.org/show_bug.cgi?id=138}{bug 138} in the
-OpenSSH BTS.
-
-This is not a PuTTY-specific problem; if you try to connect with
-another client you'll likely have similar problems. (Although PuTTY's
-default cipher differs from many other clients.)
-
-\e{OpenSSH 3.1p1:} configurations known to be broken (and symptoms):
-
-\b SSH-2 with AES cipher (PuTTY says \q{Assertion failed! Expression:
-(len & 15) == 0} in \cw{sshaes.c}, or \q{Out of memory}, or crashes)
-
-\b SSH-2 with 3DES (PuTTY says \q{Incorrect MAC received on packet})
-
-\b SSH-1 with Blowfish (PuTTY says \q{Incorrect CRC received on
-packet})
-
-\b SSH-1 with 3DES
-
-\e{OpenSSH 3.4p1:} as of 3.4p1, only the problem with SSH-1 and
-Blowfish remains. Rebuild your server, apply the patch linked to from
-bug 138 above, or use another cipher (e.g., 3DES) instead.
-
-\e{Other versions:} we occasionally get reports of the same symptom
-and workarounds with older versions of OpenSSH, although it's not
-clear the underlying cause is the same.
-
\S{faq-ssh2key-ssh1conn}{Question} Why do I see \q{Couldn't load
private key from ...}? Why can PuTTYgen load my key but not PuTTY?
From e5dd1435e2f9446d7f131b4b0dd849dcec880aed Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Tue, 23 May 2017 23:16:36 +0100
Subject: [PATCH 031/607] Remove FAQ about Plink on Win95.
While it's still true, the link to Winsock 2 is dead, our standard
release builds don't run on Win95 any more, and it's certainly not
frequently asked.
---
doc/faq.but | 15 ---------------
doc/index.but | 3 ---
2 files changed, 18 deletions(-)
diff --git a/doc/faq.but b/doc/faq.but
index dbc3fcc9..cd3254a8 100644
--- a/doc/faq.but
+++ b/doc/faq.but
@@ -636,21 +636,6 @@ Clicking on \q{ANSI Green} won't turn your session green; it will
only allow you to adjust the \e{shade} of green used when PuTTY is
instructed by the server to display green text.
-\S{faq-winsock2}{Question} Plink on \i{Windows 95} says it can't find
-\i\cw{WS2_32.DLL}.
-
-Plink requires the extended Windows network library, WinSock version
-2. This is installed as standard on Windows 98 and above, and on
-Windows NT, and even on later versions of Windows 95; but early
-Win95 installations don't have it.
-
-In order to use Plink on these systems, you will need to download
-the
-\W{http://www.microsoft.com/windows95/downloads/contents/wuadmintools/s_wunetworkingtools/w95sockets2/}{WinSock 2 upgrade}:
-
-\c http://www.microsoft.com/windows95/downloads/contents/
-\c wuadmintools/s_wunetworkingtools/w95sockets2/
-
\S{faq-outofmem}{Question} After trying to establish an SSH-2
connection, PuTTY says \q{\ii{Out of memory}} and dies.
diff --git a/doc/index.but b/doc/index.but
index 1e71234f..30ebd784 100644
--- a/doc/index.but
+++ b/doc/index.but
@@ -829,9 +829,6 @@ saved sessions from
\IM{login scripts}{startup scripts} login scripts
\IM{login scripts}{startup scripts} startup scripts
-\IM{WS2_32.DLL} \cw{WS2_32.DLL}
-\IM{WS2_32.DLL} WinSock version 2
-
\IM{Red Hat Linux} Red Hat Linux
\IM{Red Hat Linux} Linux, Red Hat
From 2e66a0d2601cd4275f039a7cc82c41e4add19f7f Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 24 May 2017 20:34:38 +0100
Subject: [PATCH 032/607] Fix a build failure under Visual Studio 2010.
Patch due to Brian K. White; we now condition our own declaration of
DLL_DIRECTORY_COOKIE on whether the toolchain's headers had #defined
it already, rather than trying to guess the answer to that from
version-number macros.
(Apparently everything that defines DLL_DIRECTORY_COOKIE does it by
does seem to work in all my tests.)
---
windows/winstuff.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/windows/winstuff.h b/windows/winstuff.h
index dfa032f4..10fcdaba 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -533,8 +533,9 @@ GLOBAL int restricted_acl;
#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
#endif
-#if _MSC_VER < 1400
+#ifndef DLL_DIRECTORY_COOKIE
typedef PVOID DLL_DIRECTORY_COOKIE;
+DECLSPEC_IMPORT DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory (PCWSTR NewDirectory);
#endif
/*
From 8d2755c55f9d8b854ef9051937ececb23c5a4b23 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 25 May 2017 08:17:42 +0100
Subject: [PATCH 033/607] Under clang-cl, include stdint.h regardless of
_MSC_VER.
stdint.h is one of the set of standard C headers that has more to do
with the compiler than the library, and hence, clang provides its own
version of it, even when you're using it in clang-cl mode with Visual
Studio headers, and even when those headers are old enough not to have
a stdint.h of their own. So in clang builds, including stdint.h is
always the right way to get uintptr_t defined.
---
windows/winstuff.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/windows/winstuff.h b/windows/winstuff.h
index 10fcdaba..546c273b 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -19,7 +19,7 @@
* stddef.h. So here we try to make sure _some_ standard header is
* included which defines uintptr_t. */
#include
-#if !defined _MSC_VER || _MSC_VER >= 1600
+#if !defined _MSC_VER || _MSC_VER >= 1600 || defined __clang__
#include
#endif
From f02587f7ac4f47982bf97674f5499dd3b6dede4b Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 25 May 2017 08:21:19 +0100
Subject: [PATCH 034/607] Makefile.clangcl: provide a way to tell lld-link
about crt0.obj.
I've been experimenting with using clang-cl with older versions of the
Visual Studio libraries and headers, and found that the older VS
libraries have a quirk which can cause link failure in a way that
doesn't happen with the newer one. I assume the corresponding old VS
linker must be doing some kind of special-case handling that lld isn't
mimicking.
The quirk arises because lld tries to pull in more than one of the
*crt0.obj startup objects which set things up before calling main or
WinMain (or whatever), and those objects define some of the same
symbols as each other. The fix is to explicitly ask for the right one
of those objects on the link command line, so that it's already been
loaded _before_ the linker starts searching libraries for unresolved
symbols; then the disputed symbols are never unresolved in the first
place during the library search phase.
But this means you have to pick your crt0 object differently depending
on which subsystem you're compiling for. Accordingly, here's an extra
feature in Makefile.clangcl to make that possible by means of the
right definitions on the make command line.
---
mkfiles.pl | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/mkfiles.pl b/mkfiles.pl
index 8a63c65d..1d83912b 100755
--- a/mkfiles.pl
+++ b/mkfiles.pl
@@ -492,6 +492,31 @@ sub manpages {
# paths in $LIB) it's reasonable to have the choice of
# compilation target driven by another environment variable
# set in parallel with that one.
+ # - for older versions of the VS libraries you may also have to
+ # set EXTRA_console and/or EXTRA_windows to the name of an
+ # object file manually extracted from one of those libraries.
+ # * This is because old VS seems to manage its startup code by
+ # having libcmt.lib contain lots of *crt0.obj objects, one
+ # for each possible user entry point (main, WinMain and the
+ # wide-char versions of both), of which the linker arranges
+ # to include the right one by special-case code. But lld
+ # only seems to mimic half of that code - it does include
+ # the right crt0 object, but it doesn't also deliberately
+ # _avoid_ including the _wrong_ ones, and since all those
+ # objects define a common set of global symbols for other
+ # parts of the library to use, lld may well select an
+ # arbitrary one of them the first time it sees a reference
+ # to one of those global symbols, and then later also select
+ # the _right_ one for the application's entry point, causing
+ # a multiple-definitions crash.
+ # * So the workaround is to explicitly include the right
+ # *crt0.obj file on the linker command line before lld even
+ # begins searching libraries. Hence, for a console
+ # application, you might extract crt0.obj from the library
+ # in question and set EXTRA_console=crt0.obj, and for a GUI
+ # application, do the same with wincrt0.obj. Then this
+ # makefile will include the right one of those objects
+ # alongside the matching /subsystem linker option.
open OUT, ">$makefiles{'clangcl'}"; select OUT;
print
@@ -539,7 +564,8 @@ sub manpages {
print &splitline("\t\$(LD) \$(LFLAGS) \$(XLFLAGS) ".
"/out:\$(BUILDDIR)$prog.exe ".
"/lldmap:\$(BUILDDIR)$prog.map ".
- "/subsystem:$subsys\$(SUBSYSVER) $objstr")."\n\n";
+ "/subsystem:$subsys\$(SUBSYSVER) ".
+ "\$(EXTRA_$subsys) $objstr")."\n\n";
}
foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res.o", $dirpfx, "/", "vc")) {
$extradeps = $forceobj{$d->{obj_orig}} ? ["*.c","*.h","*.rc"] : [];
From eda5364eb476d4771e1f5a35ff81f50b4e309135 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 25 May 2017 08:22:22 +0100
Subject: [PATCH 035/607] Use / in pathnames in the Wix installer source.
I've also been experimenting recently with running Wix on Linux under
Mono, rather than running it on Windows in the obvious way. Wix under
Mono insists on forward slashes in pathnames, and it turns out that
Wix on Windows doesn't object to them either, so I can safely change
them over unconditionally and then my installer source will work in
both modes.
---
windows/installer.wxs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/windows/installer.wxs b/windows/installer.wxs
index 4a720e40..e3ed8c71 100644
--- a/windows/installer.wxs
+++ b/windows/installer.wxs
@@ -191,7 +191,7 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/dd391569(v=vs.85).aspx
+ Source="../doc/putty.chm" KeyPath="yes">
@@ -209,7 +209,7 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/dd391569(v=vs.85).aspx
+ Source="../LICENCE" KeyPath="yes" />
From bbdb527d4dc0eeabd083242d10faa1a2aad28e5d Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 27 May 2017 20:05:07 +0100
Subject: [PATCH 036/607] Turn off the Inno Setup installer build.
We've been planning to do that for a while, and this installer-builder
isn't going to work anyway in the build environment I'm about to move
everything to, so this seems like the moment.
---
Buildscr | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/Buildscr b/Buildscr
index de7f41b5..8bd680df 100644
--- a/Buildscr
+++ b/Buildscr
@@ -177,11 +177,8 @@ delegate windows
in putty/windows with wix do/win candle -arch x86 -dWin64=no -dBuilddir=build32\ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer32.msi
in putty/windows with wix do/win candle -arch x64 -dWin64=yes -dBuilddir=build64\ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer64.msi
- # Build the old Inno Setup installer, for 32-bit only.
- in putty/windows with innosetup do/win iscc putty.iss
-
# Sign the installers.
- ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi Output/installer.exe
+ ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi
# Finished Windows builds.
return putty/windows/buildold/*.exe
@@ -192,7 +189,6 @@ delegate windows
return putty/windows/build64/*.map
return putty/windows/installer32.msi
return putty/windows/installer64.msi
- return putty/windows/Output/installer.exe
enddelegate
in putty/doc do make mostlyclean
in putty/doc do make $(Docmakever)
@@ -210,7 +206,6 @@ deliver putty/windows/build64/*.exe putty/w64/$@
deliver putty/windows/build64/putty.zip putty/w64/$@
deliver putty/windows/installer32.msi putty/w32/$(Ifilename32).msi
deliver putty/windows/installer64.msi putty/w64/$(Ifilename64).msi
-deliver putty/windows/Output/installer.exe putty/w32/$(Ifilename32).exe
deliver putty/doc/puttydoc.zip putty/$@
deliver putty/doc/putty.chm putty/$@
deliver putty/doc/putty.hlp putty/$@
From 599ca6d726fa9c251fda6b45fd5161878a208c53 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 27 May 2017 20:06:11 +0100
Subject: [PATCH 037/607] Build using clang-cl instead of Visual Studio.
This permits me to do the binary builds via cross-compilation on
Linux, which is nice from a perspective of not having my build
environment subject to the same potential pool of Windows malware that
might be interested in infecting the resulting binaries. Also, it's
quicker to build and the code generation is noticeably better.
---
Buildscr | 51 ++++++++++++++++++++++++++++-----------------------
1 file changed, 28 insertions(+), 23 deletions(-)
diff --git a/Buildscr b/Buildscr
index 8bd680df..4cbf6c41 100644
--- a/Buildscr
+++ b/Buildscr
@@ -156,37 +156,42 @@ in putty/icons do make
in putty do convert -size 164x312 'gradient:blue-white' -distort SRT -90 -swirl 180 \( -size 329x312 canvas:white \) +append \( icons/putty-48.png -geometry +28+24 \) -composite \( icons/pscp-48.png -geometry +88+96 \) -composite \( icons/puttygen-48.png -geometry +28+168 \) -composite \( icons/pageant-48.png -geometry +88+240 \) -composite windows/msidialog.bmp
in putty do convert -size 493x58 canvas:white \( icons/putty-48.png -geometry +440+5 \) -composite windows/msibanner.bmp
-delegate windows
- # Build the original binaries.
- in putty/windows with visualstudio do/win mkdir buildold && nmake -f Makefile.vc BUILDDIR=buildold\ $(Makeargs) all cleantestprogs
-
- # Build the VS2015 binaries. For the 32-bit ones, we set a subsystem
- # version of 5.01, which allows the resulting files to still run on
- # Windows XP.
- in putty/windows with visualstudio2015_32bit do/win mkdir build32 && nmake -f Makefile.vc BUILDDIR=build32\ SUBSYSVER=,5.01 $(Makeargs) all cleantestprogs
- in putty/windows with visualstudio2015_64bit do/win mkdir build64 && nmake -f Makefile.vc BUILDDIR=build64\ $(Makeargs) all cleantestprogs
-
- # Code-sign the binaries, if the local bob config provides a script
- # to do so. We assume here that the script accepts an -i option to
- # provide a 'more info' URL, and an optional -n option to provide a
- # program name, and that it can take multiple .exe filename
- # arguments and sign them all in place.
- ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ build*/*.exe
+# Build the standard binaries, in both 32- and 64-bit flavours.
+#
+# For the 32-bit ones, we set a subsystem version of 5.01, which
+# allows the resulting files to still run on Windows XP.
+in putty/windows with clangcl32 do mkdir build32 && Platform=x86 make -f Makefile.clangcl BUILDDIR=build32/ SUBSYSVER=,5.01 $(Makeargs) all cleantestprogs
+in putty/windows with clangcl64 do mkdir build64 && Platform=x64 make -f Makefile.clangcl BUILDDIR=build64/ $(Makeargs) all cleantestprogs
+
+# Build the 'old' binaries, which should still run on all 32-bit
+# versions of Windows back to Win95 (but not Win32s). These link
+# against Visual Studio 2003 libraries (the more modern versions
+# assume excessively modern Win32 API calls to be available), specify
+# a subsystem version of 4.0, and compile with /arch:IA32 to prevent
+# the use of modern CPU features like MMX which older machines also
+# might not have.
+in putty/windows with clangcl32_2003 do mkdir buildold && Platform=x86 make -f Makefile.clangcl BUILDDIR=buildold/ $(Makeargs) CCTARGET=i386-pc-windows-msvc13.0.0 SUBSYSVER=,4.0 EXTRA_windows=wincrt0.obj EXTRA_console=crt0.obj XFLAGS=/arch:IA32 all cleantestprogs
+
+# Code-sign the Windows binaries, if the local bob config provides a
+# script to do so in a cross-compiling way. We assume here that the
+# script accepts an -i option to provide a 'more info' URL, an
+# optional -n option to provide a program name, and an -N option to
+# take the program name from an .exe's version resource, and that it
+# can accept multiple .exe or .msi filename arguments and sign them
+# all in place.
+ifneq "$(cross_winsigncode)" "" in putty/windows do $(cross_winsigncode) -N -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ build*/*.exe
+delegate windows
# Build a WiX MSI installer, for each of build32 and build64.
in putty/windows with wix do/win candle -arch x86 -dWin64=no -dBuilddir=build32\ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer32.msi
in putty/windows with wix do/win candle -arch x64 -dWin64=yes -dBuilddir=build64\ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer64.msi
- # Sign the installers.
+ # Sign the installers, if the local bob config provides
+ # $(winsigncode) that can run in a Windows delegation and behaves
+ # the same as $(cross_winsigncode) used above.
ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi
# Finished Windows builds.
- return putty/windows/buildold/*.exe
- return putty/windows/buildold/*.map
- return putty/windows/build32/*.exe
- return putty/windows/build32/*.map
- return putty/windows/build64/*.exe
- return putty/windows/build64/*.map
return putty/windows/installer32.msi
return putty/windows/installer64.msi
enddelegate
From fd6898b5865b3f7fcccfe57c6ee272984f52a34f Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 27 May 2017 20:07:00 +0100
Subject: [PATCH 038/607] Build the MSI using Wix run on Linux via Mono.
I have a grubby method of getting this to work without Wine, which I
intend to get round to publishing just as soon as I finish deciding
what its best shape is. But I don't want to wait for that before
starting to actually use it, because this eliminates the last trace of
Windows in the PuTTY Windows builds.
---
Buildscr | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/Buildscr b/Buildscr
index 4cbf6c41..ab94ea92 100644
--- a/Buildscr
+++ b/Buildscr
@@ -181,20 +181,13 @@ in putty/windows with clangcl32_2003 do mkdir buildold && Platform=x86 make -f M
# all in place.
ifneq "$(cross_winsigncode)" "" in putty/windows do $(cross_winsigncode) -N -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ build*/*.exe
-delegate windows
- # Build a WiX MSI installer, for each of build32 and build64.
- in putty/windows with wix do/win candle -arch x86 -dWin64=no -dBuilddir=build32\ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer32.msi
- in putty/windows with wix do/win candle -arch x64 -dWin64=yes -dBuilddir=build64\ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer64.msi
-
- # Sign the installers, if the local bob config provides
- # $(winsigncode) that can run in a Windows delegation and behaves
- # the same as $(cross_winsigncode) used above.
- ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi
-
- # Finished Windows builds.
- return putty/windows/installer32.msi
- return putty/windows/installer64.msi
-enddelegate
+# Build a WiX MSI installer, for each of build32 and build64.
+in putty/windows with wixonlinux do candle -arch x86 -dWin64=no -dBuilddir=build32/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer32.msi -spdb
+in putty/windows with wixonlinux do candle -arch x64 -dWin64=yes -dBuilddir=build64/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer64.msi -spdb
+
+# Sign the Windows installers.
+ifneq "$(cross_winsigncode)" "" in putty/windows do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi
+
in putty/doc do make mostlyclean
in putty/doc do make $(Docmakever)
in putty/windows/buildold do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../../doc/putty.chm ../../doc/putty.hlp ../../doc/putty.cnt
From 1da3c71474aab3296a99ae0ed40e4fa1aa425185 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 30 May 2017 22:49:25 +0100
Subject: [PATCH 039/607] Have clang-cl builds announce their _MSC_VER.
In particular, this means the w32 and w32old builds have
distinguishable buildinfo text, which should protect us against at
least one source of confusion when receiving bug reports.
---
misc.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/misc.c b/misc.c
index 9aff234b..734ea8b1 100644
--- a/misc.c
+++ b/misc.c
@@ -1165,11 +1165,21 @@ char *buildinfo(const char *newline)
BUILDINFO_PLATFORM);
#ifdef __clang_version__
+#define FOUND_COMPILER
strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__);
#elif defined __GNUC__ && defined __VERSION__
+#define FOUND_COMPILER
strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__);
-#elif defined _MSC_VER
- strbuf_catf(buf, "%sCompiler: Visual Studio", newline);
+#endif
+
+#if defined _MSC_VER
+#ifndef FOUND_COMPILER
+#define FOUND_COMPILER
+ strbuf_catf(buf, "%sCompiler: ", newline);
+#else
+ strbuf_catf(buf, ", emulating ");
+#endif
+ strbuf_catf(buf, "Visual Studio", newline);
#if _MSC_VER == 1900
strbuf_catf(buf, " 2015 / MSVC++ 14.0");
#elif _MSC_VER == 1800
@@ -1178,12 +1188,14 @@ char *buildinfo(const char *newline)
strbuf_catf(buf, " 2012 / MSVC++ 11.0");
#elif _MSC_VER == 1600
strbuf_catf(buf, " 2010 / MSVC++ 10.0");
-#elif _MSC_VER == 1500
+#elif _MSC_VER == 1500
strbuf_catf(buf, " 2008 / MSVC++ 9.0");
-#elif _MSC_VER == 1400
+#elif _MSC_VER == 1400
strbuf_catf(buf, " 2005 / MSVC++ 8.0");
-#elif _MSC_VER == 1310
+#elif _MSC_VER == 1310
strbuf_catf(buf, " 2003 / MSVC++ 7.1");
+#elif _MSC_VER == 1300
+ strbuf_catf(buf, " 2003 / MSVC++ 7.0");
#else
strbuf_catf(buf, ", unrecognised version");
#endif
From 05f499e55f19b82c773559ee4465ba11c898c9eb Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Tue, 6 Jun 2017 09:34:21 +0100
Subject: [PATCH 040/607] Add 'passthrough printing' as an index term.
---
doc/config.but | 4 ++--
doc/index.but | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/doc/config.but b/doc/config.but
index bb18b794..bd12efb2 100644
--- a/doc/config.but
+++ b/doc/config.but
@@ -499,8 +499,8 @@ instead of relying on the automatic detection.
\cfg{winhelp-topic}{terminal.printing}
A lot of VT100-compatible terminals support printing under control
-of the remote server. PuTTY supports this feature as well, but it is
-turned off by default.
+of the remote server (sometimes called \q{passthrough printing}).
+PuTTY supports this feature as well, but it is turned off by default.
To enable remote-controlled printing, choose a printer from the
\q{Printer to send ANSI printer output to} drop-down list box. This
diff --git a/doc/index.but b/doc/index.but
index 30ebd784..0877ee90 100644
--- a/doc/index.but
+++ b/doc/index.but
@@ -340,6 +340,7 @@ saved sessions from
\IM{remote-controlled printing} ANSI printing
\IM{remote-controlled printing} remote-controlled printing
\IM{remote-controlled printing} printing, remote-controlled
+\IM{remote-controlled printing} passthrough printing
\IM{Home and End keys} Home key
\IM{Home and End keys} End key
From 6aac4b9cefbaa651639ef0495ebf9d098e67481c Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Wed, 7 Jun 2017 22:02:28 +0100
Subject: [PATCH 041/607] StartDocPrinter returns DWORD, not BOOL.
(Introduced in 793ac8727.)
---
windows/winprint.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/windows/winprint.c b/windows/winprint.c
index 11587273..a17166e6 100644
--- a/windows/winprint.c
+++ b/windows/winprint.c
@@ -23,7 +23,7 @@ DECL_WINDOWS_FUNCTION(static, BOOL, EnumPrinters,
DECL_WINDOWS_FUNCTION(static, BOOL, OpenPrinter,
(LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS));
DECL_WINDOWS_FUNCTION(static, BOOL, ClosePrinter, (HANDLE));
-DECL_WINDOWS_FUNCTION(static, BOOL, StartDocPrinter, (HANDLE, DWORD, LPBYTE));
+DECL_WINDOWS_FUNCTION(static, DWORD, StartDocPrinter, (HANDLE, DWORD, LPBYTE));
DECL_WINDOWS_FUNCTION(static, BOOL, EndDocPrinter, (HANDLE));
DECL_WINDOWS_FUNCTION(static, BOOL, StartPagePrinter, (HANDLE));
DECL_WINDOWS_FUNCTION(static, BOOL, EndPagePrinter, (HANDLE));
From f31a72ba09d20e076f62e39e0f59404511bf6c86 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 10 Jun 2017 11:29:21 +0100
Subject: [PATCH 042/607] Unix: use conf_dest() in 'unable to open connection'
error box.
Alamy Liu points out that asking for CONF_host will display the wrong
part of the configuration in the case where serial port setup fails.
The Windows front end's analogous message already got this right, but
I must have forgotten to change this one too when I introduced
conf_dest.
---
unix/gtkwin.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index f54289a3..9e73181e 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -4274,7 +4274,7 @@ static void start_backend(struct gui_data *inst)
if (error) {
char *msg = dupprintf("Unable to open connection to %s:\n%s",
- conf_get_str(inst->conf, CONF_host), error);
+ conf_dest(inst->conf), error);
inst->exited = TRUE;
fatal_message_box(inst->window, msg);
sfree(msg);
From a9e1053c8ae29c80cf0827c346e7dac976fb949b Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 15 Jun 2017 18:58:01 +0100
Subject: [PATCH 043/607] Log the server's diagnostics if main channel open
fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
---
ssh.c | 61 ++++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 42 insertions(+), 19 deletions(-)
diff --git a/ssh.c b/ssh.c
index b6298b03..1d80e919 100644
--- a/ssh.c
+++ b/ssh.c
@@ -8496,18 +8496,37 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
ssh_channel_try_eof(c); /* in case we had a pending EOF */
}
-static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
+static char *ssh2_channel_open_failure_error_text(struct Packet *pktin)
{
static const char *const reasons[] = {
- "",
- "Administratively prohibited",
- "Connect failed",
- "Unknown channel type",
- "Resource shortage",
+ NULL,
+ "Administratively prohibited",
+ "Connect failed",
+ "Unknown channel type",
+ "Resource shortage",
};
unsigned reason_code;
+ const char *reason_code_string;
+ char reason_code_buf[256];
char *reason_string;
int reason_length;
+
+ reason_code = ssh_pkt_getuint32(pktin);
+ if (reason_code < lenof(reasons) && reasons[reason_code]) {
+ reason_code_string = reasons[reason_code];
+ } else {
+ reason_code_string = reason_code_buf;
+ sprintf(reason_code_buf, "unknown reason code %#x", reason_code);
+ }
+
+ ssh_pkt_getstring(pktin, &reason_string, &reason_length);
+
+ return dupprintf("%s [%.*s]", reason_code_string,
+ reason_length, NULLTOEMPTY(reason_string));
+}
+
+static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
+{
struct ssh_channel *c;
c = ssh_channel_msg(ssh, pktin);
@@ -8516,14 +8535,9 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
assert(c->halfopen); /* ssh_channel_msg will have enforced this */
if (c->type == CHAN_SOCKDATA) {
- reason_code = ssh_pkt_getuint32(pktin);
- if (reason_code >= lenof(reasons))
- reason_code = 0; /* ensure reasons[reason_code] in range */
- ssh_pkt_getstring(pktin, &reason_string, &reason_length);
- logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
- reasons[reason_code], reason_length,
- NULLTOEMPTY(reason_string));
-
+ char *errtext = ssh2_channel_open_failure_error_text(pktin);
+ logeventf(ssh, "Forwarded connection refused by server: %s", errtext);
+ sfree(errtext);
pfd_close(c->u.pfd.pf);
} else if (c->type == CHAN_ZOMBIE) {
/*
@@ -10727,15 +10741,24 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
ssh->ncmode = FALSE;
}
crWaitUntilV(pktin);
- if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
- bombout(("Server refused to open channel"));
+ if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION &&
+ pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE) {
+ bombout(("Server sent strange packet %d in response to main "
+ "channel open request", pktin->type));
crStopV;
- /* FIXME: error data comes back in FAILURE packet */
- }
+ }
if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {
- bombout(("Server's channel confirmation cited wrong channel"));
+ bombout(("Server's response to main channel open cited wrong"
+ " channel number"));
+ crStopV;
+ }
+ if (pktin->type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
+ char *errtext = ssh2_channel_open_failure_error_text(pktin);
+ bombout(("Server refused to open main channel: %s", errtext));
+ sfree(errtext);
crStopV;
}
+
ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
ssh->mainchan->halfopen = FALSE;
ssh->mainchan->type = CHAN_MAINSESSION;
From 892d4a0188ffd8aa60dc11b4bace30dfb0f9d50e Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Thu, 15 Jun 2017 23:33:57 +0100
Subject: [PATCH 044/607] Seek all Windows print functions in winspool.drv.
Rather than loading some from spoolss.dll, which some MSDN documentation
suggests, but which doesn't work very well in practice.
(Also, remove an unused variable.)
---
windows/winprint.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/windows/winprint.c b/windows/winprint.c
index a17166e6..6a1a74c2 100644
--- a/windows/winprint.c
+++ b/windows/winprint.c
@@ -33,20 +33,25 @@ DECL_WINDOWS_FUNCTION(static, BOOL, WritePrinter,
static void init_winfuncs(void)
{
static int initialised = FALSE;
- char buf[4096];
if (initialised)
return;
{
HMODULE winspool_module = load_system32_dll("winspool.drv");
- HMODULE spoolss_module = load_system32_dll("spoolss.dll");
+ /* Some MSDN documentation claims that some of the below functions
+ * should be loaded from spoolss.dll, but this doesn't seem to
+ * be reliable in practice.
+ * Nevertheless, we load spoolss.dll ourselves using our safe
+ * loading method, against the possibility that winspool.drv
+ * later loads it unsafely. */
+ (void) load_system32_dll("spoolss.dll");
GET_WINDOWS_FUNCTION_PP(winspool_module, EnumPrinters);
GET_WINDOWS_FUNCTION_PP(winspool_module, OpenPrinter);
- GET_WINDOWS_FUNCTION_PP(spoolss_module, ClosePrinter);
+ GET_WINDOWS_FUNCTION_PP(winspool_module, ClosePrinter);
GET_WINDOWS_FUNCTION_PP(winspool_module, StartDocPrinter);
- GET_WINDOWS_FUNCTION_PP(spoolss_module, EndDocPrinter);
- GET_WINDOWS_FUNCTION_PP(spoolss_module, StartPagePrinter);
- GET_WINDOWS_FUNCTION_PP(spoolss_module, EndPagePrinter);
- GET_WINDOWS_FUNCTION_PP(spoolss_module, WritePrinter);
+ GET_WINDOWS_FUNCTION_PP(winspool_module, EndDocPrinter);
+ GET_WINDOWS_FUNCTION_PP(winspool_module, StartPagePrinter);
+ GET_WINDOWS_FUNCTION_PP(winspool_module, EndPagePrinter);
+ GET_WINDOWS_FUNCTION_PP(winspool_module, WritePrinter);
}
initialised = TRUE;
}
From 4387b5f1617bf0222fc64e3ec73d14e252465309 Mon Sep 17 00:00:00 2001
From: Ilya Shipitsin
Date: Mon, 19 Jun 2017 20:57:28 +0500
Subject: [PATCH 045/607] resolve an issue found by cppcheck:
[unix/osxlaunch.c:133] -> [unix/osxlaunch.c:134]: (warning) Either the condition '!qhead' is redundant or there is possible null pointer dereference: qhead.
---
unix/osxlaunch.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unix/osxlaunch.c b/unix/osxlaunch.c
index 2627c642..28516185 100644
--- a/unix/osxlaunch.c
+++ b/unix/osxlaunch.c
@@ -130,11 +130,11 @@ char *get_unused_env_prefix(void)
char **e;
qhead = (struct bucket *)malloc(sizeof(struct bucket));
- qhead->prefixlen = 0;
if (!qhead) {
fprintf(stderr, "out of memory\n");
exit(1);
}
+ qhead->prefixlen = 0;
for (e = environ; *e; e++)
qhead->first_node = new_node(qhead->first_node, *e, strcspn(*e, "="));
From 5efee183569ede0a4155f8195e1b1be58e954b95 Mon Sep 17 00:00:00 2001
From: Ilya Shipitsin
Date: Mon, 19 Jun 2017 21:39:32 +0500
Subject: [PATCH 046/607] resolve couple of issues found by cppcheck:
[psftp.c:1135] -> [psftp.c:1134]: (warning) Either the condition '!dir' is redundant or there is possible null pointer dereference: dir.
[psftp.c:1420] -> [psftp.c:1419]: (warning) Either the condition '!dir' is redundant or there is possible null pointer dereference: dir.
---
psftp.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/psftp.c b/psftp.c
index 5394c1fb..aa397181 100644
--- a/psftp.c
+++ b/psftp.c
@@ -1128,12 +1128,12 @@ int sftp_cmd_cd(struct sftp_command *cmd)
if (cmd->nwords < 2)
dir = dupstr(homedir);
- else
+ else {
dir = canonify(cmd->words[1]);
-
- if (!dir) {
- printf("%s: canonify: %s\n", dir, fxp_error());
- return 0;
+ if (!dir) {
+ printf("%s: canonify: %s\n", cmd->words[1], fxp_error());
+ return 0;
+ }
}
req = fxp_opendir_send(dir);
@@ -1417,7 +1417,7 @@ int sftp_cmd_mkdir(struct sftp_command *cmd)
for (i = 1; i < cmd->nwords; i++) {
dir = canonify(cmd->words[i]);
if (!dir) {
- printf("%s: canonify: %s\n", dir, fxp_error());
+ printf("%s: canonify: %s\n", cmd->words[i], fxp_error());
return 0;
}
From 3f2df8cc9dc6ae19c6513091eb009999f4406a60 Mon Sep 17 00:00:00 2001
From: Ilya Shipitsin
Date: Mon, 19 Jun 2017 21:42:16 +0500
Subject: [PATCH 047/607] resolve (no real impact) issue found by cppcheck:
[unix/osxlaunch.c:411]: (error) Memory leak: macos
[unix/osxlaunch.c:411]: (error) Memory leak: contents
[unix/osxlaunch.c:411]: (error) Memory leak: new_argv
---
unix/osxlaunch.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/unix/osxlaunch.c b/unix/osxlaunch.c
index 28516185..8082f2de 100644
--- a/unix/osxlaunch.c
+++ b/unix/osxlaunch.c
@@ -408,6 +408,9 @@ int main(int argc, char **argv)
execv(realbin, new_argv);
perror("execv");
+ free(new_argv);
+ free(contents);
+ free(macos);
return 127;
}
From 02043ec5ace4985fc35fef8b5d6543a871ccab3c Mon Sep 17 00:00:00 2001
From: Ilya Shipitsin
Date: Tue, 20 Jun 2017 09:36:07 +0500
Subject: [PATCH 048/607] resolve (no real impact) issue found by cppcheck:
[windows/winnet.c:589]: (error) Uninitialized variable: err
---
windows/winnet.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/windows/winnet.c b/windows/winnet.c
index ea950bba..e2edc87a 100644
--- a/windows/winnet.c
+++ b/windows/winnet.c
@@ -548,7 +548,7 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) {
struct hostent *h = NULL;
- int err;
+ int err = 0;
#ifndef NO_IPV6
/*
* Use getaddrinfo when it's available
From 20e36ae4a24e06f86ce533a24c5f61f00e2ab91c Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 20 Jun 2017 07:05:39 +0100
Subject: [PATCH 049/607] Fix a collection of type / format string mismatches.
Ilya Shipitsin sent me a list of errors reported by a tool 'cppcheck',
which I hadn't seen before, together with some fixes for things
already taken off that list. This change picks out all the things from
the remaining list that I could quickly identify as actual errors,
which it turns out are all format-string goofs along the lines of
using a %d with an unsigned int, or a %u with a signed int, or (in the
cases in charset/utf8.c) an actual _size_ mismatch which could in
principle have caused trouble on a big-endian target.
---
charset/utf8.c | 4 ++--
import.c | 3 ++-
minibidi.c | 2 +-
misc.c | 3 ++-
pscp.c | 2 +-
terminal.c | 2 +-
windows/window.c | 4 ++--
7 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/charset/utf8.c b/charset/utf8.c
index 3c777292..fe46cf98 100644
--- a/charset/utf8.c
+++ b/charset/utf8.c
@@ -267,7 +267,7 @@ void utf8_read_test(int line, char *input, int inlen, ...)
}
if (l != str[i]) {
printf("%d: char %d came out as %08x, should be %08x\n",
- line, i, str[i], l);
+ line, i, str[i], (unsigned)l);
total_errs++;
}
}
@@ -306,7 +306,7 @@ void utf8_write_test(int line, const long *input, int inlen, ...)
}
if (l != str[i]) {
printf("%d: char %d came out as %08x, should be %08x\n",
- line, i, str[i], l);
+ line, i, str[i], (unsigned)l);
total_errs++;
}
}
diff --git a/import.c b/import.c
index adf68777..88589a71 100644
--- a/import.c
+++ b/import.c
@@ -445,7 +445,7 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
if (!strcmp(p, "ENCRYPTED"))
ret->encrypted = TRUE;
} else if (!strcmp(line, "DEK-Info")) {
- int i, j, ivlen;
+ int i, ivlen;
if (!strncmp(p, "DES-EDE3-CBC,", 13)) {
ret->encryption = OP_E_3DES;
@@ -459,6 +459,7 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
}
p = strchr(p, ',') + 1;/* always non-NULL, by above checks */
for (i = 0; i < ivlen; i++) {
+ unsigned j;
if (1 != sscanf(p, "%2x", &j)) {
errmsg = "expected more iv data in DEK-Info";
goto error;
diff --git a/minibidi.c b/minibidi.c
index 8d78594d..2bdf4deb 100644
--- a/minibidi.c
+++ b/minibidi.c
@@ -2014,7 +2014,7 @@ int main(int argc, char **argv)
unsigned long chr = strtoul(argv[i], NULL, 0);
int type = getType(chr);
assert(typetoname[type].type == type);
- printf("U+%04x: %s\n", chr, typetoname[type].name);
+ printf("U+%04x: %s\n", (unsigned)chr, typetoname[type].name);
}
return 0;
diff --git a/misc.c b/misc.c
index 734ea8b1..469e4aeb 100644
--- a/misc.c
+++ b/misc.c
@@ -157,7 +157,8 @@ int main(void)
passes++; \
} else { \
printf("fail: %s(%s,%s)%s = %u, expected %u\n", \
- #func, #string, #arg2, #suffix, ret, result); \
+ #func, #string, #arg2, #suffix, ret, \
+ (unsigned)result); \
fails++; \
} \
} while (0)
diff --git a/pscp.c b/pscp.c
index 454ec084..210362df 100644
--- a/pscp.c
+++ b/pscp.c
@@ -1476,7 +1476,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
act->action = SCP_SINK_ENDDIR;
return 0;
case 'T':
- if (sscanf(act->buf, "%ld %*d %ld %*d",
+ if (sscanf(act->buf, "%lu %*d %lu %*d",
&act->mtime, &act->atime) == 2) {
act->settime = 1;
back->send(backhandle, "", 1);
diff --git a/terminal.c b/terminal.c
index f47fe1bd..ba9dd617 100644
--- a/terminal.c
+++ b/terminal.c
@@ -4327,7 +4327,7 @@ static void term_out(Terminal *term)
for (i = 1; i < term->esc_nargs; i++) {
if (i != 1)
strcat(term->id_string, ";");
- sprintf(lbuf, "%d", term->esc_args[i]);
+ sprintf(lbuf, "%u", term->esc_args[i]);
strcat(term->id_string, lbuf);
}
strcat(term->id_string, "c");
diff --git a/windows/window.c b/windows/window.c
index 89ddb863..96600311 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -5199,8 +5199,8 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des
multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
NULL, 0, NULL, NULL);
if (multilen != 1) {
- blen = sprintf(before, "{\\uc%d\\u%d", multilen,
- udata[uindex]);
+ blen = sprintf(before, "{\\uc%d\\u%d", (int)multilen,
+ (int)udata[uindex]);
alen = 1; strcpy(after, "}");
} else {
blen = sprintf(before, "\\u%d", udata[uindex]);
From 98cbe6963b8126659cf16c622302cbd872026936 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 20 Jun 2017 19:02:13 +0100
Subject: [PATCH 050/607] Another format-string fix.
Oops - I found this in an editor autosave file after pushing the
previous commit. I meant it to be part of the previous commit. Oh
well, better late than never.
---
tree234.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tree234.c b/tree234.c
index f1c0c2ed..d3c5293d 100644
--- a/tree234.c
+++ b/tree234.c
@@ -681,7 +681,7 @@ static void *delpos234_internal(tree234 * t, int index)
LOG((" moving to subtree %d\n", ki));
sub = n->kids[ki];
if (!sub->elems[1]) {
- LOG((" subtree has only one element!\n", ki));
+ LOG((" subtree has only one element!\n"));
if (ki > 0 && n->kids[ki - 1]->elems[1]) {
/*
* Case 3a, left-handed variant. Child ki has
From 4696f4a40bd38386dac5bc0f8331ca9c4e579bd6 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 20 Jun 2017 19:02:48 +0100
Subject: [PATCH 051/607] Coverity build fixes.
Like every other toolchain I've tried, my Coverity scanning build has
its share of random objections to parts of my Windows API type-
checking system. I do wonder if that bright idea was worth the hassle
- but it would probably cost all the annoyance all over again to back
out now...
---
windows/wincapi.c | 9 ++++++++-
windows/winhsock.c | 5 +++--
windows/winmisc.c | 8 +++++---
windows/winnet.c | 10 ++++++++++
4 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/windows/wincapi.c b/windows/wincapi.c
index 2550b6de..2bd03470 100644
--- a/windows/wincapi.c
+++ b/windows/wincapi.c
@@ -19,7 +19,14 @@ int got_crypt(void)
attempted = TRUE;
crypt = load_system32_dll("crypt32.dll");
successful = crypt &&
- GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory);
+#ifdef COVERITY
+ /* The build toolchain I use with Coverity doesn't know
+ * about this function, so can't type-check it */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(crypt, CryptProtectMemory)
+#else
+ GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory)
+#endif
+ ;
}
return successful;
}
diff --git a/windows/winhsock.c b/windows/winhsock.c
index 799fd28d..7c8b0ba6 100644
--- a/windows/winhsock.c
+++ b/windows/winhsock.c
@@ -284,10 +284,11 @@ static char *sk_handle_peer_info(Socket s)
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
-#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__
+#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__ || defined COVERITY
/* For older Visual Studio, and MinGW too (at least as of
* Ubuntu 16.04), this function isn't available in the header
- * files to type-check */
+ * files to type-check. Ditto the toolchain I use for
+ * Coveritying the Windows code. */
GET_WINDOWS_FUNCTION_NO_TYPECHECK(
kernel32_module, GetNamedPipeClientProcessId);
#else
diff --git a/windows/winmisc.c b/windows/winmisc.c
index 85fa3c95..59d64312 100644
--- a/windows/winmisc.c
+++ b/windows/winmisc.c
@@ -177,9 +177,11 @@ void dll_hijacking_protection(void)
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
-#if defined _MSC_VER && _MSC_VER < 1900
- /* For older Visual Studio, this function isn't available in
- * the header files to type-check */
+#if (defined _MSC_VER && _MSC_VER < 1900) || defined COVERITY
+ /* For older Visual Studio, and also for the system I
+ * currently use for Coveritying the Windows code, this
+ * function isn't available in the header files to
+ * type-check */
GET_WINDOWS_FUNCTION_NO_TYPECHECK(
kernel32_module, SetDefaultDllDirectories);
#else
diff --git a/windows/winnet.c b/windows/winnet.c
index e2edc87a..86f7735f 100644
--- a/windows/winnet.c
+++ b/windows/winnet.c
@@ -305,11 +305,21 @@ void sk_init(void)
GET_WINDOWS_FUNCTION(winsock_module, WSAStartup);
GET_WINDOWS_FUNCTION(winsock_module, WSACleanup);
GET_WINDOWS_FUNCTION(winsock_module, closesocket);
+#ifndef COVERITY
GET_WINDOWS_FUNCTION(winsock_module, ntohl);
GET_WINDOWS_FUNCTION(winsock_module, htonl);
GET_WINDOWS_FUNCTION(winsock_module, htons);
GET_WINDOWS_FUNCTION(winsock_module, ntohs);
GET_WINDOWS_FUNCTION(winsock_module, gethostname);
+#else
+ /* The toolchain I use for Windows Coverity builds doesn't know
+ * the type signatures of these */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, ntohl);
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, htonl);
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, htons);
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, ntohs);
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gethostname);
+#endif
GET_WINDOWS_FUNCTION(winsock_module, gethostbyname);
GET_WINDOWS_FUNCTION(winsock_module, getservbyname);
GET_WINDOWS_FUNCTION(winsock_module, inet_addr);
From d61897414cae6e40dc39da6aac84ab48c6cdf149 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 20 Jun 2017 21:17:43 +0100
Subject: [PATCH 052/607] Fixes spotted by Coverity.
A lenof() being applied to a non-array, a couple of missing frees on
an error path, and a case in pfd_receive() where we could have gone
round a loop again after freeing the port forwarding structure it was
working on.
---
config.c | 2 +-
portfwd.c | 1 +
psftp.c | 2 ++
3 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/config.c b/config.c
index 220c1aa8..efe1ff1e 100644
--- a/config.c
+++ b/config.c
@@ -1005,7 +1005,7 @@ static void ttymodes_handler(union control *ctrl, void *dlg,
char type;
{
- const char *types = "ANV";
+ const char types[] = {'A', 'N', 'V'};
int button = dlg_radiobutton_get(td->valradio, dlg);
assert(button >= 0 && button < lenof(types));
type = types[button];
diff --git a/portfwd.c b/portfwd.c
index febd8af9..b68e4bb8 100644
--- a/portfwd.c
+++ b/portfwd.c
@@ -359,6 +359,7 @@ static void pfd_receive(Plug plug, int urgent, char *data, int len)
* close the connection rudely.
*/
pfd_close(pf);
+ break;
}
return;
diff --git a/psftp.c b/psftp.c
index aa397181..e4e77c3a 100644
--- a/psftp.c
+++ b/psftp.c
@@ -1050,6 +1050,8 @@ int sftp_cmd_ls(struct sftp_command *cmd)
if (dirh == NULL) {
printf("Unable to open %s: %s\n", dir, fxp_error());
+ sfree(cdir);
+ sfree(unwcdir);
return 0;
} else {
nnames = namesize = 0;
From 4241734dde44caea5a9c73a79db9c6d4cae50861 Mon Sep 17 00:00:00 2001
From: David Taylor
Date: Sat, 1 Jul 2017 23:25:49 +0100
Subject: [PATCH 053/607] Update wcwidth.c with Unicode 9.0.0 data
In order to maintain compatibility with screen[1], update
wcwidth functions to use Unicode 9.0.0 character database.
Updated intervals extracted from output of [2] from ucd files[3].
The comments at the head of [2] state that the output is unrestricted.
Therefore it is not subject to the GPL as applies to the script itself.
[1] See: https://savannah.gnu.org/bugs/?func=detailitem&item_id=50044
[2] https://raw.githubusercontent.com/GNOME/glib/37d4c2941bd0326b8b6e6bb22c81bd424fcc040b/glib/gen-unicode-tables.pl
[3] http://www.unicode.org/Public/9.0.0/ucd/
---
wcwidth.c | 373 ++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 305 insertions(+), 68 deletions(-)
diff --git a/wcwidth.c b/wcwidth.c
index ec0fb2ba..25372a79 100644
--- a/wcwidth.c
+++ b/wcwidth.c
@@ -176,6 +176,120 @@ int mk_wcwidth(unsigned int ucs)
{ 0xE0100, 0xE01EF }
};
+ /* A sorted list of intervals of double-width characters generated by:
+ * https://raw.githubusercontent.com/GNOME/glib/37d4c2941bd0326b8b6e6bb22c81bd424fcc040b/glib/gen-unicode-tables.pl
+ * from the Unicode 9.0.0 data files available at:
+ * http://www.unicode.org/Public/9.0.0/ucd/
+ */
+ static const struct interval wide[] = {
+ {0x1100, 0x115F},
+ {0x231A, 0x231B},
+ {0x2329, 0x232A},
+ {0x23E9, 0x23EC},
+ {0x23F0, 0x23F0},
+ {0x23F3, 0x23F3},
+ {0x25FD, 0x25FE},
+ {0x2614, 0x2615},
+ {0x2648, 0x2653},
+ {0x267F, 0x267F},
+ {0x2693, 0x2693},
+ {0x26A1, 0x26A1},
+ {0x26AA, 0x26AB},
+ {0x26BD, 0x26BE},
+ {0x26C4, 0x26C5},
+ {0x26CE, 0x26CE},
+ {0x26D4, 0x26D4},
+ {0x26EA, 0x26EA},
+ {0x26F2, 0x26F3},
+ {0x26F5, 0x26F5},
+ {0x26FA, 0x26FA},
+ {0x26FD, 0x26FD},
+ {0x2705, 0x2705},
+ {0x270A, 0x270B},
+ {0x2728, 0x2728},
+ {0x274C, 0x274C},
+ {0x274E, 0x274E},
+ {0x2753, 0x2755},
+ {0x2757, 0x2757},
+ {0x2795, 0x2797},
+ {0x27B0, 0x27B0},
+ {0x27BF, 0x27BF},
+ {0x2B1B, 0x2B1C},
+ {0x2B50, 0x2B50},
+ {0x2B55, 0x2B55},
+ {0x2E80, 0x2E99},
+ {0x2E9B, 0x2EF3},
+ {0x2F00, 0x2FD5},
+ {0x2FF0, 0x2FFB},
+ {0x3000, 0x303E},
+ {0x3041, 0x3096},
+ {0x3099, 0x30FF},
+ {0x3105, 0x312D},
+ {0x3131, 0x318E},
+ {0x3190, 0x31BA},
+ {0x31C0, 0x31E3},
+ {0x31F0, 0x321E},
+ {0x3220, 0x3247},
+ {0x3250, 0x32FE},
+ {0x3300, 0x4DBF},
+ {0x4E00, 0xA48C},
+ {0xA490, 0xA4C6},
+ {0xA960, 0xA97C},
+ {0xAC00, 0xD7A3},
+ {0xF900, 0xFAFF},
+ {0xFE10, 0xFE19},
+ {0xFE30, 0xFE52},
+ {0xFE54, 0xFE66},
+ {0xFE68, 0xFE6B},
+ {0xFF01, 0xFF60},
+ {0xFFE0, 0xFFE6},
+ {0x16FE0, 0x16FE0},
+ {0x17000, 0x187EC},
+ {0x18800, 0x18AF2},
+ {0x1B000, 0x1B001},
+ {0x1F004, 0x1F004},
+ {0x1F0CF, 0x1F0CF},
+ {0x1F18E, 0x1F18E},
+ {0x1F191, 0x1F19A},
+ {0x1F200, 0x1F202},
+ {0x1F210, 0x1F23B},
+ {0x1F240, 0x1F248},
+ {0x1F250, 0x1F251},
+ {0x1F300, 0x1F320},
+ {0x1F32D, 0x1F335},
+ {0x1F337, 0x1F37C},
+ {0x1F37E, 0x1F393},
+ {0x1F3A0, 0x1F3CA},
+ {0x1F3CF, 0x1F3D3},
+ {0x1F3E0, 0x1F3F0},
+ {0x1F3F4, 0x1F3F4},
+ {0x1F3F8, 0x1F43E},
+ {0x1F440, 0x1F440},
+ {0x1F442, 0x1F4FC},
+ {0x1F4FF, 0x1F53D},
+ {0x1F54B, 0x1F54E},
+ {0x1F550, 0x1F567},
+ {0x1F57A, 0x1F57A},
+ {0x1F595, 0x1F596},
+ {0x1F5A4, 0x1F5A4},
+ {0x1F5FB, 0x1F64F},
+ {0x1F680, 0x1F6C5},
+ {0x1F6CC, 0x1F6CC},
+ {0x1F6D0, 0x1F6D2},
+ {0x1F6EB, 0x1F6EC},
+ {0x1F6F4, 0x1F6F6},
+ {0x1F910, 0x1F91E},
+ {0x1F920, 0x1F927},
+ {0x1F930, 0x1F930},
+ {0x1F933, 0x1F93E},
+ {0x1F940, 0x1F94B},
+ {0x1F950, 0x1F95E},
+ {0x1F980, 0x1F991},
+ {0x1F9C0, 0x1F9C0},
+ {0x20000, 0x2FFFD},
+ {0x30000, 0x3FFFD},
+ };
+
/* test for 8-bit control characters */
if (ucs == 0)
return 0;
@@ -189,20 +303,13 @@ int mk_wcwidth(unsigned int ucs)
/* if we arrive here, ucs is not a combining or C0/C1 control character */
- return 1 +
- (ucs >= 0x1100 &&
- (ucs <= 0x115f || /* Hangul Jamo init. consonants */
- ucs == 0x2329 || ucs == 0x232a ||
- (ucs >= 0x2e80 && ucs <= 0xa4cf &&
- ucs != 0x303f) || /* CJK ... Yi */
- (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
- (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
- (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
- (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
- (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
- (ucs >= 0xffe0 && ucs <= 0xffe6) ||
- (ucs >= 0x20000 && ucs <= 0x2fffd) ||
- (ucs >= 0x30000 && ucs <= 0x3fffd)));
+ /* binary search in table of double-width characters */
+ if (bisearch(ucs, wide,
+ sizeof(wide) / sizeof(struct interval) - 1))
+ return 2;
+
+ /* normal width character */
+ return 1;
}
@@ -231,61 +338,191 @@ int mk_wcswidth(const unsigned int *pwcs, size_t n)
*/
int mk_wcwidth_cjk(unsigned int ucs)
{
- /* sorted list of non-overlapping intervals of East Asian Ambiguous
- * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
+ /* A sorted list of intervals of ambiguous width characters generated by:
+ * https://raw.githubusercontent.com/GNOME/glib/37d4c2941bd0326b8b6e6bb22c81bd424fcc040b/glib/gen-unicode-tables.pl
+ * from the Unicode 9.0.0 data files available at:
+ * http://www.unicode.org/Public/9.0.0/ucd/
+ */
static const struct interval ambiguous[] = {
- { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
- { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
- { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
- { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
- { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
- { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
- { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
- { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
- { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
- { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
- { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
- { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
- { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
- { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
- { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
- { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
- { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
- { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
- { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
- { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
- { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
- { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
- { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
- { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
- { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
- { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
- { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
- { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
- { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
- { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
- { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
- { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
- { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
- { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
- { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
- { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
- { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
- { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
- { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
- { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
- { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
- { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
- { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
- { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
- { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
- { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
- { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
- { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
- { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
- { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
- { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
- { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
+ {0x00A1, 0x00A1},
+ {0x00A4, 0x00A4},
+ {0x00A7, 0x00A8},
+ {0x00AA, 0x00AA},
+ {0x00AD, 0x00AE},
+ {0x00B0, 0x00B4},
+ {0x00B6, 0x00BA},
+ {0x00BC, 0x00BF},
+ {0x00C6, 0x00C6},
+ {0x00D0, 0x00D0},
+ {0x00D7, 0x00D8},
+ {0x00DE, 0x00E1},
+ {0x00E6, 0x00E6},
+ {0x00E8, 0x00EA},
+ {0x00EC, 0x00ED},
+ {0x00F0, 0x00F0},
+ {0x00F2, 0x00F3},
+ {0x00F7, 0x00FA},
+ {0x00FC, 0x00FC},
+ {0x00FE, 0x00FE},
+ {0x0101, 0x0101},
+ {0x0111, 0x0111},
+ {0x0113, 0x0113},
+ {0x011B, 0x011B},
+ {0x0126, 0x0127},
+ {0x012B, 0x012B},
+ {0x0131, 0x0133},
+ {0x0138, 0x0138},
+ {0x013F, 0x0142},
+ {0x0144, 0x0144},
+ {0x0148, 0x014B},
+ {0x014D, 0x014D},
+ {0x0152, 0x0153},
+ {0x0166, 0x0167},
+ {0x016B, 0x016B},
+ {0x01CE, 0x01CE},
+ {0x01D0, 0x01D0},
+ {0x01D2, 0x01D2},
+ {0x01D4, 0x01D4},
+ {0x01D6, 0x01D6},
+ {0x01D8, 0x01D8},
+ {0x01DA, 0x01DA},
+ {0x01DC, 0x01DC},
+ {0x0251, 0x0251},
+ {0x0261, 0x0261},
+ {0x02C4, 0x02C4},
+ {0x02C7, 0x02C7},
+ {0x02C9, 0x02CB},
+ {0x02CD, 0x02CD},
+ {0x02D0, 0x02D0},
+ {0x02D8, 0x02DB},
+ {0x02DD, 0x02DD},
+ {0x02DF, 0x02DF},
+ {0x0300, 0x036F},
+ {0x0391, 0x03A1},
+ {0x03A3, 0x03A9},
+ {0x03B1, 0x03C1},
+ {0x03C3, 0x03C9},
+ {0x0401, 0x0401},
+ {0x0410, 0x044F},
+ {0x0451, 0x0451},
+ {0x2010, 0x2010},
+ {0x2013, 0x2016},
+ {0x2018, 0x2019},
+ {0x201C, 0x201D},
+ {0x2020, 0x2022},
+ {0x2024, 0x2027},
+ {0x2030, 0x2030},
+ {0x2032, 0x2033},
+ {0x2035, 0x2035},
+ {0x203B, 0x203B},
+ {0x203E, 0x203E},
+ {0x2074, 0x2074},
+ {0x207F, 0x207F},
+ {0x2081, 0x2084},
+ {0x20AC, 0x20AC},
+ {0x2103, 0x2103},
+ {0x2105, 0x2105},
+ {0x2109, 0x2109},
+ {0x2113, 0x2113},
+ {0x2116, 0x2116},
+ {0x2121, 0x2122},
+ {0x2126, 0x2126},
+ {0x212B, 0x212B},
+ {0x2153, 0x2154},
+ {0x215B, 0x215E},
+ {0x2160, 0x216B},
+ {0x2170, 0x2179},
+ {0x2189, 0x2189},
+ {0x2190, 0x2199},
+ {0x21B8, 0x21B9},
+ {0x21D2, 0x21D2},
+ {0x21D4, 0x21D4},
+ {0x21E7, 0x21E7},
+ {0x2200, 0x2200},
+ {0x2202, 0x2203},
+ {0x2207, 0x2208},
+ {0x220B, 0x220B},
+ {0x220F, 0x220F},
+ {0x2211, 0x2211},
+ {0x2215, 0x2215},
+ {0x221A, 0x221A},
+ {0x221D, 0x2220},
+ {0x2223, 0x2223},
+ {0x2225, 0x2225},
+ {0x2227, 0x222C},
+ {0x222E, 0x222E},
+ {0x2234, 0x2237},
+ {0x223C, 0x223D},
+ {0x2248, 0x2248},
+ {0x224C, 0x224C},
+ {0x2252, 0x2252},
+ {0x2260, 0x2261},
+ {0x2264, 0x2267},
+ {0x226A, 0x226B},
+ {0x226E, 0x226F},
+ {0x2282, 0x2283},
+ {0x2286, 0x2287},
+ {0x2295, 0x2295},
+ {0x2299, 0x2299},
+ {0x22A5, 0x22A5},
+ {0x22BF, 0x22BF},
+ {0x2312, 0x2312},
+ {0x2460, 0x24E9},
+ {0x24EB, 0x254B},
+ {0x2550, 0x2573},
+ {0x2580, 0x258F},
+ {0x2592, 0x2595},
+ {0x25A0, 0x25A1},
+ {0x25A3, 0x25A9},
+ {0x25B2, 0x25B3},
+ {0x25B6, 0x25B7},
+ {0x25BC, 0x25BD},
+ {0x25C0, 0x25C1},
+ {0x25C6, 0x25C8},
+ {0x25CB, 0x25CB},
+ {0x25CE, 0x25D1},
+ {0x25E2, 0x25E5},
+ {0x25EF, 0x25EF},
+ {0x2605, 0x2606},
+ {0x2609, 0x2609},
+ {0x260E, 0x260F},
+ {0x261C, 0x261C},
+ {0x261E, 0x261E},
+ {0x2640, 0x2640},
+ {0x2642, 0x2642},
+ {0x2660, 0x2661},
+ {0x2663, 0x2665},
+ {0x2667, 0x266A},
+ {0x266C, 0x266D},
+ {0x266F, 0x266F},
+ {0x269E, 0x269F},
+ {0x26BF, 0x26BF},
+ {0x26C6, 0x26CD},
+ {0x26CF, 0x26D3},
+ {0x26D5, 0x26E1},
+ {0x26E3, 0x26E3},
+ {0x26E8, 0x26E9},
+ {0x26EB, 0x26F1},
+ {0x26F4, 0x26F4},
+ {0x26F6, 0x26F9},
+ {0x26FB, 0x26FC},
+ {0x26FE, 0x26FF},
+ {0x273D, 0x273D},
+ {0x2776, 0x277F},
+ {0x2B56, 0x2B59},
+ {0x3248, 0x324F},
+ {0xE000, 0xF8FF},
+ {0xFE00, 0xFE0F},
+ {0xFFFD, 0xFFFD},
+ {0x1F100, 0x1F10A},
+ {0x1F110, 0x1F12D},
+ {0x1F130, 0x1F169},
+ {0x1F170, 0x1F18D},
+ {0x1F18F, 0x1F190},
+ {0x1F19B, 0x1F1AC},
+ {0xE0100, 0xE01EF},
+ {0xF0000, 0xFFFFD},
+ {0x100000, 0x10FFFD},
};
/* binary search in table of non-spacing characters */
From 4624115b76903b0b9ad97bee713afafcbd70a31c Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 3 Jul 2017 07:27:05 +0100
Subject: [PATCH 054/607] Make -DMINEFIELD show up in Windows buildinfo.
I listed a lot of other build options, but not that one. The release
checklist still recommends doing test builds with it, so it seems
sensible to arrange that you can tell if a build _is_ one of those or
not.
---
misc.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/misc.c b/misc.c
index 469e4aeb..fc5b149d 100644
--- a/misc.c
+++ b/misc.c
@@ -1214,6 +1214,9 @@ char *buildinfo(const char *newline)
}
#endif
+#if defined _WINDOWS && defined MINEFIELD
+ strbuf_catf(buf, "%sBuild option: MINEFIELD", newline);
+#endif
#ifdef NO_SECURITY
strbuf_catf(buf, "%sBuild option: NO_SECURITY", newline);
#endif
From ea0ab1c8218f28957b6e20dd84f9d2a3f19313e6 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 3 Jul 2017 07:19:07 +0100
Subject: [PATCH 055/607] Simplify running of release.pl --setver.
Previously, it demanded that your checkout was in a state where you
had run autoconf but not configure; so if you previously did have a
Makefile then you had to 'make distclean' to remove it, whereas if you
previously had no generated files at all (e.g. starting from a
completely clean checkout) then you had to run mkfiles.pl and
mkauto.sh to generate 'configure'.
This is obviously confusing, and moreover, the dependence on prior
generated files is fragile and prone to them having been generated
wrong. Adjusted the script so that it uses 'git archive' to get a
clean directory containing only the version-controlled files, and then
runs scripts itself to get that directory into the state it wants.
---
CHECKLST.txt | 1 -
release.pl | 7 +++++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/CHECKLST.txt b/CHECKLST.txt
index 8d55ac41..f757c5e0 100644
--- a/CHECKLST.txt
+++ b/CHECKLST.txt
@@ -52,7 +52,6 @@ for it:
- Now update the version numbers and the transcripts in the docs, by
checking out the release branch and running
- make distclean
./release.pl --version=X.YZ --setver
Then check that the resulting automated git commit has updated the
diff --git a/release.pl b/release.pl
index cf73d9eb..7c76c6ae 100755
--- a/release.pl
+++ b/release.pl
@@ -31,8 +31,11 @@
0 == system "git", "diff-files", "--quiet" or die "working tree is dirty";
-f "Makefile" and die "run 'make distclean' first";
my $builddir = tempdir(DIR => ".", CLEANUP => 1);
- 0 == system "./mkfiles.pl" or die;
- 0 == system "cd $builddir && ../configure" or die;
+ 0 == system "git archive --format=tar HEAD | ( cd $builddir && tar xf - )"
+ or die;
+ 0 == system "cd $builddir && ./mkfiles.pl" or die;
+ 0 == system "cd $builddir && ./mkauto.sh" or die;
+ 0 == system "cd $builddir && ./configure" or die;
0 == system "cd $builddir && make pscp plink RELEASE=${version}" or die;
our $pscp_transcript = `cd $builddir && ./pscp --help`;
$pscp_transcript =~ s/^Unidentified build/Release ${version}/m or die;
From 5cac6013b702b014a063ed98ad91128308bc1b48 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 3 Jul 2017 07:38:20 +0100
Subject: [PATCH 056/607] Rework the release checklist for current practice.
In recent releases we've taken to making the actual release build (or
rather, candidates for it) ahead of time so that we can do some
slightly more thorough last-minute testing of the exact binaries that
we're going to release to everyone. It's time I actually wrote that
procedure down in the checklist, so that I remember what it is.
In particular, we had the idea that we should not properly GPG-sign
the release until the last moment, and use the presence of a set of
full GPG signatures as a means of distinguishing the real release
build from an RC that accidentally got out into the wild somehow. This
checklist update formalises that too, and documents the method I used
of ensuring the binaries weren't tampered with between RC building and
release signing (by making a signature on just the sha512sums). I also
include in this commit an extra command-line option to sign.sh to make
that preliminary signature step more convenient.
---
CHECKLST.txt | 104 +++++++++++++++++++++++++++++++--------------------
sign.sh | 43 ++++++++++++++++-----
2 files changed, 97 insertions(+), 50 deletions(-)
diff --git a/CHECKLST.txt b/CHECKLST.txt
index f757c5e0..21f38656 100644
--- a/CHECKLST.txt
+++ b/CHECKLST.txt
@@ -24,25 +24,28 @@ pre-releases on the website:
add a news announcement in components/news. (Previous naming
convention has been to name it in the form 'X.YZ-pre.mi'.)
-Preparing to make a release
----------------------------
+Things to do during the branch-stabilisation period:
-Now that PuTTY is in git, a lot of the release preparation can be done
-in advance, in local checkouts, and not pushed until the actual
-process of _releasing_ it.
+ - Go through the source (including the documentation), and the
+ website, and review anything tagged with a comment containing the
+ word XXX-REVIEW-BEFORE-RELEASE. (Any such comments should state
+ clearly what needs to be done.)
-To begin with, before dropping the tag, make sure everything is ready
-for it:
+ - Do some testing of the Windows version with Minefield (you can
+ build a Minefield version using 'bob . XFLAGS=-DMINEFIELD'), and of
+ the Unix version with valgrind. In particular, any headline
+ features for the release should get a workout with memory checking
+ enabled!
- - First of all, go through the source (including the documentation),
- and the website, and review anything tagged with a comment
- containing the word XXX-REVIEW-BEFORE-RELEASE.
- (Any such comments should state clearly what needs to be done.)
+Making a release candidate build
+--------------------------------
- - Also, do some testing of the Windows version with Minefield, and
- of the Unix version with valgrind or efence or both. In
- particular, any headline features for the release should get a
- workout with memory checking enabled!
+ - Make a directory to hold all the release paraphernalia. I usually
+ call it ~/src/putty/X.YZ (where X.YZ will stand throughout for the
+ version number). In that directory, make a git clone of the PuTTY
+ repository, where you can make release-related commits and tags
+ tentatively, and keep them out of the way of any 'git push' you
+ might still be doing in other checkouts.
- Double-check that we have removed anything tagged with a comment
containing the words XXX-REMOVE-BEFORE-RELEASE or
@@ -50,7 +53,8 @@ for it:
hits in this file itself.)
- Now update the version numbers and the transcripts in the docs, by
- checking out the release branch and running
+ checking out the release branch in the release-specific checkout
+ and running
./release.pl --version=X.YZ --setver
@@ -71,6 +75,42 @@ for it:
- If the release is on a branch (which I expect it generally will
be), merge that branch to master.
+ - Make a release-candidate build from the release tag, and put the
+ build.out and build.log dfiles somewhere safe. Normally I store
+ these in an adjacent directory, so I'll run a command like
+ bob -o ../X.YZ/build-X.YZ-rcN.out -l ../X.YZ/build-X.YZ-rcN.log -c X.YZ . RELEASE=X.YZ
+ This should generate a basically valid release directory as
+ `build-X.YZ-rcN.out/putty', and provide link maps and sign.sh
+ alongside that.
+
+ - Double-check in build-X.YZ-rcN.log that the release was built from
+ the right git commit.
+
+ - Make a preliminary gpg signature, but don't run the full release-
+ signing procedure. (We use the presence of a full set of GPG
+ signatures to distinguish _abandoned_ release candidates from the
+ one that ended up being the release.) In the 'build.X.YZ-rcN.out'
+ directory, run
+ sh sign.sh -r -p putty
+ and you should only have to enter the release key passphrase once,
+ which will generate a clearsigned file called
+ sha512sums-preliminary.gpg _outside_ the 'putty' subdirectory.
+
+ - For my own safety, make the release candidate build read-only.
+ chmod -R a-w build-X.YZ-rcN.out build-X.YZ-rcN.log
+
+ - Now do some checking of the release binaries, and pass them to the
+ rest of the team to do some as well. Do at least these things:
+ * make sure they basically work
+ * check they report the right version number
+ * if there's any easily observable behaviour difference between
+ the release branch and master, arrange to observe it
+ * test the Windows installer
+ * test the Unix source tarball.
+
+Preparing to make the release
+-----------------------------
+
- Write a release announcement (basically a summary of the changes
since the last release). Squirrel it away in
thyestes:src/putty-local/announce- in case it's needed again
@@ -96,31 +136,15 @@ for it:
them as fixed in the new release), add appropriate Fixed-in
headers for those.
- - Make a release-candidate build from the release tag, and put the
- build.out and build.log dfiles somewhere safe. Normally I store
- these in an adjacent directory, so I'll run a command like
- bob -o ../X.YZ/build-X.YZ-rcN.out -l ../X.YZ/build-X.YZ-rcN.log -c X.YZ . RELEASE=X.YZ
- This should generate a basically valid release directory as
- `build-X.YZ-rcN.out/putty', and provide link maps and sign.sh
- alongside that.
-
- - Double-check in build-X.YZ-rcN.log that the release was built from
- the right git commit.
-
- - Do a bit of checking of the release binaries:
- * make sure they basically work
- * check they report the right version number
- * if there's any easily observable behaviour difference between
- the release branch and master, arrange to observe it
- * test the Windows installer
- * test the Unix source tarball.
-
- - Sign the release: in the `build-X.YZ-rcN.out' directory, type
+ - Sign the release in full. In the `build-X.YZ-rcN.out' directory,
+ re-verify that the preliminary signed checksums file has a correct
+ signature on it and also matches the files you're about to sign for real:
+ gpg -d sha512sums-preliminary.gpg | (cd putty; sha512sum -c)
+ If the combined output of that pipeline reports both a good
+ signature (from the release key) and a successful verification of
+ all the sha512sums, then all is well, so now run
sh sign.sh -r putty
- and enter the passphrases a lot of times.
-
- - For my own safety, make the release candidate build read-only.
- chmod -R a-w build-X.YZ-rcN.out build-X.YZ-rcN.log
+ and enter the release key passphrase a lot of times.
The actual release procedure
----------------------------
diff --git a/sign.sh b/sign.sh
index bdf6245f..8dbdb613 100755
--- a/sign.sh
+++ b/sign.sh
@@ -10,11 +10,27 @@
set -e
keyname=EEF20295D15F7E8A
+preliminary=false
-if test "x$1" = "x-r"; then
- shift
- keyname=9DFE2648B43434E4
-fi
+while :; do
+ case "$1" in
+ -r)
+ shift
+ keyname=9DFE2648B43434E4
+ ;;
+ -p)
+ shift
+ preliminary=true
+ ;;
+ -*)
+ echo "Unknown option '$1'" >&2
+ exit 1
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
sign() {
# Check for the prior existence of the signature, so we can
@@ -27,9 +43,16 @@ sign() {
cd "$1"
echo "===== Signing with key '$keyname'"
-for i in putty*src.zip putty*.tar.gz w32/*.exe w32/*.zip w32/*.msi w64/*.exe w64/*.zip w64/*.msi w32old/*.exe w32old/*.zip; do
- sign --detach-sign "$i" "$i.gpg"
-done
-for i in md5sums sha1sums sha256sums sha512sums; do
- sign --clearsign "$i" "$i.gpg"
-done
+if $preliminary; then
+ sign --clearsign sha512sums ../sha512sums-preliminary.gpg
+else
+ for i in putty*src.zip putty*.tar.gz \
+ w32/*.exe w32/*.zip w32/*.msi \
+ w64/*.exe w64/*.zip w64/*.msi \
+ w32old/*.exe w32old/*.zip; do
+ sign --detach-sign "$i" "$i.gpg"
+ done
+ for i in md5sums sha1sums sha256sums sha512sums; do
+ sign --clearsign "$i" "$i.gpg"
+ done
+fi
From a2b040ee094fcd43b4d53e14121c8c2301fba303 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 4 Jul 2017 19:35:18 +0100
Subject: [PATCH 057/607] Expand the About box.
It wasn't big enough to fit the full buildinfo text, when compiling
with clang-cl which has a bulky compiler identification string.
---
windows/win_res.rc2 | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/windows/win_res.rc2 b/windows/win_res.rc2
index 92d39cd5..0c486ce5 100644
--- a/windows/win_res.rc2
+++ b/windows/win_res.rc2
@@ -16,15 +16,15 @@ IDI_MAINICON ICON "putty.ico"
IDI_CFGICON ICON "puttycfg.ico"
/* Accelerators used: clw */
-IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 270, 106
+IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 270, 136
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About PuTTY"
FONT 8, "MS Shell Dlg"
BEGIN
- DEFPUSHBUTTON "&Close", IDOK, 216, 88, 48, 14
- PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 88, 70, 14
- PUSHBUTTON "Visit &Web Site", IDA_WEB, 140, 88, 70, 14
- EDITTEXT IDA_TEXT, 10, 6, 250, 80, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE
+ DEFPUSHBUTTON "&Close", IDOK, 216, 118, 48, 14
+ PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 118, 70, 14
+ PUSHBUTTON "Visit &Web Site", IDA_WEB, 140, 118, 70, 14
+ EDITTEXT IDA_TEXT, 10, 6, 250, 110, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE
END
/* Accelerators used: aco */
From 3cd10509a51edf5a21cdc80aabf7e6a934522d47 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 4 Jul 2017 20:29:54 +0100
Subject: [PATCH 058/607] Update version number for 0.70 release.
---
Buildscr | 2 +-
LATEST.VER | 2 +-
doc/plink.but | 2 +-
doc/pscp.but | 2 +-
windows/putty.iss | 8 ++++----
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/Buildscr b/Buildscr
index ab94ea92..8833a9c4 100644
--- a/Buildscr
+++ b/Buildscr
@@ -35,7 +35,7 @@ module putty
ifeq "$(RELEASE)" "" set Ndate $(!builddate)
ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -pe 's/(....)(..)(..)/$$1-$$2-$$3/' > date
ifneq "$(Ndate)" "" read Date date
-set Epoch 16280 # update this at every release
+set Epoch 16351 # update this at every release
ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -ne 'use Time::Local; /(....)(..)(..)/ and print timegm(0,0,0,$$3,$$2-1,$$1) / 86400 - $(Epoch)' > days
ifneq "$(Ndate)" "" read Days days
diff --git a/LATEST.VER b/LATEST.VER
index b04c6474..ac37bbea 100644
--- a/LATEST.VER
+++ b/LATEST.VER
@@ -1 +1 @@
-0.69
+0.70
diff --git a/doc/plink.but b/doc/plink.but
index 153982e0..459ceadf 100644
--- a/doc/plink.but
+++ b/doc/plink.but
@@ -41,7 +41,7 @@ use Plink:
\c Z:\sysosd>plink
\c Plink: command-line connection utility
-\c Release 0.69
+\c Release 0.70
\c Usage: plink [options] [user@]host [command]
\c ("host" can also be a PuTTY saved session name)
\c Options:
diff --git a/doc/pscp.but b/doc/pscp.but
index 30a47f83..7b90810b 100644
--- a/doc/pscp.but
+++ b/doc/pscp.but
@@ -39,7 +39,7 @@ use PSCP:
\c Z:\owendadmin>pscp
\c PuTTY Secure Copy client
-\c Release 0.69
+\c Release 0.70
\c Usage: pscp [options] [user@]host:source target
\c pscp [options] source [source...] [user@]host:target
\c pscp [options] -ls [user@]host:filespec
diff --git a/windows/putty.iss b/windows/putty.iss
index 3fadcb92..ae926310 100644
--- a/windows/putty.iss
+++ b/windows/putty.iss
@@ -14,10 +14,10 @@
[Setup]
AppName=PuTTY
-AppVerName=PuTTY version 0.69
-VersionInfoTextVersion=Release 0.69
-AppVersion=0.69
-VersionInfoVersion=0.69.0.0
+AppVerName=PuTTY version 0.70
+VersionInfoTextVersion=Release 0.70
+AppVersion=0.70
+VersionInfoVersion=0.70.0.0
AppPublisher=Simon Tatham
AppPublisherURL=https://www.chiark.greenend.org.uk/~sgtatham/putty/
AppReadmeFile={app}\README.txt
From 7470e3bdaffc64260c0f786908682128b58c38c7 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 8 Jul 2017 09:20:55 +0100
Subject: [PATCH 059/607] Stop release.pl --setver failing if Makefile exists.
This should have been part of commit ea0ab1c82; it's part of the
general revamp that we regenerate the autoconf files ourselves in a
clean directory - so we don't depend on them being present, but we
also don't depend on them being _absent_ either.
But when I made that commit my immediate priority was to get --setver
to work from a completely clean checkout, not from one already
littered with cruft, so I didn't check quite as carefully that my
changes fixed the problem in the latter case too :-)
---
release.pl | 1 -
1 file changed, 1 deletion(-)
diff --git a/release.pl b/release.pl
index 7c76c6ae..da7d2a88 100755
--- a/release.pl
+++ b/release.pl
@@ -29,7 +29,6 @@
0 == system "git", "diff-index", "--quiet", "--cached", "HEAD"
or die "index is dirty";
0 == system "git", "diff-files", "--quiet" or die "working tree is dirty";
- -f "Makefile" and die "run 'make distclean' first";
my $builddir = tempdir(DIR => ".", CLEANUP => 1);
0 == system "git archive --format=tar HEAD | ( cd $builddir && tar xf - )"
or die;
From 0e2955ffbff083212d2a8a0d7fd829716f283081 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 8 Jul 2017 09:23:51 +0100
Subject: [PATCH 060/607] Add a --no-ftp mode to the release.pl download
checks.
chiark's ftp server sometimes randomly refuses downloads. In the case
where this happens at postcheck time, this isn't really
release-blocking (all the files have been test-downloaded by precheck
already, so the main aim at this stage is to check that the 'latest'
symlink points to the right place, and even one or two successful
downloads are good enough to confirm that in practice). So now I can
add --no-ftp to the postcheck command line if that makes my life
easier.
---
release.pl | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/release.pl b/release.pl
index da7d2a88..b5ad149c 100755
--- a/release.pl
+++ b/release.pl
@@ -15,11 +15,13 @@
my $upload = 0;
my $precheck = 0;
my $postcheck = 0;
+my $skip_ftp = 0;
GetOptions("version=s" => \$version,
"setver" => \$setver,
"upload" => \$upload,
"precheck" => \$precheck,
- "postcheck" => \$postcheck)
+ "postcheck" => \$postcheck,
+ "no-ftp" => \$skip_ftp)
or &usage();
# --set-version: construct a local commit which updates the version
@@ -163,11 +165,13 @@
}
# Now test-download the files themselves.
- my $ftpdata = `curl -s $ftp_uri`;
- printf " got %d bytes via FTP", length $ftpdata;
- die "FTP download for $ftp_uri did not match"
- if $ftpdata ne $real_content;
- print ", ok\n";
+ unless ($skip_ftp) {
+ my $ftpdata = `curl -s $ftp_uri`;
+ printf " got %d bytes via FTP", length $ftpdata;
+ die "FTP download for $ftp_uri did not match"
+ if $ftpdata ne $real_content;
+ print ", ok\n";
+ }
my $ua = LWP::UserAgent->new;
my $httpresponse = $ua->get($http_uri);
From 309c3dfd95505f1bf1e4b4d488a5f03f0cf24b3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?=
Date: Thu, 6 Jul 2017 10:18:27 +0200
Subject: [PATCH 061/607] Add -share -noshare command line option to plink to
share SSL connections.
---
cmdline.c | 13 ++++++++++++-
doc/man-pl.but | 9 +++++++++
doc/plink.but | 24 ++++++++++++++++++++++++
unix/uxplink.c | 2 ++
windows/winplink.c | 2 ++
5 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/cmdline.c b/cmdline.c
index f288ed62..d2a81592 100644
--- a/cmdline.c
+++ b/cmdline.c
@@ -403,7 +403,18 @@ int cmdline_process_param(const char *p, char *value,
SAVEABLE(0);
conf_set_int(conf, CONF_tryagent, FALSE);
}
-
+ if (!strcmp(p, "-share")) {
+ RETURN(1);
+ UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
+ SAVEABLE(0);
+ conf_set_int(conf, CONF_ssh_connection_sharing, TRUE);
+ }
+ if (!strcmp(p, "-noshare")) {
+ RETURN(1);
+ UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
+ SAVEABLE(0);
+ conf_set_int(conf, CONF_ssh_connection_sharing, FALSE);
+ }
if (!strcmp(p, "-A")) {
RETURN(1);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
diff --git a/doc/man-pl.but b/doc/man-pl.but
index 9f411871..58ca7a28 100644
--- a/doc/man-pl.but
+++ b/doc/man-pl.but
@@ -182,6 +182,15 @@ which of the agent's keys to use. }
\dd Allow use of an authentication agent. (This option is only necessary
to override a setting in a saved session.)
+\dt \cw{\-noshare}
+
+\dd Don't test and try to share an existing connection, always make
+a new connection.
+
+\dt \cw{\-share}
+
+\dd Test and try to share an existing connection.
+
\dt \cw{\-hostkey} \e{key}
\dd Specify an acceptable host public key. This option may be specified
diff --git a/doc/plink.but b/doc/plink.but
index 459ceadf..74da18a1 100644
--- a/doc/plink.but
+++ b/doc/plink.but
@@ -75,6 +75,8 @@ use Plink:
\c -i key private key file for user authentication
\c -noagent disable use of Pageant
\c -agent enable use of Pageant
+\c -noshare disable use of connection sharing
+\c -share enable use of connection sharing
\c -hostkey aa:bb:cc:...
\c manually specify a host key (may be repeated)
\c -m file read remote command(s) from file
@@ -237,6 +239,28 @@ line.
(This option is only meaningful with the SSH-2 protocol.)
+\S2{plink-option-share} \I{-share-plink}\c{-share}:
+Test and try to share an existing connection.
+
+This option tris to detect if an existing connection can be shared
+(See \k{config-ssh-sharing} for more information about SSH connection
+sharing.) and reuses that connection.
+
+A Plink invocation of the form:
+
+\c plink -share
+\e iiiiiiiii
+
+will test whether there is currently a viable \q{upstream} for the
+session in question, which can be specified using any syntax you'd
+normally use with Plink to make an actual connection (a host/port
+number, a bare saved session name, \c{-load}, etc). If no \q{upstream}
+viable session is found and \c{-share} is specified, this connection
+will be become the \q{upstream} connection for subsequent connection
+sharing tries.
+
+(This option is only meaningful with the SSH-2 protocol.)
+
\S2{plink-option-shareexists} \I{-shareexists-plink}\c{-shareexists}:
test for connection-sharing upstream
diff --git a/unix/uxplink.c b/unix/uxplink.c
index e891d66a..2a1926ef 100644
--- a/unix/uxplink.c
+++ b/unix/uxplink.c
@@ -579,6 +579,8 @@ static void usage(void)
printf(" -i key private key file for user authentication\n");
printf(" -noagent disable use of Pageant\n");
printf(" -agent enable use of Pageant\n");
+ printf(" -noshare disable use of connection sharing\n");
+ printf(" -share enable use of connection sharing\n");
printf(" -hostkey aa:bb:cc:...\n");
printf(" manually specify a host key (may be repeated)\n");
printf(" -m file read remote command(s) from file\n");
diff --git a/windows/winplink.c b/windows/winplink.c
index ea417501..84e47d6e 100644
--- a/windows/winplink.c
+++ b/windows/winplink.c
@@ -207,6 +207,8 @@ static void usage(void)
printf(" -i key private key file for user authentication\n");
printf(" -noagent disable use of Pageant\n");
printf(" -agent enable use of Pageant\n");
+ printf(" -noshare disable use of connection sharing\n");
+ printf(" -share enable use of connection sharing\n");
printf(" -hostkey aa:bb:cc:...\n");
printf(" manually specify a host key (may be repeated)\n");
printf(" -m file read remote command(s) from file\n");
From 25683f0f3d5a654c1e2a8e1d43dca3778a77e7de Mon Sep 17 00:00:00 2001
From: Jacob Nevins
Date: Wed, 12 Jul 2017 10:19:23 +0100
Subject: [PATCH 062/607] Add a FAQ about servers that don't like IUTF8.
---
doc/faq.but | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/doc/faq.but b/doc/faq.but
index cd3254a8..4d9e14a9 100644
--- a/doc/faq.but
+++ b/doc/faq.but
@@ -1002,6 +1002,26 @@ appropriate kind of binaries in \cw{SYSTEM32}. Thus, operations in
the PuTTY suite that involve it accessing its own executables, such as
\i{\q{New Session}} and \q{Duplicate Session}, will not work.
+\S{faq-iutf8}{Question} After I upgraded PuTTY to 0.68, I can no longer
+connect to my embedded device or appliance.
+
+If your SSH server has started unexpectedly closing SSH connections
+after you enter your password, and it worked before 0.68, you may have
+a buggy server that objects to certain SSH protocol extensions.
+
+The SSH protocol recently gained a new \q{terminal mode}, \cw{IUTF8},
+which PuTTY sends by default; see \k{config-ttymodes}. This is the
+first new terminal mode since the SSH-2 protocol was defined. While
+servers are supposed to ignore modes they don't know about, some buggy
+servers will unceremoniously close the connection if they see anything
+they don't recognise. SSH servers in embedded devices, network
+appliances, and the like seem to disproportionately have this bug.
+
+If you think you have such a server, from 0.69 onwards you can disable
+sending of the \cw{IUTF8} mode: on the SSH / TTY panel, select
+\cw{IUTF8} on the list, select \q{Nothing}, and press \q{Set}. (It's
+not possible to disable sending this mode in 0.68.)
+
\H{faq-secure} Security questions
\S{faq-publicpc}{Question} Is it safe for me to download PuTTY and
From f0126dd198358f2ae351bc49e8edf056c3ce2c6e Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 17 Jul 2017 20:57:07 +0100
Subject: [PATCH 063/607] Set ssh->mainchan->type earlier.
A user reported a nonsensical assertion failure (claiming that
ssh->version != 2) which suggested that a channel had somehow outlived
its parent Ssh in the situation where the opening of the main session
channel is rejected by the server. Checking with valgrind suggested
that things start to go wrong at the point where we free the half-set-
up ssh->mainchan before having filled in its type field, so that the
switch in ssh_channel_close_local() picks an arbitrary wrong action.
I haven't reproduced the same failure the user reported, but with this
change, Unix plink is now valgrind-clean in that failure situation.
---
ssh.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ssh.c b/ssh.c
index 1d80e919..89041f48 100644
--- a/ssh.c
+++ b/ssh.c
@@ -10722,6 +10722,7 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
} else {
ssh->mainchan = snew(struct ssh_channel);
ssh->mainchan->ssh = ssh;
+ ssh->mainchan->type = CHAN_MAINSESSION;
ssh_channel_init(ssh->mainchan);
if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
@@ -10761,7 +10762,6 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
ssh->mainchan->halfopen = FALSE;
- ssh->mainchan->type = CHAN_MAINSESSION;
ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
update_specials_menu(ssh->frontend);
From 0a93b5d9bc6131c0cd84395f4aa88cac0cb40f23 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Jul 2017 07:22:03 +0100
Subject: [PATCH 064/607] Stop ssh2_msg_channel_response using a stale
ssh_channel.
When it calls through ocr->handler() to process the response to a
channel request, sometimes that call ends up back in the main SSH-2
authconn coroutine, and sometimes _that_ will call bomb_out(), which
closes the whole SSH connection and frees all the channels - so that
when control returns back up the call stack to
ssh2_msg_channel_response itself which continues working with the
channel it was passed, it's using freed memory and things go badly.
This is the sort of thing I'd _like_ to fix using some kind of
large-scale refactoring along the lines of moving all the actual
free() calls out into top-level callbacks, so that _any_ function
which is holding a pointer to something can rely on that pointer still
being valid after it calls a subroutine. But I haven't worked out all
the details of how that system should work, and doubtless it will turn
out to have problems of its own once I do, so here's a point fix which
simply checks if the whole SSH session has been closed (which is easy
- much easier than checking if that _channel_ structure still exists)
and fixes the immediate bug.
(I think this is the real fix for the problem reported by the user I
mention in commit f0126dd19, because I actually got the details wrong
in the log message for that previous commit: the user's SSH server
wasn't rejecting the _opening_ of the main session channel, it was
rejecting the "shell" channel request, so this code path was the one
being exercised. Still, the other bug was real too, so no harm done!)
---
ssh.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/ssh.c b/ssh.c
index 89041f48..aa9fd5dd 100644
--- a/ssh.c
+++ b/ssh.c
@@ -8105,6 +8105,8 @@ static void ssh2_msg_channel_response(Ssh ssh, struct Packet *pktin)
return;
}
ocr->handler(c, pktin, ocr->ctx);
+ if (ssh->state == SSH_STATE_CLOSED)
+ return; /* in case the handler called bomb_out(), which some can */
c->v.v2.chanreq_head = ocr->next;
sfree(ocr);
/*
From 55efbc56a0f482d237b2315efac42c2f3301d986 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 4 Aug 2017 19:46:21 +0100
Subject: [PATCH 065/607] Fix filename of the 64-bit MIT Kerberos DLL.
64-bit PuTTY should be loading gssapi64.dll, not gssapi32.dll. (In
contrast to the Windows system API DLLs, such as secur32.dll which is
also mentioned in the same source file; those keep the "32" in their
name whether we're in Win32 or Win64.)
---
windows/wingss.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/windows/wingss.c b/windows/wingss.c
index ef056167..54b1e477 100644
--- a/windows/wingss.c
+++ b/windows/wingss.c
@@ -13,9 +13,15 @@
/* Windows code to set up the GSSAPI library list. */
+#ifdef _WIN64
+#define MIT_KERB_SUFFIX "64"
+#else
+#define MIT_KERB_SUFFIX "32"
+#endif
+
const int ngsslibs = 3;
const char *const gsslibnames[3] = {
- "MIT Kerberos GSSAPI32.DLL",
+ "MIT Kerberos GSSAPI"MIT_KERB_SUFFIX".DLL",
"Microsoft SSPI SECUR32.DLL",
"User-specified GSSAPI DLL",
};
@@ -90,7 +96,6 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
list->nlibraries = 0;
/* MIT Kerberos GSSAPI implementation */
- /* TODO: For 64-bit builds, check for gssapi64.dll */
module = NULL;
if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", ®key)
== ERROR_SUCCESS) {
@@ -115,7 +120,7 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
p_AddDllDirectory(dllPath);
sfree(dllPath);
}
- strcat (buffer, "\\gssapi32.dll");
+ strcat (buffer, "\\gssapi"MIT_KERB_SUFFIX".dll");
module = LoadLibraryEx (buffer, NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32 |
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
@@ -130,7 +135,7 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
&list->libraries[list->nlibraries++];
lib->id = 0;
- lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL";
+ lib->gsslogmsg = "Using GSSAPI from GSSAPI"MIT_KERB_SUFFIX".DLL";
lib->handle = (void *)module;
#define BIND_GSS_FN(name) \
From a459fc58e844a2152cedb068d5b552bd3aabd654 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 26 Aug 2017 15:23:56 +0100
Subject: [PATCH 066/607] Switch to producing .res files, not .res.o.
I've just upgraded my build process to a version of lld-link
that knows how to read .res, and I think it's a slightly more
commonly found format, so less confusing to encounter.
---
mkfiles.pl | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/mkfiles.pl b/mkfiles.pl
index 1d83912b..02421316 100755
--- a/mkfiles.pl
+++ b/mkfiles.pl
@@ -556,10 +556,10 @@ sub manpages {
print "\n\n";
foreach $p (&prognames("G:C")) {
($prog, $type) = split ",", $p;
- $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res.o", undef);
+ $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", undef);
print &splitline("\$(BUILDDIR)$prog.exe: " . $objstr), "\n";
- $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res.o", "X.lib");
+ $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", "X.lib");
$subsys = ($type eq "G") ? "windows" : "console";
print &splitline("\t\$(LD) \$(LFLAGS) \$(XLFLAGS) ".
"/out:\$(BUILDDIR)$prog.exe ".
@@ -567,11 +567,11 @@ sub manpages {
"/subsystem:$subsys\$(SUBSYSVER) ".
"\$(EXTRA_$subsys) $objstr")."\n\n";
}
- foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res.o", $dirpfx, "/", "vc")) {
+ foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", $dirpfx, "/", "vc")) {
$extradeps = $forceobj{$d->{obj_orig}} ? ["*.c","*.h","*.rc"] : [];
print &splitline(sprintf("%s: %s", $d->{obj},
join " ", @$extradeps, @{$d->{deps}})), "\n";
- if ($d->{obj} =~ /\.res\.o$/) {
+ if ($d->{obj} =~ /\.res$/) {
print "\t\$(RC) \$(RCFLAGS) ".$d->{deps}->[0]." -o ".$d->{obj}."\n\n";
} else {
print "\t\$(CC) /Fo\$(BUILDDIR) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) /c \$<\n\n";
@@ -581,7 +581,7 @@ sub manpages {
print &def($makefile_extra{'clangcl'}->{'end'});
print "\nclean:\n".
&splitline("\trm -f \$(BUILDDIR)*.obj \$(BUILDDIR)*.exe ".
- "\$(BUILDDIR)*.res.o \$(BUILDDIR)*.map ".
+ "\$(BUILDDIR)*.res \$(BUILDDIR)*.map ".
"\$(BUILDDIR)*.exe.manifest")."\n";
select STDOUT; close OUT;
}
From 4634cd47f75e74a697840fb32f18edb7f1cf41da Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 5 Sep 2017 20:14:33 +0100
Subject: [PATCH 067/607] Avoid zero-length ldisc_send() in terminal.c.
A user reports that a remote window title query, if the window title
is empty or if the option to return it is disabled, fails the
assertion in ldisc_send that I introduced as part of commit c269dd013
to catch any lingering uses of ldisc_send with length 0 that should
have turned into ldisc_echoedit_update. Added a check for len > 0
guarding that ldisc_send call, and likewise at one or two others I
noticed on my way here.
(Probably at some point I should decide that the period of smoking out
lingering old-style ldisc_send(0) calls is over, and declare it safe
to remove that assertion again and get rid of all the cumbersome
safety checks at call sites like these ones. But not quite yet.)
---
terminal.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/terminal.c b/terminal.c
index ba9dd617..f7bcbb95 100644
--- a/terminal.c
+++ b/terminal.c
@@ -3355,7 +3355,7 @@ static void term_out(Terminal *term)
break;
case 'Z': /* DECID: terminal type query */
compatibility(VT100);
- if (term->ldisc)
+ if (term->ldisc && term->id_string[0])
ldisc_send(term->ldisc, term->id_string,
strlen(term->id_string), 0);
break;
@@ -3662,7 +3662,7 @@ static void term_out(Terminal *term)
case 'c': /* DA: terminal type query */
compatibility(VT100);
/* This is the response for a VT102 */
- if (term->ldisc)
+ if (term->ldisc && term->id_string[0])
ldisc_send(term->ldisc, term->id_string,
strlen(term->id_string), 0);
break;
@@ -4069,7 +4069,8 @@ static void term_out(Terminal *term)
p = EMPTY_WINDOW_TITLE;
len = strlen(p);
ldisc_send(term->ldisc, "\033]L", 3, 0);
- ldisc_send(term->ldisc, p, len, 0);
+ if (len > 0)
+ ldisc_send(term->ldisc, p, len, 0);
ldisc_send(term->ldisc, "\033\\", 2, 0);
}
break;
@@ -4082,7 +4083,8 @@ static void term_out(Terminal *term)
p = EMPTY_WINDOW_TITLE;
len = strlen(p);
ldisc_send(term->ldisc, "\033]l", 3, 0);
- ldisc_send(term->ldisc, p, len, 0);
+ if (len > 0)
+ ldisc_send(term->ldisc, p, len, 0);
ldisc_send(term->ldisc, "\033\\", 2, 0);
}
break;
From 4ec27919454102386f18d24df188cac3f663dbdc Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 13 Sep 2017 19:24:17 +0100
Subject: [PATCH 068/607] Remove Makefile.bor.
After a conversation this week with a user who tried to use it, it's
clear that Borland C can't build the up-to-date PuTTY without having
to make too many compromises of functionality (unsupported API
details, no 'long long' type), even above the issues that could be
worked round with extra porting ifdefs.
---
.gitignore | 2 -
README | 4 --
Recipe | 1 -
doc/udp.but | 6 +--
mkfiles.pl | 117 +---------------------------------------------
windows/rcstuff.h | 2 +-
6 files changed, 5 insertions(+), 127 deletions(-)
diff --git a/.gitignore b/.gitignore
index 15c253c2..c04f119b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,7 +19,6 @@
/*.tds
/*.td2
/*.map
-/Makefile.bor
/Makefile.mgw
/Makefile.vc
/Makefile.lcc
@@ -128,7 +127,6 @@
/windows/*.td2
/windows/*.map
/windows/Makefile.clangcl
-/windows/Makefile.bor
/windows/Makefile.mgw
/windows/Makefile.vc
/windows/Makefile.lcc
diff --git a/README b/README
index 50314ca6..de6eb9b0 100644
--- a/README
+++ b/README
@@ -34,10 +34,6 @@ For building on Windows:
MSVC/putty/putty.dsp builds PuTTY itself, MSVC/plink/plink.dsp
builds Plink, and so on.
- - windows/Makefile.bor is for the Borland C compiler. Type `make -f
- Makefile.bor' while in the `windows' subdirectory to build all
- the PuTTY binaries.
-
- windows/Makefile.mgw is for MinGW / Cygwin installations. Type
`make -f Makefile.mgw' while in the `windows' subdirectory to
build all the PuTTY binaries.
diff --git a/Recipe b/Recipe
index f5458122..5715938b 100644
--- a/Recipe
+++ b/Recipe
@@ -16,7 +16,6 @@
!makefile vc windows/Makefile.vc
!makefile vcproj windows/MSVC
!makefile cygwin windows/Makefile.mgw
-!makefile borland windows/Makefile.bor
!makefile lcc windows/Makefile.lcc
!makefile gtk unix/Makefile.gtk
!makefile unix unix/Makefile.ux
diff --git a/doc/udp.but b/doc/udp.but
index 9ca8ed3f..b71688b7 100644
--- a/doc/udp.but
+++ b/doc/udp.but
@@ -138,9 +138,9 @@ construct. Use these wherever possible.
\H{udp-multi-compiler} Independence of specific compiler
-Windows PuTTY can currently be compiled with any of four Windows
-compilers: MS Visual C, Borland's freely downloadable C compiler,
-the Cygwin / \cw{mingw32} GNU tools, and \cw{lcc-win32}.
+Windows PuTTY can currently be compiled with any of three Windows
+compilers: MS Visual C, the Cygwin / \cw{mingw32} GNU tools, and
+\cw{clang} (in MS compatibility mode).
This is a really useful property of PuTTY, because it means people
who want to contribute to the coding don't depend on having a
diff --git a/mkfiles.pl b/mkfiles.pl
index 02421316..6765f2a8 100755
--- a/mkfiles.pl
+++ b/mkfiles.pl
@@ -268,7 +268,7 @@ ($)
# Returns true if the argument is a known makefile type. Otherwise,
# prints a warning and returns false;
if (grep { $type eq $_ }
- ("vc","vcproj","cygwin","borland","lcc","devcppproj","gtk","unix",
+ ("vc","vcproj","cygwin","lcc","devcppproj","gtk","unix",
"am","osx","vstudio10","vstudio12","clangcl")) {
return 1;
}
@@ -659,121 +659,6 @@ sub manpages {
}
-##-- Borland makefile
-if (defined $makefiles{'borland'}) {
- $dirpfx = &dirpfx($makefiles{'borland'}, "\\");
-
- %stdlibs = ( # Borland provides many Win32 API libraries intrinsically
- "advapi32" => 1,
- "comctl32" => 1,
- "comdlg32" => 1,
- "gdi32" => 1,
- "imm32" => 1,
- "shell32" => 1,
- "user32" => 1,
- "winmm" => 1,
- "winspool" => 1,
- "wsock32" => 1,
- );
- open OUT, ">$makefiles{'borland'}"; select OUT;
- print
- "# Makefile for $project_name under Borland C.\n".
- "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
- "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
- # bcc32 command line option is -D not /D
- ($_ = $help) =~ s/([=" ])\/D/$1-D/gs;
- print $_;
- print
- "\n".
- "# If you rename this file to `Makefile', you should change this line,\n".
- "# so that the .rsp files still depend on the correct makefile.\n".
- "MAKEFILE = Makefile.bor\n".
- "\n".
- "# C compilation flags\n".
- "CFLAGS = -D_WINDOWS -DWINVER=0x0500\n".
- "# Resource compilation flags\n".
- "RCFLAGS = -DNO_WINRESRC_H -DWIN32 -D_WIN32 -DWINVER=0x0401\n".
- "\n".
- "# Get include directory for resource compiler\n".
- "!if !\$d(BCB)\n".
- "BCB = \$(MAKEDIR)\\..\n".
- "!endif\n".
- "\n".
- &def($makefile_extra{'borland'}->{'vars'}) .
- "\n".
- ".c.obj:\n".
- &splitline("\tbcc32 -w-aus -w-ccc -w-par -w-pia \$(COMPAT)".
- " \$(CFLAGS) \$(XFLAGS) ".
- (join " ", map {"-I$dirpfx$_"} @srcdirs) .
- " /c \$*.c",69)."\n".
- ".rc.res:\n".
- &splitline("\tbrcc32 \$(RCFL) -i \$(BCB)\\include -r".
- " \$(RCFLAGS) \$*.rc",69)."\n".
- "\n";
- print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
- print "\n\n";
- foreach $p (&prognames("G:C")) {
- ($prog, $type) = split ",", $p;
- $objstr = &objects($p, "X.obj", "X.res", undef);
- print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n";
- my $ap = ($type eq "G") ? "-aa" : "-ap";
- print "\tilink32 $ap -Gn -L\$(BCB)\\lib \@$prog.rsp\n\n";
- }
- foreach $p (&prognames("G:C")) {
- ($prog, $type) = split ",", $p;
- print $prog, ".rsp: \$(MAKEFILE)\n";
- $objstr = &objects($p, "X.obj", undef, undef);
- @objlist = split " ", $objstr;
- @objlines = ("");
- foreach $i (@objlist) {
- if (length($objlines[$#objlines] . " $i") > 50) {
- push @objlines, "";
- }
- $objlines[$#objlines] .= " $i";
- }
- $c0w = ($type eq "G") ? "c0w32" : "c0x32";
- print "\techo $c0w + > $prog.rsp\n";
- for ($i=0; $i<=$#objlines; $i++) {
- $plus = ($i < $#objlines ? " +" : "");
- print "\techo$objlines[$i]$plus >> $prog.rsp\n";
- }
- print "\techo $prog.exe >> $prog.rsp\n";
- $objstr = &objects($p, "X.obj", "X.res", undef);
- @libs = split " ", &objects($p, undef, undef, "X");
- @libs = grep { !$stdlibs{$_} } @libs;
- unshift @libs, "cw32", "import32";
- $libstr = join ' ', @libs;
- print "\techo nul,$libstr, >> $prog.rsp\n";
- print "\techo " . &objects($p, undef, "X.res", undef) . " >> $prog.rsp\n";
- print "\n";
- }
- foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\", "borland")) {
- if ($forceobj{$d->{obj_orig}}) {
- printf("%s: FORCE\n", $d->{obj});
- } else {
- print &splitline(sprintf("%s: %s", $d->{obj},
- join " ", @{$d->{deps}})), "\n";
- }
- }
- print "\n";
- print &def($makefile_extra{'borland'}->{'end'});
- print "\nclean:\n".
- "\t-del *.obj\n".
- "\t-del *.exe\n".
- "\t-del *.res\n".
- "\t-del *.pch\n".
- "\t-del *.aps\n".
- "\t-del *.il*\n".
- "\t-del *.pdb\n".
- "\t-del *.rsp\n".
- "\t-del *.tds\n".
- "\t-del *.\$\$\$\$\$\$\n".
- "\n".
- "FORCE:\n".
- "\t-rem dummy command\n";
- select STDOUT; close OUT;
-}
-
if (defined $makefiles{'vc'}) {
$dirpfx = &dirpfx($makefiles{'vc'}, "\\");
diff --git a/windows/rcstuff.h b/windows/rcstuff.h
index 22b22035..ee2c7696 100644
--- a/windows/rcstuff.h
+++ b/windows/rcstuff.h
@@ -9,7 +9,7 @@
#include
#else
-/* Some compilers, like Borland, don't have winresrc.h */
+/* Some compilers don't have winresrc.h */
#ifndef NO_WINRESRC_H
#ifndef MSVC4
#include
From ba4837dae819cffffe36fd6c0985d8df10d2873c Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 20 Sep 2017 18:04:37 +0100
Subject: [PATCH 069/607] Add a -restrict-putty-acl option to Windows Pageant.
This causes PuTTY processes spawned from its system-tray menu to run
with the -restrict-acl option (or rather, the synonymous &R prefix
used by my auto-constructed command lines for easier parsing).
The previous behaviour of Pageant was never to pass -restrict-acl to
PuTTY, even when started with -restrict-acl itself; this is not
actually a silly thing to want to do, because Pageant might well have
more need of -restrict-acl than PuTTY (it stores longer-term and more
powerful secrets) and conversely PuTTY might have more need to _not_
restrict its ACL than Pageant (in that among the things enabled by an
unrestricted ACL are various kinds of accessibility software, which is
more useful on the more user-facing PuTTY than on Pageant).
But for those who want to lock everything down with every security
option possible (even though -restrict-acl is only an ad-hoc
precaution and cannot deliver any hard guarantees), this new option
should fill in the UI gap.
---
doc/using.but | 12 ++++++++++++
windows/winpgnt.c | 26 ++++++++++++++++++++------
2 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/doc/using.but b/doc/using.but
index 7d184b7c..f5e3b57b 100644
--- a/doc/using.but
+++ b/doc/using.but
@@ -1042,3 +1042,15 @@ any processes started with Duplicate Session, New Session etc.
(However, if you're invoking PuTTY tools explicitly, for instance as a
proxy command, you'll need to arrange to pass them the
\c{-restrict-acl} option yourself, if that's what you want.)
+
+If Pageant is started with the \c{-restrict-acl} option, and you use
+it to launch a PuTTY session from its System Tray submenu, then
+Pageant will \e{not} default to starting the PuTTY subprocess with a
+restricted ACL. This is because PuTTY is more likely to suffer reduced
+functionality as a result of restricted ACLs (e.g. screen reader
+software will have a greater need to interact with it), whereas
+Pageant stores the more critical information (hence benefits more from
+the extra protection), so it's reasonable to want to run Pageant but
+not PuTTY with the ACL restrictions. You can force Pageant to start
+subsidiary PuTTY processes with a restricted ACL if you also pass the
+\c{-restrict-putty-acl} option.
diff --git a/windows/winpgnt.c b/windows/winpgnt.c
index ebb6c6ac..1919a9b8 100644
--- a/windows/winpgnt.c
+++ b/windows/winpgnt.c
@@ -57,6 +57,7 @@ static HMENU systray_menu, session_menu;
static int already_running;
static char *putty_path;
+static int restrict_putty_acl = FALSE;
/* CWD for "add key" file requester. */
static filereq *keypath = NULL;
@@ -847,11 +848,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
case WM_SYSCOMMAND:
switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
case IDM_PUTTY:
- if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
- SW_SHOW) <= 32) {
- MessageBox(NULL, "Unable to execute PuTTY!",
- "Error", MB_OK | MB_ICONERROR);
- }
+ {
+ TCHAR cmdline[10];
+ cmdline[0] = '\0';
+ if (restrict_putty_acl)
+ strcat(cmdline, "&R");
+
+ if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, cmdline,
+ _T(""), SW_SHOW) <= 32) {
+ MessageBox(NULL, "Unable to execute PuTTY!",
+ "Error", MB_OK | MB_ICONERROR);
+ }
+ }
break;
case IDM_CLOSE:
if (passphrase_box)
@@ -912,7 +920,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
mii.cch = MAX_PATH;
mii.dwTypeData = buf;
GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
- strcpy(param, "@");
+ param[0] = '\0';
+ if (restrict_putty_acl)
+ strcat(param, "&R");
+ strcat(param, "@");
strcat(param, mii.dwTypeData);
if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param,
_T(""), SW_SHOW) <= 32) {
@@ -1169,6 +1180,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
!strcmp(argv[i], "-restrict_acl") ||
!strcmp(argv[i], "-restrictacl")) {
restrict_process_acl();
+ } else if (!strcmp(argv[i], "-restrict-putty-acl") ||
+ !strcmp(argv[i], "-restrict_putty_acl")) {
+ restrict_putty_acl = TRUE;
} else if (!strcmp(argv[i], "-c")) {
/*
* If we see `-c', then the rest of the
From 581dd7071ea5131408420654808388ca7c42b3a2 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 30 Sep 2017 17:30:11 +0100
Subject: [PATCH 070/607] Squash the 256-colour test text into fewer lines.
I'm about to want to add more stuff to that file, and it would be nice
to have it still fit on a screen after I do.
---
testdata/colours.txt | 24 ++++++++----------------
1 file changed, 8 insertions(+), 16 deletions(-)
diff --git a/testdata/colours.txt b/testdata/colours.txt
index 33709d23..2311eb83 100644
--- a/testdata/colours.txt
+++ b/testdata/colours.txt
@@ -4,19 +4,11 @@ Normal text [1mand bold[m; [7mreverse video [1mand bold[m
ANSI plus bold: [30m0 [1mbold[m [31m1 [1mbold[m [32m2 [1mbold[m [33m3 [1mbold[m [34m4 [1mbold[m [35m5 [1mbold[m [36m6 [1mbold[m [37m7 [1mbold[m
xterm bright: [90mfg0[m [100mbg0[m [91mfg1[m [101mbg1[m [92mfg2[m [102mbg2[m [93mfg3[m [103mbg3[m [94mfg4[m [104mbg4[m [95mfg5[m [105mbg5[m [96mfg6[m [106mbg6[m [97mfg7[m [107mbg7[m
xterm 256: [48;5;16mg[48;5;232mr[48;5;233me[48;5;234my[48;5;235ms[48;5;236m [48;5;237m [48;5;238m [48;5;239m [48;5;240m [48;5;241m [48;5;242m [48;5;243m [48;5;244m [48;5;245m [48;5;246m [48;5;247m [48;5;248m [48;5;249m [48;5;250m [48;5;251m [48;5;252m [48;5;253m [48;5;254m [48;5;255m [48;5;231m [m [31;48;5;16mr[48;5;52me[48;5;88md[48;5;124ms[48;5;160m [48;5;196m [m [32;48;5;16mg[48;5;22mr[48;5;28me[48;5;34me[48;5;40mn[48;5;46ms[m [34;48;5;16mb[48;5;17ml[48;5;18mu[48;5;19me[48;5;20ms[48;5;21m [m [33;48;5;16my[48;5;58me[48;5;100ml[48;5;142ml[48;5;184mo[48;5;226mw[m [35;48;5;16mm[48;5;53ma[48;5;90mg[48;5;127me[48;5;164mn[48;5;201mt[m [36;48;5;16mc[48;5;23my[48;5;30ma[48;5;37mn[48;5;44ms[48;5;51m [m
-[38;5;0m 0[m[38;5;1m 1[m[38;5;2m 2[m[38;5;3m 3[m[38;5;4m 4[m[38;5;5m 5[m[38;5;6m 6[m[38;5;7m 7[m[38;5;8m 8[m[38;5;9m 9[m[38;5;10m 10[m[38;5;11m 11[m[38;5;12m 12[m[38;5;13m 13[m[38;5;14m 14[m[38;5;15m 15[m
-[38;5;16m 16[m[38;5;17m 17[m[38;5;18m 18[m[38;5;19m 19[m[38;5;20m 20[m[38;5;21m 21[m[38;5;22m 22[m[38;5;23m 23[m[38;5;24m 24[m[38;5;25m 25[m[38;5;26m 26[m[38;5;27m 27[m[38;5;28m 28[m[38;5;29m 29[m[38;5;30m 30[m[38;5;31m 31[m
-[38;5;32m 32[m[38;5;33m 33[m[38;5;34m 34[m[38;5;35m 35[m[38;5;36m 36[m[38;5;37m 37[m[38;5;38m 38[m[38;5;39m 39[m[38;5;40m 40[m[38;5;41m 41[m[38;5;42m 42[m[38;5;43m 43[m[38;5;44m 44[m[38;5;45m 45[m[38;5;46m 46[m[38;5;47m 47[m
-[38;5;48m 48[m[38;5;49m 49[m[38;5;50m 50[m[38;5;51m 51[m[38;5;52m 52[m[38;5;53m 53[m[38;5;54m 54[m[38;5;55m 55[m[38;5;56m 56[m[38;5;57m 57[m[38;5;58m 58[m[38;5;59m 59[m[38;5;60m 60[m[38;5;61m 61[m[38;5;62m 62[m[38;5;63m 63[m
-[38;5;64m 64[m[38;5;65m 65[m[38;5;66m 66[m[38;5;67m 67[m[38;5;68m 68[m[38;5;69m 69[m[38;5;70m 70[m[38;5;71m 71[m[38;5;72m 72[m[38;5;73m 73[m[38;5;74m 74[m[38;5;75m 75[m[38;5;76m 76[m[38;5;77m 77[m[38;5;78m 78[m[38;5;79m 79[m
-[38;5;80m 80[m[38;5;81m 81[m[38;5;82m 82[m[38;5;83m 83[m[38;5;84m 84[m[38;5;85m 85[m[38;5;86m 86[m[38;5;87m 87[m[38;5;88m 88[m[38;5;89m 89[m[38;5;90m 90[m[38;5;91m 91[m[38;5;92m 92[m[38;5;93m 93[m[38;5;94m 94[m[38;5;95m 95[m
-[38;5;96m 96[m[38;5;97m 97[m[38;5;98m 98[m[38;5;99m 99[m[38;5;100m 100[m[38;5;101m 101[m[38;5;102m 102[m[38;5;103m 103[m[38;5;104m 104[m[38;5;105m 105[m[38;5;106m 106[m[38;5;107m 107[m[38;5;108m 108[m[38;5;109m 109[m[38;5;110m 110[m[38;5;111m 111[m
-[38;5;112m 112[m[38;5;113m 113[m[38;5;114m 114[m[38;5;115m 115[m[38;5;116m 116[m[38;5;117m 117[m[38;5;118m 118[m[38;5;119m 119[m[38;5;120m 120[m[38;5;121m 121[m[38;5;122m 122[m[38;5;123m 123[m[38;5;124m 124[m[38;5;125m 125[m[38;5;126m 126[m[38;5;127m 127[m
-[38;5;128m 128[m[38;5;129m 129[m[38;5;130m 130[m[38;5;131m 131[m[38;5;132m 132[m[38;5;133m 133[m[38;5;134m 134[m[38;5;135m 135[m[38;5;136m 136[m[38;5;137m 137[m[38;5;138m 138[m[38;5;139m 139[m[38;5;140m 140[m[38;5;141m 141[m[38;5;142m 142[m[38;5;143m 143[m
-[38;5;144m 144[m[38;5;145m 145[m[38;5;146m 146[m[38;5;147m 147[m[38;5;148m 148[m[38;5;149m 149[m[38;5;150m 150[m[38;5;151m 151[m[38;5;152m 152[m[38;5;153m 153[m[38;5;154m 154[m[38;5;155m 155[m[38;5;156m 156[m[38;5;157m 157[m[38;5;158m 158[m[38;5;159m 159[m
-[38;5;160m 160[m[38;5;161m 161[m[38;5;162m 162[m[38;5;163m 163[m[38;5;164m 164[m[38;5;165m 165[m[38;5;166m 166[m[38;5;167m 167[m[38;5;168m 168[m[38;5;169m 169[m[38;5;170m 170[m[38;5;171m 171[m[38;5;172m 172[m[38;5;173m 173[m[38;5;174m 174[m[38;5;175m 175[m
-[38;5;176m 176[m[38;5;177m 177[m[38;5;178m 178[m[38;5;179m 179[m[38;5;180m 180[m[38;5;181m 181[m[38;5;182m 182[m[38;5;183m 183[m[38;5;184m 184[m[38;5;185m 185[m[38;5;186m 186[m[38;5;187m 187[m[38;5;188m 188[m[38;5;189m 189[m[38;5;190m 190[m[38;5;191m 191[m
-[38;5;192m 192[m[38;5;193m 193[m[38;5;194m 194[m[38;5;195m 195[m[38;5;196m 196[m[38;5;197m 197[m[38;5;198m 198[m[38;5;199m 199[m[38;5;200m 200[m[38;5;201m 201[m[38;5;202m 202[m[38;5;203m 203[m[38;5;204m 204[m[38;5;205m 205[m[38;5;206m 206[m[38;5;207m 207[m
-[38;5;208m 208[m[38;5;209m 209[m[38;5;210m 210[m[38;5;211m 211[m[38;5;212m 212[m[38;5;213m 213[m[38;5;214m 214[m[38;5;215m 215[m[38;5;216m 216[m[38;5;217m 217[m[38;5;218m 218[m[38;5;219m 219[m[38;5;220m 220[m[38;5;221m 221[m[38;5;222m 222[m[38;5;223m 223[m
-[38;5;224m 224[m[38;5;225m 225[m[38;5;226m 226[m[38;5;227m 227[m[38;5;228m 228[m[38;5;229m 229[m[38;5;230m 230[m[38;5;231m 231[m[38;5;232m 232[m[38;5;233m 233[m[38;5;234m 234[m[38;5;235m 235[m[38;5;236m 236[m[38;5;237m 237[m[38;5;238m 238[m[38;5;239m 239[m
-[38;5;240m 240[m[38;5;241m 241[m[38;5;242m 242[m[38;5;243m 243[m[38;5;244m 244[m[38;5;245m 245[m[38;5;246m 246[m[38;5;247m 247[m[38;5;248m 248[m[38;5;249m 249[m[38;5;250m 250[m[38;5;251m 251[m[38;5;252m 252[m[38;5;253m 253[m[38;5;254m 254[m[38;5;255m 255[m
+[38;5;0m00[38;5;1m01[38;5;2m02[38;5;3m03[38;5;4m04[38;5;5m05[38;5;6m06[38;5;7m07 [38;5;8m08[38;5;9m09[38;5;10m0a[38;5;11m0b[38;5;12m0c[38;5;13m0d[38;5;14m0e[38;5;15m0f [38;5;16m10[38;5;17m11[38;5;18m12[38;5;19m13[38;5;20m14[38;5;21m15[38;5;22m16[38;5;23m17 [38;5;24m18[38;5;25m19[38;5;26m1a[38;5;27m1b[38;5;28m1c[38;5;29m1d[38;5;30m1e[38;5;31m1f[m
+[38;5;32m20[38;5;33m21[38;5;34m22[38;5;35m23[38;5;36m24[38;5;37m25[38;5;38m26[38;5;39m27 [38;5;40m28[38;5;41m29[38;5;42m2a[38;5;43m2b[38;5;44m2c[38;5;45m2d[38;5;46m2e[38;5;47m2f [38;5;48m30[38;5;49m31[38;5;50m32[38;5;51m33[38;5;52m34[38;5;53m35[38;5;54m36[38;5;55m37 [38;5;56m38[38;5;57m39[38;5;58m3a[38;5;59m3b[38;5;60m3c[38;5;61m3d[38;5;62m3e[38;5;63m3f[m
+[38;5;64m40[38;5;65m41[38;5;66m42[38;5;67m43[38;5;68m44[38;5;69m45[38;5;70m46[38;5;71m47 [38;5;72m48[38;5;73m49[38;5;74m4a[38;5;75m4b[38;5;76m4c[38;5;77m4d[38;5;78m4e[38;5;79m4f [38;5;80m50[38;5;81m51[38;5;82m52[38;5;83m53[38;5;84m54[38;5;85m55[38;5;86m56[38;5;87m57 [38;5;88m58[38;5;89m59[38;5;90m5a[38;5;91m5b[38;5;92m5c[38;5;93m5d[38;5;94m5e[38;5;95m5f[m
+[38;5;96m60[38;5;97m61[38;5;98m62[38;5;99m63[38;5;100m64[38;5;101m65[38;5;102m66[38;5;103m67 [38;5;104m68[38;5;105m69[38;5;106m6a[38;5;107m6b[38;5;108m6c[38;5;109m6d[38;5;110m6e[38;5;111m6f [38;5;112m70[38;5;113m71[38;5;114m72[38;5;115m73[38;5;116m74[38;5;117m75[38;5;118m76[38;5;119m77 [38;5;120m78[38;5;121m79[38;5;122m7a[38;5;123m7b[38;5;124m7c[38;5;125m7d[38;5;126m7e[38;5;127m7f[m
+[38;5;128m80[38;5;129m81[38;5;130m82[38;5;131m83[38;5;132m84[38;5;133m85[38;5;134m86[38;5;135m87 [38;5;136m88[38;5;137m89[38;5;138m8a[38;5;139m8b[38;5;140m8c[38;5;141m8d[38;5;142m8e[38;5;143m8f [38;5;144m90[38;5;145m91[38;5;146m92[38;5;147m93[38;5;148m94[38;5;149m95[38;5;150m96[38;5;151m97 [38;5;152m98[38;5;153m99[38;5;154m9a[38;5;155m9b[38;5;156m9c[38;5;157m9d[38;5;158m9e[38;5;159m9f[m
+[38;5;160ma0[38;5;161ma1[38;5;162ma2[38;5;163ma3[38;5;164ma4[38;5;165ma5[38;5;166ma6[38;5;167ma7 [38;5;168ma8[38;5;169ma9[38;5;170maa[38;5;171mab[38;5;172mac[38;5;173mad[38;5;174mae[38;5;175maf [38;5;176mb0[38;5;177mb1[38;5;178mb2[38;5;179mb3[38;5;180mb4[38;5;181mb5[38;5;182mb6[38;5;183mb7 [38;5;184mb8[38;5;185mb9[38;5;186mba[38;5;187mbb[38;5;188mbc[38;5;189mbd[38;5;190mbe[38;5;191mbf[m
+[38;5;192mc0[38;5;193mc1[38;5;194mc2[38;5;195mc3[38;5;196mc4[38;5;197mc5[38;5;198mc6[38;5;199mc7 [38;5;200mc8[38;5;201mc9[38;5;202mca[38;5;203mcb[38;5;204mcc[38;5;205mcd[38;5;206mce[38;5;207mcf [38;5;208md0[38;5;209md1[38;5;210md2[38;5;211md3[38;5;212md4[38;5;213md5[38;5;214md6[38;5;215md7 [38;5;216md8[38;5;217md9[38;5;218mda[38;5;219mdb[38;5;220mdc[38;5;221mdd[38;5;222mde[38;5;223mdf[m
+[38;5;224me0[38;5;225me1[38;5;226me2[38;5;227me3[38;5;228me4[38;5;229me5[38;5;230me6[38;5;231me7 [38;5;232me8[38;5;233me9[38;5;234mea[38;5;235meb[38;5;236mec[38;5;237med[38;5;238mee[38;5;239mef [38;5;240mf0[38;5;241mf1[38;5;242mf2[38;5;243mf3[38;5;244mf4[38;5;245mf5[38;5;246mf6[38;5;247mf7 [38;5;248mf8[38;5;249mf9[38;5;250mfa[38;5;251mfb[38;5;252mfc[38;5;253mfd[38;5;254mfe[38;5;255mff[m
From a4cbd3dfdb71d258e83bbf5b03a874c06d0b3106 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 30 Sep 2017 17:32:32 +0100
Subject: [PATCH 071/607] Support ESC[38;2;R;G;Bm for 24-bit true colour.
This is a heavily rewritten version of a patch originally by Lorenz
Diener; it was tidied up somewhat by Christian Brabandt, and then
tidied up more by me. The basic idea is to add to the termchar
structure a pair of small structs encoding 24-bit RGB values, each
with a flag indicating whether it's turned on; if it is, it overrides
any other specification of fg or bg colour for that character cell.
I've added a test line to colours.txt containing a few example colours
from /usr/share/X11/rgb.txt. In fact it makes quite a good demo to run
the whole of rgb.txt through this treatment, with a command such as
perl -pe 's!^\s*(\d+)\s+(\d+)\s+(\d+).*$!\e[38;2;$1;$2;$3m$&\e[m!' rgb.txt
---
LICENCE | 3 +-
fuzzterm.c | 4 +-
putty.h | 28 +++++++++-
terminal.c | 129 ++++++++++++++++++++++++++++++++++++++++---
terminal.h | 2 +
testdata/colours.txt | 1 +
unix/gtkwin.c | 38 ++++++++++---
windows/window.c | 33 ++++++-----
8 files changed, 204 insertions(+), 34 deletions(-)
diff --git a/LICENCE b/LICENCE
index 7c49ceb3..30b1fe2b 100644
--- a/LICENCE
+++ b/LICENCE
@@ -3,7 +3,8 @@ PuTTY is copyright 1997-2017 Simon Tatham.
Portions copyright Robert de Bath, Joris van Rantwijk, Delian
Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
-Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.
+Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian
+Brabandt, and CORE SDI S.A.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
diff --git a/fuzzterm.c b/fuzzterm.c
index 15b5d635..480dca37 100644
--- a/fuzzterm.c
+++ b/fuzzterm.c
@@ -45,7 +45,7 @@ int from_backend(void *frontend, int is_stderr, const char *data, int len)
void request_resize(void *frontend, int x, int y) { }
void do_text(Context ctx, int x, int y, wchar_t * text, int len,
- unsigned long attr, int lattr)
+ unsigned long attr, int lattr, truecolour tc)
{
int i;
@@ -56,7 +56,7 @@ void do_text(Context ctx, int x, int y, wchar_t * text, int len,
printf("\n");
}
void do_cursor(Context ctx, int x, int y, wchar_t * text, int len,
- unsigned long attr, int lattr)
+ unsigned long attr, int lattr, truecolour tc)
{
int i;
diff --git a/putty.h b/putty.h
index fd2d0250..61d6278d 100644
--- a/putty.h
+++ b/putty.h
@@ -592,12 +592,36 @@ void prompt_ensure_result_size(prompt_t *pr, int len);
/* Burn the evidence. (Assumes _all_ strings want free()ing.) */
void free_prompts(prompts_t *p);
+/*
+ * Data type definitions for true-colour terminal display.
+ * 'optionalrgb' describes a single RGB colour, which overrides the
+ * other colour settings if 'enabled' is nonzero, and is ignored
+ * otherwise. 'truecolour' contains a pair of those for foreground and
+ * background.
+ */
+typedef struct optionalrgb {
+ unsigned char enabled;
+ unsigned char r, g, b;
+} optionalrgb;
+extern const optionalrgb optionalrgb_none;
+typedef struct truecolour {
+ optionalrgb fg, bg;
+} truecolour;
+#define optionalrgb_equal(r1,r2) ( \
+ (r1).enabled==(r2).enabled && \
+ (r1).r==(r2).r && (r1).g==(r2).g && (r1).b==(r2).b)
+#define truecolour_equal(c1,c2) ( \
+ optionalrgb_equal((c1).fg, (c2).fg) && \
+ optionalrgb_equal((c1).bg, (c2).bg))
+
/*
* Exports from the front end.
*/
void request_resize(void *frontend, int, int);
-void do_text(Context, int, int, wchar_t *, int, unsigned long, int);
-void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int);
+void do_text(Context, int, int, wchar_t *, int, unsigned long, int,
+ truecolour);
+void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int,
+ truecolour);
int char_width(Context ctx, int uc);
#ifdef OPTIMISE_SCROLL
void do_scroll(Context, int, int, int);
diff --git a/terminal.c b/terminal.c
index f7bcbb95..e597d472 100644
--- a/terminal.c
+++ b/terminal.c
@@ -108,6 +108,7 @@ static void update_sbar(Terminal *);
static void deselect(Terminal *);
static void term_print_finish(Terminal *);
static void scroll(Terminal *, int, int, int, int);
+static void parse_optionalrgb(optionalrgb *out, unsigned *values);
#ifdef OPTIMISE_SCROLL
static void scroll_display(Terminal *, int, int, int);
#endif /* OPTIMISE_SCROLL */
@@ -283,6 +284,8 @@ static int termchars_equal_override(termchar *a, termchar *b,
unsigned long bchr, unsigned long battr)
{
/* FULL-TERMCHAR */
+ if (!truecolour_equal(a->truecolour, b->truecolour))
+ return FALSE;
if (a->chr != bchr)
return FALSE;
if ((a->attr &~ DATTR_MASK) != (battr &~ DATTR_MASK))
@@ -607,6 +610,24 @@ static void makeliteral_attr(struct buf *b, termchar *c, unsigned long *state)
add(b, (unsigned char)(attr & 0xFF));
}
}
+static void makeliteral_truecolour(struct buf *b, termchar *c, unsigned long *state)
+{
+ /*
+ * Put the used parts of the colour info into the buffer.
+ */
+ add(b, ((c->truecolour.fg.enabled ? 1 : 0) |
+ (c->truecolour.bg.enabled ? 2 : 0)));
+ if (c->truecolour.fg.enabled) {
+ add(b, c->truecolour.fg.r);
+ add(b, c->truecolour.fg.g);
+ add(b, c->truecolour.fg.b);
+ }
+ if (c->truecolour.bg.enabled) {
+ add(b, c->truecolour.bg.r);
+ add(b, c->truecolour.bg.g);
+ add(b, c->truecolour.bg.b);
+ }
+}
static void makeliteral_cc(struct buf *b, termchar *c, unsigned long *state)
{
/*
@@ -681,6 +702,7 @@ static unsigned char *compressline(termline *ldata)
*/
makerle(b, ldata, makeliteral_chr);
makerle(b, ldata, makeliteral_attr);
+ makerle(b, ldata, makeliteral_truecolour);
makerle(b, ldata, makeliteral_cc);
/*
@@ -826,6 +848,29 @@ static void readliteral_attr(struct buf *b, termchar *c, termline *ldata,
c->attr = attr;
}
+static void readliteral_truecolour(struct buf *b, termchar *c, termline *ldata,
+ unsigned long *state)
+{
+ int flags = get(b);
+
+ if (flags & 1) {
+ c->truecolour.fg.enabled = TRUE;
+ c->truecolour.fg.r = get(b);
+ c->truecolour.fg.g = get(b);
+ c->truecolour.fg.b = get(b);
+ } else {
+ c->truecolour.fg = optionalrgb_none;
+ }
+
+ if (flags & 2) {
+ c->truecolour.bg.enabled = TRUE;
+ c->truecolour.bg.r = get(b);
+ c->truecolour.bg.g = get(b);
+ c->truecolour.bg.b = get(b);
+ } else {
+ c->truecolour.bg = optionalrgb_none;
+ }
+}
static void readliteral_cc(struct buf *b, termchar *c, termline *ldata,
unsigned long *state)
{
@@ -899,6 +944,7 @@ static termline *decompressline(unsigned char *data, int *bytes_used)
*/
readrle(b, ldata, readliteral_chr);
readrle(b, ldata, readliteral_attr);
+ readrle(b, ldata, readliteral_truecolour);
readrle(b, ldata, readliteral_cc);
/* Return the number of bytes read, for diagnostic purposes. */
@@ -1570,6 +1616,8 @@ void term_clrsb(Terminal *term)
update_sbar(term);
}
+const optionalrgb optionalrgb_none = {0, 0, 0, 0};
+
/*
* Initialise the terminal.
*/
@@ -1646,6 +1694,8 @@ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata,
term->basic_erase_char.chr = CSET_ASCII | ' ';
term->basic_erase_char.attr = ATTR_DEFAULT;
term->basic_erase_char.cc_next = 0;
+ term->basic_erase_char.truecolour.fg = optionalrgb_none;
+ term->basic_erase_char.truecolour.bg = optionalrgb_none;
term->erase_char = term->basic_erase_char;
return term;
@@ -3212,6 +3262,8 @@ static void term_out(Terminal *term)
clear_cc(cline, term->curs.x);
cline->chars[term->curs.x].chr = c;
cline->chars[term->curs.x].attr = term->curr_attr;
+ cline->chars[term->curs.x].truecolour =
+ term->curr_truecolour;
term->curs.x++;
@@ -3219,6 +3271,8 @@ static void term_out(Terminal *term)
clear_cc(cline, term->curs.x);
cline->chars[term->curs.x].chr = UCSWIDE;
cline->chars[term->curs.x].attr = term->curr_attr;
+ cline->chars[term->curs.x].truecolour =
+ term->curr_truecolour;
break;
case 1:
@@ -3229,6 +3283,8 @@ static void term_out(Terminal *term)
clear_cc(cline, term->curs.x);
cline->chars[term->curs.x].chr = c;
cline->chars[term->curs.x].attr = term->curr_attr;
+ cline->chars[term->curs.x].truecolour =
+ term->curr_truecolour;
break;
case 0:
@@ -3799,6 +3855,8 @@ static void term_out(Terminal *term)
switch (def(term->esc_args[i], 0)) {
case 0: /* restore defaults */
term->curr_attr = term->default_attr;
+ term->curr_truecolour =
+ term->basic_erase_char.truecolour;
break;
case 1: /* enable bold */
compatibility(VT100AVO);
@@ -3860,6 +3918,7 @@ static void term_out(Terminal *term)
case 36:
case 37:
/* foreground */
+ term->curr_truecolour.fg.enabled = FALSE;
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |=
(term->esc_args[i] - 30)<curr_truecolour.fg.enabled = FALSE;
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |=
((term->esc_args[i] - 90 + 8)
<< ATTR_FGSHIFT);
break;
case 39: /* default-foreground */
+ term->curr_truecolour.fg.enabled = FALSE;
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |= ATTR_DEFFG;
break;
@@ -3891,6 +3952,7 @@ static void term_out(Terminal *term)
case 46:
case 47:
/* background */
+ term->curr_truecolour.bg.enabled = FALSE;
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |=
(term->esc_args[i] - 40)<curr_truecolour.bg.enabled = FALSE;
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |=
((term->esc_args[i] - 100 + 8)
<< ATTR_BGSHIFT);
break;
case 49: /* default-background */
+ term->curr_truecolour.bg.enabled = FALSE;
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |= ATTR_DEFBG;
break;
- case 38: /* xterm 256-colour mode */
+
+ /*
+ * 256-colour and true-colour
+ * sequences. A 256-colour
+ * foreground is selected by a
+ * sequence of 3 arguments in the
+ * form 38;5;n, where n is in the
+ * range 0-255. A true-colour RGB
+ * triple is selected by 5 args of
+ * the form 38;2;r;g;b. Replacing
+ * the initial 38 with 48 in both
+ * cases selects the same colour
+ * as the background.
+ */
+ case 38:
if (i+2 < term->esc_nargs &&
term->esc_args[i+1] == 5) {
term->curr_attr &= ~ATTR_FGMASK;
@@ -3921,9 +3999,16 @@ static void term_out(Terminal *term)
((term->esc_args[i+2] & 0xFF)
<< ATTR_FGSHIFT);
i += 2;
+ }
+ if (i + 4 < term->esc_nargs &&
+ term->esc_args[i + 1] == 2) {
+ parse_optionalrgb(
+ &term->curr_truecolour.fg,
+ term->esc_args + (i+2));
+ i += 4;
}
break;
- case 48: /* xterm 256-colour mode */
+ case 48:
if (i+2 < term->esc_nargs &&
term->esc_args[i+1] == 5) {
term->curr_attr &= ~ATTR_BGMASK;
@@ -3932,6 +4017,13 @@ static void term_out(Terminal *term)
<< ATTR_BGSHIFT);
i += 2;
}
+ if (i + 4 < term->esc_nargs &&
+ term->esc_args[i+1] == 2) {
+ parse_optionalrgb(
+ &term->curr_truecolour.bg,
+ term->esc_args + (i+2));
+ i += 4;
+ }
break;
}
}
@@ -4733,6 +4825,19 @@ static void term_out(Terminal *term)
logflush(term->logctx);
}
+/*
+ * Small subroutine to parse three consecutive escape-sequence
+ * arguments representing a true-colour RGB triple into an
+ * optionalrgb.
+ */
+static void parse_optionalrgb(optionalrgb *out, unsigned *values)
+{
+ out->enabled = TRUE;
+ out->r = values[0] < 256 ? values[0] : 0;
+ out->g = values[1] < 256 ? values[1] : 0;
+ out->b = values[2] < 256 ? values[2] : 0;
+}
+
/*
* To prevent having to run the reasonably tricky bidi algorithm
* too many times, we maintain a cache of the last lineful of data
@@ -5035,6 +5140,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
int last_run_dirty = 0;
int laststart, dirtyrect;
int *backward;
+ truecolour tc;
scrpos.y = i + term->disptop;
ldata = lineptr(scrpos.y);
@@ -5064,6 +5170,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) |
ATTR_DEFFG | ATTR_DEFBG;
+ tc = d->truecolour;
if (!term->xterm_256_colour) {
int colour;
colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT;
@@ -5131,6 +5238,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
/* FULL-TERMCHAR */
newline[j].attr = tattr;
newline[j].chr = tchar;
+ newline[j].truecolour = tc;
/* Combining characters are still read from lchars */
newline[j].cc_next = 0;
}
@@ -5181,6 +5289,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
term->disptext[i]->lattr);
term->disptext[i]->lattr = ldata->lattr;
+ tc = term->erase_char.truecolour;
for (j = 0; j < term->cols; j++) {
unsigned long tattr, tchar;
int break_run, do_copy;
@@ -5194,6 +5303,9 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
break_run = ((tattr ^ attr) & term->attr_mask) != 0;
+ if (!truecolour_equal(newline[j].truecolour, tc))
+ break_run = TRUE;
+
#ifdef USES_VTLINE_HACK
/* Special hack for VT100 Linedraw glyphs */
if ((tchar >= 0x23BA && tchar <= 0x23BD) ||
@@ -5226,15 +5338,15 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
if (break_run) {
if ((dirty_run || last_run_dirty) && ccount > 0) {
- do_text(ctx, start, i, ch, ccount, attr,
- ldata->lattr);
+ do_text(ctx, start, i, ch, ccount, attr, ldata->lattr, tc);
if (attr & (TATTR_ACTCURS | TATTR_PASCURS))
do_cursor(ctx, start, i, ch, ccount, attr,
- ldata->lattr);
+ ldata->lattr, tc);
}
start = j;
ccount = 0;
attr = tattr;
+ tc = newline[j].truecolour;
cset = CSET_OF(tchar);
if (term->ucsdata->dbcs_screenfont)
last_run_dirty = dirty_run;
@@ -5303,6 +5415,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
copy_termchar(term->disptext[i], j, d);
term->disptext[i]->chars[j].chr = tchar;
term->disptext[i]->chars[j].attr = tattr;
+ term->disptext[i]->chars[j].truecolour = tc;
if (start == j)
term->disptext[i]->chars[j].attr |= DATTR_STARTRUN;
}
@@ -5324,11 +5437,9 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
}
}
if (dirty_run && ccount > 0) {
- do_text(ctx, start, i, ch, ccount, attr,
- ldata->lattr);
+ do_text(ctx, start, i, ch, ccount, attr, ldata->lattr, tc);
if (attr & (TATTR_ACTCURS | TATTR_PASCURS))
- do_cursor(ctx, start, i, ch, ccount, attr,
- ldata->lattr);
+ do_cursor(ctx, start, i, ch, ccount, attr, ldata->lattr, tc);
}
unlineptr(ldata);
diff --git a/terminal.h b/terminal.h
index 2ed9e6ef..21b8774a 100644
--- a/terminal.h
+++ b/terminal.h
@@ -40,6 +40,7 @@ struct termchar {
*/
unsigned long chr;
unsigned long attr;
+ truecolour truecolour;
/*
* The cc_next field is used to link multiple termchars
@@ -102,6 +103,7 @@ struct terminal_tag {
#endif /* OPTIMISE_SCROLL */
int default_attr, curr_attr, save_attr;
+ truecolour curr_truecolour;
termchar basic_erase_char, erase_char;
bufchain inbuf; /* terminal input buffer */
diff --git a/testdata/colours.txt b/testdata/colours.txt
index 2311eb83..34dff8a5 100644
--- a/testdata/colours.txt
+++ b/testdata/colours.txt
@@ -12,3 +12,4 @@ xterm 256: [48;5;16mg[48;5;232mr[48;5;233me[48;5;234my[48;5;235ms[48;5;236
[38;5;160ma0[38;5;161ma1[38;5;162ma2[38;5;163ma3[38;5;164ma4[38;5;165ma5[38;5;166ma6[38;5;167ma7 [38;5;168ma8[38;5;169ma9[38;5;170maa[38;5;171mab[38;5;172mac[38;5;173mad[38;5;174mae[38;5;175maf [38;5;176mb0[38;5;177mb1[38;5;178mb2[38;5;179mb3[38;5;180mb4[38;5;181mb5[38;5;182mb6[38;5;183mb7 [38;5;184mb8[38;5;185mb9[38;5;186mba[38;5;187mbb[38;5;188mbc[38;5;189mbd[38;5;190mbe[38;5;191mbf[m
[38;5;192mc0[38;5;193mc1[38;5;194mc2[38;5;195mc3[38;5;196mc4[38;5;197mc5[38;5;198mc6[38;5;199mc7 [38;5;200mc8[38;5;201mc9[38;5;202mca[38;5;203mcb[38;5;204mcc[38;5;205mcd[38;5;206mce[38;5;207mcf [38;5;208md0[38;5;209md1[38;5;210md2[38;5;211md3[38;5;212md4[38;5;213md5[38;5;214md6[38;5;215md7 [38;5;216md8[38;5;217md9[38;5;218mda[38;5;219mdb[38;5;220mdc[38;5;221mdd[38;5;222mde[38;5;223mdf[m
[38;5;224me0[38;5;225me1[38;5;226me2[38;5;227me3[38;5;228me4[38;5;229me5[38;5;230me6[38;5;231me7 [38;5;232me8[38;5;233me9[38;5;234mea[38;5;235meb[38;5;236mec[38;5;237med[38;5;238mee[38;5;239mef [38;5;240mf0[38;5;241mf1[38;5;242mf2[38;5;243mf3[38;5;244mf4[38;5;245mf5[38;5;246mf6[38;5;247mf7 [38;5;248mf8[38;5;249mf9[38;5;250mfa[38;5;251mfb[38;5;252mfc[38;5;253mfd[38;5;254mfe[38;5;255mff[m
+24-bit colour: [38;2;112;128;144mSlateGrey[m [38;2;107;142;35mOliveDrab[m [38;2;218;165;32mgoldenrod[m [38;2;139;69;19mSaddleBrown[m [30;48;2;148;0;211mDarkViolet (bg)[m
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 9e73181e..f49f92af 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -3029,6 +3029,24 @@ static void draw_set_colour(struct draw_ctx *dctx, int col)
#endif
}
+static void draw_set_colour_rgb(struct draw_ctx *dctx, optionalrgb orgb)
+{
+#ifdef DRAW_TEXT_GDK
+ if (dctx->uctx.type == DRAWTYPE_GDK) {
+ GdkColor color;
+ color.red = orgb.r * 256;
+ color.green = orgb.g * 256;
+ color.blue = orgb.b * 256;
+ gdk_gc_set_rgb_fg_color(dctx->uctx.u.gdk.gc, &color);
+ }
+#endif
+#ifdef DRAW_TEXT_CAIRO
+ if (dctx->uctx.type == DRAWTYPE_CAIRO)
+ cairo_set_source_rgb(dctx->uctx.u.cairo.cr,
+ orgb.r / 255.0, orgb.g / 255.0, orgb.b / 255.0);
+#endif
+}
+
static void draw_rectangle(struct draw_ctx *dctx, int filled,
int x, int y, int w, int h)
{
@@ -3222,7 +3240,7 @@ static void draw_backing_rect(struct gui_data *inst)
* We are allowed to fiddle with the contents of `text'.
*/
void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
- unsigned long attr, int lattr)
+ unsigned long attr, int lattr, truecolour truecolour)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
struct gui_data *inst = dctx->inst;
@@ -3316,13 +3334,19 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
((lattr & LATTR_MODE) == LATTR_BOT));
}
- draw_set_colour(dctx, nbg);
+ if (truecolour.bg.enabled)
+ draw_set_colour_rgb(dctx, truecolour.bg);
+ else
+ draw_set_colour(dctx, nbg);
draw_rectangle(dctx, TRUE,
x*inst->font_width+inst->window_border,
y*inst->font_height+inst->window_border,
rlen*widefactor*inst->font_width, inst->font_height);
- draw_set_colour(dctx, nfg);
+ if (truecolour.fg.enabled)
+ draw_set_colour_rgb(dctx, truecolour.fg);
+ else
+ draw_set_colour(dctx, nfg);
if (ncombining > 1) {
assert(len == 1);
unifont_draw_combining(&dctx->uctx, inst->fonts[fontid],
@@ -3362,13 +3386,13 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
}
void do_text(Context ctx, int x, int y, wchar_t *text, int len,
- unsigned long attr, int lattr)
+ unsigned long attr, int lattr, truecolour truecolour)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
struct gui_data *inst = dctx->inst;
int widefactor;
- do_text_internal(ctx, x, y, text, len, attr, lattr);
+ do_text_internal(ctx, x, y, text, len, attr, lattr, truecolour);
if (attr & ATTR_WIDE) {
widefactor = 2;
@@ -3392,7 +3416,7 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
}
void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
- unsigned long attr, int lattr)
+ unsigned long attr, int lattr, truecolour truecolour)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
struct gui_data *inst = dctx->inst;
@@ -3409,7 +3433,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
active = 1;
} else
active = 0;
- do_text_internal(ctx, x, y, text, len, attr, lattr);
+ do_text_internal(ctx, x, y, text, len, attr, lattr, truecolour);
if (attr & TATTR_COMBINING)
len = 1;
diff --git a/windows/window.c b/windows/window.c
index 96600311..452eb771 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -3395,7 +3395,7 @@ static void sys_cursor_update(void)
* We are allowed to fiddle with the contents of `text'.
*/
void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
- unsigned long attr, int lattr)
+ unsigned long attr, int lattr, truecolour truecolour)
{
COLORREF fg, bg, t;
int nfg, nbg, nfont;
@@ -3522,8 +3522,16 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
if (nbg < 16) nbg |= 8;
else if (nbg >= 256) nbg |= 1;
}
- fg = colours[nfg];
- bg = colours[nbg];
+ if (truecolour.fg.enabled)
+ fg = RGB(truecolour.fg.r, truecolour.fg.g, truecolour.fg.b);
+ else
+ fg = colours[nfg];
+
+ if (truecolour.bg.enabled)
+ bg = RGB(truecolour.bg.r, truecolour.bg.g, truecolour.bg.b);
+ else
+ bg = colours[nbg];
+
SelectObject(hdc, fonts[nfont]);
SetTextColor(hdc, fg);
SetBkColor(hdc, bg);
@@ -3768,7 +3776,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
* Wrapper that handles combining characters.
*/
void do_text(Context ctx, int x, int y, wchar_t *text, int len,
- unsigned long attr, int lattr)
+ unsigned long attr, int lattr, truecolour truecolour)
{
if (attr & TATTR_COMBINING) {
unsigned long a = 0;
@@ -3778,13 +3786,13 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
len0 = 2;
if (len-len0 >= 1 && IS_LOW_VARSEL(text[len0])) {
attr &= ~TATTR_COMBINING;
- do_text_internal(ctx, x, y, text, len0+1, attr, lattr);
+ do_text_internal(ctx, x, y, text, len0+1, attr, lattr, truecolour);
text += len0+1;
len -= len0+1;
a = TATTR_COMBINING;
} else if (len-len0 >= 2 && IS_HIGH_VARSEL(text[len0], text[len0+1])) {
attr &= ~TATTR_COMBINING;
- do_text_internal(ctx, x, y, text, len0+2, attr, lattr);
+ do_text_internal(ctx, x, y, text, len0+2, attr, lattr, truecolour);
text += len0+2;
len -= len0+2;
a = TATTR_COMBINING;
@@ -3794,22 +3802,21 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
while (len--) {
if (len >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) {
- do_text_internal(ctx, x, y, text, 2, attr | a, lattr);
+ do_text_internal(ctx, x, y, text, 2, attr | a, lattr, truecolour);
len--;
text++;
- } else {
- do_text_internal(ctx, x, y, text, 1, attr | a, lattr);
- }
+ } else
+ do_text_internal(ctx, x, y, text, 1, attr | a, lattr, truecolour);
text++;
a = TATTR_COMBINING;
}
} else
- do_text_internal(ctx, x, y, text, len, attr, lattr);
+ do_text_internal(ctx, x, y, text, len, attr, lattr, truecolour);
}
void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
- unsigned long attr, int lattr)
+ unsigned long attr, int lattr, truecolour truecolour)
{
int fnt_width;
@@ -3821,7 +3828,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
if (*text != UCSWIDE) {
- do_text(ctx, x, y, text, len, attr, lattr);
+ do_text(ctx, x, y, text, len, attr, lattr, truecolour);
return;
}
ctype = 2;
From 16214ea0f5de7023c7e08fba57e7a49c2d518583 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 1 Oct 2017 20:59:00 +0100
Subject: [PATCH 072/607] Initialise term->curr_truecolour at startup.
Somehow I managed to miss _that_ really obvious bug in the true-
colour patch.
---
terminal.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/terminal.c b/terminal.c
index e597d472..0eb1a9a6 100644
--- a/terminal.c
+++ b/terminal.c
@@ -1296,6 +1296,7 @@ static void power_on(Terminal *term, int clear)
term->big_cursor = 0;
term->default_attr = term->save_attr =
term->alt_save_attr = term->curr_attr = ATTR_DEFAULT;
+ term->curr_truecolour.fg = term->curr_truecolour.bg = optionalrgb_none;
term->term_editing = term->term_echoing = FALSE;
term->app_cursor_keys = conf_get_int(term->conf, CONF_app_cursor);
term->app_keypad_keys = conf_get_int(term->conf, CONF_app_keypad);
From f813e9f937b5b6198659c8c64b3090dc3902b930 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 1 Oct 2017 21:05:25 +0100
Subject: [PATCH 073/607] uxnet.c: don't close a socket's fd if it is -1.
This is harmless in principle (you just get EBADF back from close(2)
and ignore it), but it leads to warnings in valgrind.
---
unix/uxnet.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/unix/uxnet.c b/unix/uxnet.c
index ddcd9228..f3498527 100644
--- a/unix/uxnet.c
+++ b/unix/uxnet.c
@@ -1023,9 +1023,11 @@ static void sk_tcp_close(Socket sock)
if (s->child)
sk_tcp_close((Socket)s->child);
- uxsel_del(s->s);
del234(sktree, s);
- close(s->s);
+ if (s->s >= 0) {
+ uxsel_del(s->s);
+ close(s->s);
+ }
if (s->addr)
sk_addr_free(s->addr);
sfree(s);
From 6b824713d56bf105cede71daa186593aa9906718 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 1 Oct 2017 21:53:32 +0100
Subject: [PATCH 074/607] term_mouse: make special treatment of x < 0 more
selective.
A mouse drag which manages to reach x < 0 (via SetCapture or
equivalent) was treated as having the coordinates of (x_max, y-1).
This is intended to be useful when the mouse drag is part of ordinary
raster-ordered selection.
But we were leaving that treatment enabled even for mouse actions that
went to xterm mouse tracking mode - thanks to Markus Gans for
reporting that - and when I investigated, I realised that this isn't a
sensible transformation in _rectangular_ selection mode either. Fixed
both.
---
terminal.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/terminal.c b/terminal.c
index 0eb1a9a6..433b3fa5 100644
--- a/terminal.c
+++ b/terminal.c
@@ -6088,7 +6088,18 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
term_scroll(term, 0, +1);
}
if (x < 0) {
- if (y > 0) {
+ if (y > 0 && !raw_mouse && term->seltype != RECTANGULAR) {
+ /*
+ * When we're using the mouse for normal raster-based
+ * selection, dragging off the left edge of a terminal row
+ * is treated the same as the right-hand end of the
+ * previous row, in that it's considered to identify a
+ * point _before_ the first character on row y.
+ *
+ * But if the mouse action is going to be used for
+ * anything else - rectangular selection, or xterm mouse
+ * tracking - then we disable this special treatment.
+ */
x = term->cols - 1;
y--;
} else
From 2f9738a282c9b738b135198d34c61f7d81aa69c1 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 5 Oct 2017 20:27:27 +0100
Subject: [PATCH 075/607] Make terminal true-colour mode configurable.
I know some users don't like any colour _at all_, and we have a
separate option to turn off xterm-style 256-colour sequences, so it
seems remiss not to have an option to disable true colour as well.
---
config.c | 3 +++
doc/config.but | 9 +++++++++
putty.h | 1 +
settings.c | 2 ++
terminal.c | 8 +++++++-
terminal.h | 1 +
windows/winhelp.h | 1 +
7 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/config.c b/config.c
index efe1ff1e..cc5e726b 100644
--- a/config.c
+++ b/config.c
@@ -1898,6 +1898,9 @@ void setup_config_box(struct controlbox *b, int midsession,
ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
HELPCTX(colours_xterm256), conf_checkbox_handler,
I(CONF_xterm_256_colour));
+ ctrl_checkbox(s, "Allow terminal to use 24-bit colours", '4',
+ HELPCTX(colours_truecolour), conf_checkbox_handler,
+ I(CONF_true_colour));
ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3,
HELPCTX(colours_bold),
conf_radiobutton_handler, I(CONF_bold_style),
diff --git a/doc/config.but b/doc/config.but
index bd12efb2..7ad5e282 100644
--- a/doc/config.but
+++ b/doc/config.but
@@ -1549,6 +1549,15 @@ If you do not see \cq{colors#256} in the output, you may need to
change your terminal setting. On modern Linux machines, you could
try \cq{xterm-256color}.
+\S{config-truecolour} \q{Allow terminal to use 24-bit colour}
+
+\cfg{winhelp-topic}{colours.truecolour}
+
+This option is enabled by default. If it is disabled, PuTTY will
+ignore any control sequences sent by the server which use the control
+sequences supported by modern terminals to specify arbitrary 24-bit
+RGB colour value.
+
\S{config-boldcolour} \q{Indicate bolded text by changing...}
\cfg{winhelp-topic}{colours.bold}
diff --git a/putty.h b/putty.h
index 61d6278d..8a8b5425 100644
--- a/putty.h
+++ b/putty.h
@@ -859,6 +859,7 @@ void cleanup_exit(int);
/* Colour options */ \
X(INT, NONE, ansi_colour) \
X(INT, NONE, xterm_256_colour) \
+ X(INT, NONE, true_colour) \
X(INT, NONE, system_colour) \
X(INT, NONE, try_palette) \
X(INT, NONE, bold_style) \
diff --git a/settings.c b/settings.c
index 00c01c54..47e5b9f7 100644
--- a/settings.c
+++ b/settings.c
@@ -609,6 +609,7 @@ void save_open_settings(void *sesskey, Conf *conf)
write_setting_i(sesskey, "TryPalette", conf_get_int(conf, CONF_try_palette));
write_setting_i(sesskey, "ANSIColour", conf_get_int(conf, CONF_ansi_colour));
write_setting_i(sesskey, "Xterm256Colour", conf_get_int(conf, CONF_xterm_256_colour));
+ write_setting_i(sesskey, "TrueColour", conf_get_int(conf, CONF_true_colour));
write_setting_i(sesskey, "BoldAsColour", conf_get_int(conf, CONF_bold_style)-1);
for (i = 0; i < 22; i++) {
@@ -1005,6 +1006,7 @@ void load_open_settings(void *sesskey, Conf *conf)
gppi(sesskey, "TryPalette", 0, conf, CONF_try_palette);
gppi(sesskey, "ANSIColour", 1, conf, CONF_ansi_colour);
gppi(sesskey, "Xterm256Colour", 1, conf, CONF_xterm_256_colour);
+ gppi(sesskey, "TrueColour", 1, conf, CONF_true_colour);
i = gppi_raw(sesskey, "BoldAsColour", 1); conf_set_int(conf, CONF_bold_style, i+1);
for (i = 0; i < 22; i++) {
diff --git a/terminal.c b/terminal.c
index 433b3fa5..5102db12 100644
--- a/terminal.c
+++ b/terminal.c
@@ -1458,6 +1458,7 @@ void term_copy_stuff_from_conf(Terminal *term)
term->scroll_on_disp = conf_get_int(term->conf, CONF_scroll_on_disp);
term->scroll_on_key = conf_get_int(term->conf, CONF_scroll_on_key);
term->xterm_256_colour = conf_get_int(term->conf, CONF_xterm_256_colour);
+ term->true_colour = conf_get_int(term->conf, CONF_true_colour);
/*
* Parse the control-character escapes in the configured
@@ -5171,7 +5172,6 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) |
ATTR_DEFFG | ATTR_DEFBG;
- tc = d->truecolour;
if (!term->xterm_256_colour) {
int colour;
colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT;
@@ -5182,6 +5182,12 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
tattr = (tattr &~ ATTR_BGMASK) | ATTR_DEFBG;
}
+ if (term->true_colour) {
+ tc = d->truecolour;
+ } else {
+ tc.fg = tc.bg = optionalrgb_none;
+ }
+
switch (tchar & CSET_MASK) {
case CSET_ASCII:
tchar = term->ucsdata->unitab_line[tchar & 0xFF];
diff --git a/terminal.h b/terminal.h
index 21b8774a..4a205a77 100644
--- a/terminal.h
+++ b/terminal.h
@@ -325,6 +325,7 @@ struct terminal_tag {
int scroll_on_disp;
int scroll_on_key;
int xterm_256_colour;
+ int true_colour;
};
#define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8)
diff --git a/windows/winhelp.h b/windows/winhelp.h
index 761c8c76..dbe32c91 100644
--- a/windows/winhelp.h
+++ b/windows/winhelp.h
@@ -125,6 +125,7 @@
#define WINHELP_CTX_selection_rtf "selection.rtf:config-rtfpaste"
#define WINHELP_CTX_colours_ansi "colours.ansi:config-ansicolour"
#define WINHELP_CTX_colours_xterm256 "colours.xterm256:config-xtermcolour"
+#define WINHELP_CTX_colours_truecolour "colours.truecolour:config-truecolour"
#define WINHELP_CTX_colours_bold "colours.bold:config-boldcolour"
#define WINHELP_CTX_colours_system "colours.system:config-syscolour"
#define WINHELP_CTX_colours_logpal "colours.logpal:config-logpalette"
From 1adf211d70e162b50b18ede5a9c6ba6ae73ac8b2 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 5 Oct 2017 20:30:09 +0100
Subject: [PATCH 076/607] Disable true colour on monochrome or paletted
displays.
I'm not sure if any X11 monochrome visuals or Windows paletted display
modes are still around, but just in case they are, we shouldn't
attempt true colour on either kind of display.
---
unix/gtkwin.c | 3 +++
windows/window.c | 4 ++--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index f49f92af..94ba4069 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -3255,6 +3255,9 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
} else
ncombining = 1;
+ if (monochrome)
+ truecolour.fg = truecolour.bg = optionalrgb_none;
+
nfg = ((monochrome ? ATTR_DEFFG : (attr & ATTR_FGMASK)) >> ATTR_FGSHIFT);
nbg = ((monochrome ? ATTR_DEFBG : (attr & ATTR_BGMASK)) >> ATTR_BGSHIFT);
if (!!(attr & ATTR_REVERSE) ^ (monochrome && (attr & TATTR_ACTCURS))) {
diff --git a/windows/window.c b/windows/window.c
index 452eb771..79738ff9 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -3522,12 +3522,12 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
if (nbg < 16) nbg |= 8;
else if (nbg >= 256) nbg |= 1;
}
- if (truecolour.fg.enabled)
+ if (!pal && truecolour.fg.enabled)
fg = RGB(truecolour.fg.r, truecolour.fg.g, truecolour.fg.b);
else
fg = colours[nfg];
- if (truecolour.bg.enabled)
+ if (!pal && truecolour.bg.enabled)
bg = RGB(truecolour.bg.r, truecolour.bg.g, truecolour.bg.b);
else
bg = colours[nbg];
From 262376a054719f0c248032df97be66142f0208e3 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 5 Oct 2017 20:30:04 +0100
Subject: [PATCH 077/607] Make the cursor colour override true colour.
Otherwise, moving the cursor (at least in active, filled-cell mode) on
to a true-coloured character cell causes it to vanish completely
because the cell's colours override the thing that differentiates the
cursor.
---
unix/gtkwin.c | 1 +
windows/window.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 94ba4069..dcff614f 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -3274,6 +3274,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
else if (nbg >= 256) nbg |= 1;
}
if ((attr & TATTR_ACTCURS) && !monochrome) {
+ truecolour.fg = truecolour.bg = optionalrgb_none;
nfg = 260;
nbg = 261;
}
diff --git a/windows/window.c b/windows/window.c
index 79738ff9..97c4d462 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -3429,6 +3429,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
y += offset_height;
if ((attr & TATTR_ACTCURS) && (cursor_type == 0 || term->big_cursor)) {
+ truecolour.fg = truecolour.bg = optionalrgb_none;
attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS);
/* cursor fg and bg */
attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT);
From 4743798400e3eeae2902c4540da905a565f05c47 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 5 Oct 2017 20:43:02 +0100
Subject: [PATCH 078/607] Support OSC 4 terminal colour-palette queries.
Markus Gans points out that some applications which (not at all
unreasonably) don't trust $TERM to tell them the full capabilities of
their terminal will use the sequence "OSC 4 ; nn ; ? BEL" to ask for
the colour-palette value in position nn, and they may not particularly
care _what_ the results are but they will use them to decide whether
the right number of colour palette entries even exist.
---
fuzzterm.c | 1 +
putty.h | 1 +
terminal.c | 66 +++++++++++++++++++++++++++++++++++-------------
unix/gtkwin.c | 11 ++++++++
windows/window.c | 52 +++++++++++++++++++++++++-------------
5 files changed, 96 insertions(+), 35 deletions(-)
diff --git a/fuzzterm.c b/fuzzterm.c
index 480dca37..c57412c4 100644
--- a/fuzzterm.c
+++ b/fuzzterm.c
@@ -81,6 +81,7 @@ Context get_ctx(void *frontend) {
void free_ctx(Context ctx) { }
void palette_set(void *frontend, int a, int b, int c, int d) { }
void palette_reset(void *frontend) { }
+int palette_get(void *frontend, int n, int *r, int *g, int *b) {return FALSE;}
void write_clip(void *frontend, wchar_t *a, int *b, int c, int d) { }
void get_clip(void *frontend, wchar_t **w, int *i) { }
void set_raw_mouse_mode(void *frontend, int m) { }
diff --git a/putty.h b/putty.h
index 8a8b5425..f89d0709 100644
--- a/putty.h
+++ b/putty.h
@@ -633,6 +633,7 @@ Context get_ctx(void *frontend);
void free_ctx(Context);
void palette_set(void *frontend, int, int, int, int);
void palette_reset(void *frontend);
+int palette_get(void *frontend, int n, int *r, int *g, int *b);
void write_aclip(void *frontend, char *, int, int);
void write_clip(void *frontend, wchar_t *, int *, int, int);
void get_clip(void *frontend, wchar_t **, int *);
diff --git a/terminal.c b/terminal.c
index 5102db12..0fa52e17 100644
--- a/terminal.c
+++ b/terminal.c
@@ -2727,6 +2727,22 @@ static void do_osc(Terminal *term)
if (!term->no_remote_wintitle)
set_title(term->frontend, term->osc_string);
break;
+ case 4:
+ if (term->ldisc && !strcmp(term->osc_string, "?")) {
+ int r, g, b;
+ if (palette_get(term->frontend, toint(term->esc_args[1]),
+ &r, &g, &b)) {
+ char *reply_buf = dupprintf(
+ "\033]4;%u;rgb:%04x/%04x/%04x\007",
+ term->esc_args[1],
+ (unsigned)r * 0x0101,
+ (unsigned)g * 0x0101,
+ (unsigned)b * 0x0101);
+ ldisc_send(term->ldisc, reply_buf, strlen(reply_buf), 0);
+ sfree(reply_buf);
+ }
+ }
+ break;
}
}
}
@@ -3365,6 +3381,7 @@ static void term_out(Terminal *term)
compatibility(OTHER);
term->termstate = SEEN_OSC;
term->esc_args[0] = 0;
+ term->esc_nargs = 1;
break;
case '7': /* DECSC: save cursor */
compatibility(VT100);
@@ -4470,25 +4487,40 @@ static void term_out(Terminal *term)
case '7':
case '8':
case '9':
- if (term->esc_args[0] <= UINT_MAX / 10 &&
- term->esc_args[0] * 10 <= UINT_MAX - c - '0')
- term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
+ if (term->esc_args[term->esc_nargs-1] <= UINT_MAX / 10 &&
+ term->esc_args[term->esc_nargs-1] * 10 <= UINT_MAX - c - '0')
+ term->esc_args[term->esc_nargs-1] =
+ 10 * term->esc_args[term->esc_nargs-1] + c - '0';
else
- term->esc_args[0] = UINT_MAX;
+ term->esc_args[term->esc_nargs-1] = UINT_MAX;
break;
- case 'L':
- /*
- * Grotty hack to support xterm and DECterm title
- * sequences concurrently.
- */
- if (term->esc_args[0] == 2) {
- term->esc_args[0] = 1;
- break;
- }
- /* else fall through */
- default:
- term->termstate = OSC_STRING;
- term->osc_strlen = 0;
+ default:
+ /*
+ * _Most_ other characters here terminate the
+ * immediate parsing of the OSC sequence and go
+ * into OSC_STRING state, but we deal with a
+ * couple of exceptions first.
+ */
+ if (c == 'L' && term->esc_args[0] == 2) {
+ /*
+ * Grotty hack to support xterm and DECterm title
+ * sequences concurrently.
+ */
+ term->esc_args[0] = 1;
+ } else if (c == ';' && term->esc_nargs == 1 &&
+ term->esc_args[0] == 4) {
+ /*
+ * xterm's OSC 4 sequence to query the current
+ * RGB value of a colour takes a second
+ * numeric argument which is easiest to parse
+ * using the existing system rather than in
+ * do_osc.
+ */
+ term->esc_args[term->esc_nargs++] = 0;
+ } else {
+ term->termstate = OSC_STRING;
+ term->osc_strlen = 0;
+ }
}
break;
case OSC_STRING:
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index dcff614f..969c42ff 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -2222,6 +2222,17 @@ void palette_set(void *frontend, int n, int r, int g, int b)
}
}
+int palette_get(void *frontend, int n, int *r, int *g, int *b)
+{
+ struct gui_data *inst = (struct gui_data *)frontend;
+ if (n < 0 || n >= NALLCOLOURS)
+ return FALSE;
+ *r = inst->cols[n].red >> 8;
+ *g = inst->cols[n].green >> 8;
+ *b = inst->cols[n].blue >> 8;
+ return TRUE;
+}
+
void palette_reset(void *frontend)
{
struct gui_data *inst = (struct gui_data *)frontend;
diff --git a/windows/window.c b/windows/window.c
index 97c4d462..aa96a578 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -198,6 +198,9 @@ static int descent;
#define NEXTCOLOURS 240
#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
static COLORREF colours[NALLCOLOURS];
+struct rgb {
+ int r, g, b;
+} colours_rgb[NALLCOLOURS];
static HPALETTE pal;
static LPLOGPALETTE logpal;
static RGBTRIPLE defpal[NALLCOLOURS];
@@ -1264,6 +1267,19 @@ static void systopalette(void)
}
}
+static void internal_set_colour(int i, int r, int g, int b)
+{
+ assert(i >= 0);
+ assert(i < NALLCOLOURS);
+ if (pal)
+ colours[i] = PALETTERGB(r, g, b);
+ else
+ colours[i] = RGB(r, g, b);
+ colours_rgb[i].r = r;
+ colours_rgb[i].g = g;
+ colours_rgb[i].b = b;
+}
+
/*
* Set up the colour palette.
*/
@@ -1298,15 +1314,9 @@ static void init_palette(void)
}
ReleaseDC(hwnd, hdc);
}
- if (pal)
- for (i = 0; i < NALLCOLOURS; i++)
- colours[i] = PALETTERGB(defpal[i].rgbtRed,
- defpal[i].rgbtGreen,
- defpal[i].rgbtBlue);
- else
- for (i = 0; i < NALLCOLOURS; i++)
- colours[i] = RGB(defpal[i].rgbtRed,
- defpal[i].rgbtGreen, defpal[i].rgbtBlue);
+ for (i = 0; i < NALLCOLOURS; i++)
+ internal_set_colour(i, defpal[i].rgbtRed,
+ defpal[i].rgbtGreen, defpal[i].rgbtBlue);
}
/*
@@ -4865,15 +4875,24 @@ void free_ctx(Context ctx)
static void real_palette_set(int n, int r, int g, int b)
{
+ internal_set_colour(n, r, g, b);
if (pal) {
logpal->palPalEntry[n].peRed = r;
logpal->palPalEntry[n].peGreen = g;
logpal->palPalEntry[n].peBlue = b;
logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
- colours[n] = PALETTERGB(r, g, b);
SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry);
- } else
- colours[n] = RGB(r, g, b);
+ }
+}
+
+int palette_get(void *frontend, int n, int *r, int *g, int *b)
+{
+ if (n < 0 || n >= NALLCOLOURS)
+ return FALSE;
+ *r = colours_rgb[n].r;
+ *g = colours_rgb[n].g;
+ *b = colours_rgb[n].b;
+ return TRUE;
}
void palette_set(void *frontend, int n, int r, int g, int b)
@@ -4903,17 +4922,14 @@ void palette_reset(void *frontend)
/* And this */
for (i = 0; i < NALLCOLOURS; i++) {
+ internal_set_colour(i, defpal[i].rgbtRed,
+ defpal[i].rgbtGreen, defpal[i].rgbtBlue);
if (pal) {
logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
logpal->palPalEntry[i].peFlags = 0;
- colours[i] = PALETTERGB(defpal[i].rgbtRed,
- defpal[i].rgbtGreen,
- defpal[i].rgbtBlue);
- } else
- colours[i] = RGB(defpal[i].rgbtRed,
- defpal[i].rgbtGreen, defpal[i].rgbtBlue);
+ }
}
if (pal) {
From 1a718403d40ccb88a1436eff98c82bf92268ef96 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 5 Oct 2017 21:02:56 +0100
Subject: [PATCH 079/607] Support SGR 2 to dim the foreground colour.
I've done this on a 'where possible' basis: in Windows paletted mode
(in case anyone is still using an old enough graphics card to need
that!) I simply haven't bothered, and will completely ignore the dim
flag.
---
putty.h | 19 ++++++++-------
terminal.c | 8 +++++--
unix/gtkwin.c | 61 ++++++++++++++++++++++++++++++++++--------------
windows/window.c | 8 ++++++-
4 files changed, 67 insertions(+), 29 deletions(-)
diff --git a/putty.h b/putty.h
index f89d0709..a9433fa7 100644
--- a/putty.h
+++ b/putty.h
@@ -104,15 +104,16 @@ typedef struct terminal_tag Terminal;
*/
#define UCSWIDE 0xDFFF
-#define ATTR_NARROW 0x800000U
-#define ATTR_WIDE 0x400000U
-#define ATTR_BOLD 0x040000U
-#define ATTR_UNDER 0x080000U
-#define ATTR_REVERSE 0x100000U
-#define ATTR_BLINK 0x200000U
-#define ATTR_FGMASK 0x0001FFU
-#define ATTR_BGMASK 0x03FE00U
-#define ATTR_COLOURS 0x03FFFFU
+#define ATTR_NARROW 0x0800000U
+#define ATTR_WIDE 0x0400000U
+#define ATTR_BOLD 0x0040000U
+#define ATTR_UNDER 0x0080000U
+#define ATTR_REVERSE 0x0100000U
+#define ATTR_BLINK 0x0200000U
+#define ATTR_FGMASK 0x00001FFU
+#define ATTR_BGMASK 0x003FE00U
+#define ATTR_COLOURS 0x003FFFFU
+#define ATTR_DIM 0x1000000U
#define ATTR_FGSHIFT 0
#define ATTR_BGSHIFT 9
diff --git a/terminal.c b/terminal.c
index 0fa52e17..de5748e9 100644
--- a/terminal.c
+++ b/terminal.c
@@ -3881,6 +3881,10 @@ static void term_out(Terminal *term)
compatibility(VT100AVO);
term->curr_attr |= ATTR_BOLD;
break;
+ case 2: /* enable dim */
+ compatibility(OTHER);
+ term->curr_attr |= ATTR_DIM;
+ break;
case 21: /* (enable double underline) */
compatibility(OTHER);
case 4: /* enable underline */
@@ -3912,9 +3916,9 @@ static void term_out(Terminal *term)
compatibility(SCOANSI);
if (term->no_remote_charset) break;
term->sco_acs = 2; break;
- case 22: /* disable bold */
+ case 22: /* disable bold and dim */
compatibility2(OTHER, VT220);
- term->curr_attr &= ~ATTR_BOLD;
+ term->curr_attr &= ~(ATTR_BOLD | ATTR_DIM);
break;
case 24: /* disable underline */
compatibility2(OTHER, VT220);
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 969c42ff..5c629519 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -3023,24 +3023,44 @@ static void draw_update(struct draw_ctx *dctx, int x, int y, int w, int h)
gtk_widget_queue_draw_area(dctx->inst->area, x, y, w, h);
}
-static void draw_set_colour(struct draw_ctx *dctx, int col)
+#ifdef DRAW_TEXT_CAIRO
+static void cairo_set_source_rgb_dim(cairo_t *cr, double r, double g, double b,
+ int dim)
+{
+ if (dim)
+ cairo_set_source_rgb(cr, r * 2 / 3, g * 2 / 3, b * 2 / 3);
+ else
+ cairo_set_source_rgb(cr, r, g, b);
+}
+#endif
+
+static void draw_set_colour(struct draw_ctx *dctx, int col, int dim)
{
#ifdef DRAW_TEXT_GDK
if (dctx->uctx.type == DRAWTYPE_GDK) {
- gdk_gc_set_foreground(dctx->uctx.u.gdk.gc, &dctx->inst->cols[col]);
+ if (dim) {
+ GdkColor color;
+ color.red = dctx->inst->cols[col].red * 2 / 3;
+ color.green = dctx->inst->cols[col].green * 2 / 3;
+ color.blue = dctx->inst->cols[col].blue * 2 / 3;
+ gdk_gc_set_rgb_fg_color(dctx->uctx.u.gdk.gc, &color);
+ } else {
+ gdk_gc_set_foreground(dctx->uctx.u.gdk.gc, &dctx->inst->cols[col]);
+ }
}
#endif
#ifdef DRAW_TEXT_CAIRO
if (dctx->uctx.type == DRAWTYPE_CAIRO) {
- cairo_set_source_rgb(dctx->uctx.u.cairo.cr,
- dctx->inst->cols[col].red / 65535.0,
- dctx->inst->cols[col].green / 65535.0,
- dctx->inst->cols[col].blue / 65535.0);
+ cairo_set_source_rgb_dim(dctx->uctx.u.cairo.cr,
+ dctx->inst->cols[col].red / 65535.0,
+ dctx->inst->cols[col].green / 65535.0,
+ dctx->inst->cols[col].blue / 65535.0, dim);
}
#endif
}
-static void draw_set_colour_rgb(struct draw_ctx *dctx, optionalrgb orgb)
+static void draw_set_colour_rgb(struct draw_ctx *dctx, optionalrgb orgb,
+ int dim)
{
#ifdef DRAW_TEXT_GDK
if (dctx->uctx.type == DRAWTYPE_GDK) {
@@ -3048,13 +3068,19 @@ static void draw_set_colour_rgb(struct draw_ctx *dctx, optionalrgb orgb)
color.red = orgb.r * 256;
color.green = orgb.g * 256;
color.blue = orgb.b * 256;
+ if (dim) {
+ color.red = color.red * 2 / 3;
+ color.green = color.green * 2 / 3;
+ color.blue = color.blue * 2 / 3;
+ }
gdk_gc_set_rgb_fg_color(dctx->uctx.u.gdk.gc, &color);
}
#endif
#ifdef DRAW_TEXT_CAIRO
- if (dctx->uctx.type == DRAWTYPE_CAIRO)
- cairo_set_source_rgb(dctx->uctx.u.cairo.cr,
- orgb.r / 255.0, orgb.g / 255.0, orgb.b / 255.0);
+ if (dctx->uctx.type == DRAWTYPE_CAIRO) {
+ cairo_set_source_rgb_dim(dctx->uctx.u.cairo.cr, orgb.r / 255.0,
+ orgb.g / 255.0, orgb.b / 255.0, dim);
+ }
#endif
}
@@ -3238,7 +3264,7 @@ static void draw_backing_rect(struct gui_data *inst)
struct draw_ctx *dctx = get_ctx(inst);
int w = inst->width * inst->font_width + 2*inst->window_border;
int h = inst->height * inst->font_height + 2*inst->window_border;
- draw_set_colour(dctx, 258);
+ draw_set_colour(dctx, 258, FALSE);
draw_rectangle(dctx, 1, 0, 0, w, h);
draw_update(dctx, 0, 0, w, h);
free_ctx(dctx);
@@ -3288,6 +3314,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
truecolour.fg = truecolour.bg = optionalrgb_none;
nfg = 260;
nbg = 261;
+ attr &= ~ATTR_DIM; /* don't dim the cursor */
}
fontid = shadow = 0;
@@ -3350,18 +3377,18 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
}
if (truecolour.bg.enabled)
- draw_set_colour_rgb(dctx, truecolour.bg);
+ draw_set_colour_rgb(dctx, truecolour.bg, attr & ATTR_DIM);
else
- draw_set_colour(dctx, nbg);
+ draw_set_colour(dctx, nbg, attr & ATTR_DIM);
draw_rectangle(dctx, TRUE,
x*inst->font_width+inst->window_border,
y*inst->font_height+inst->window_border,
rlen*widefactor*inst->font_width, inst->font_height);
if (truecolour.fg.enabled)
- draw_set_colour_rgb(dctx, truecolour.fg);
+ draw_set_colour_rgb(dctx, truecolour.fg, attr & ATTR_DIM);
else
- draw_set_colour(dctx, nfg);
+ draw_set_colour(dctx, nfg, attr & ATTR_DIM);
if (ncombining > 1) {
assert(len == 1);
unifont_draw_combining(&dctx->uctx, inst->fonts[fontid],
@@ -3475,7 +3502,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
* if it's passive.
*/
if (passive) {
- draw_set_colour(dctx, 261);
+ draw_set_colour(dctx, 261, FALSE);
draw_rectangle(dctx, FALSE,
x*inst->font_width+inst->window_border,
y*inst->font_height+inst->window_border,
@@ -3514,7 +3541,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
length = inst->font_height;
}
- draw_set_colour(dctx, 261);
+ draw_set_colour(dctx, 261, FALSE);
if (passive) {
for (i = 0; i < length; i++) {
if (i % 2 == 0) {
diff --git a/windows/window.c b/windows/window.c
index aa96a578..be7b4379 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -3440,7 +3440,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
if ((attr & TATTR_ACTCURS) && (cursor_type == 0 || term->big_cursor)) {
truecolour.fg = truecolour.bg = optionalrgb_none;
- attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS);
+ attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS|ATTR_DIM);
/* cursor fg and bg */
attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT);
is_cursor = TRUE;
@@ -3543,6 +3543,12 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
else
bg = colours[nbg];
+ if (!pal && (attr & ATTR_DIM)) {
+ fg = RGB(GetRValue(fg) * 2 / 3,
+ GetGValue(fg) * 2 / 3,
+ GetBValue(fg) * 2 / 3);
+ }
+
SelectObject(hdc, fonts[nfont]);
SetTextColor(hdc, fg);
SetBkColor(hdc, bg);
From 96a088195fe86cb31347b42b1c262c1aa2ee0a2f Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 8 Oct 2017 13:43:27 +0100
Subject: [PATCH 080/607] Make true colour work with background-colour erase.
Thanks to Markus Gans for reporting this bug.
---
terminal.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/terminal.c b/terminal.c
index de5748e9..8c44b906 100644
--- a/terminal.c
+++ b/terminal.c
@@ -1407,9 +1407,11 @@ void term_pwron(Terminal *term, int clear)
static void set_erase_char(Terminal *term)
{
term->erase_char = term->basic_erase_char;
- if (term->use_bce)
+ if (term->use_bce) {
term->erase_char.attr = (term->curr_attr &
(ATTR_FGMASK | ATTR_BGMASK));
+ term->erase_char.truecolour.bg = term->curr_truecolour.bg;
+ }
}
/*
From e3d92df936b97aef3cd51f1e85eb3c47409069c5 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 8 Oct 2017 13:45:08 +0100
Subject: [PATCH 081/607] Save and restore true-colour state with the cursor.
I spotted this myself while looking through the code in search of the
cause of the background-colour-erase bug: saving and restoring the
cursor via ESC 7 / ESC 8 ought to also save and restore the current
graphics rendition attributes including foreground and background
colour settings, but it was not saving and restoring the new
term->curr_truecolour along with term->curr_attr.
So there's now a term->save_truecolour to keep that in, and also a
term->alt_save_truecolour to take account of the fact that all the
saved cursor state variables get swapped out _again_ when switching
between the main and alternate screens.
(However, there is not a term->alt_truecolour to complete the cross
product, because the _active_ graphics rendition is carried over when
switching between the terminal screens; it's only the _saved_ one from
ESC 7 / ESC 8 that is saved separately. That's consistent with the
behaviour we've had all along for ordinary fg/bg colour selection.)
---
terminal.c | 8 ++++++++
terminal.h | 1 +
2 files changed, 9 insertions(+)
diff --git a/terminal.c b/terminal.c
index 8c44b906..7a7207a0 100644
--- a/terminal.c
+++ b/terminal.c
@@ -1297,6 +1297,7 @@ static void power_on(Terminal *term, int clear)
term->default_attr = term->save_attr =
term->alt_save_attr = term->curr_attr = ATTR_DEFAULT;
term->curr_truecolour.fg = term->curr_truecolour.bg = optionalrgb_none;
+ term->save_truecolour = term->alt_save_truecolour = term->curr_truecolour;
term->term_editing = term->term_echoing = FALSE;
term->app_cursor_keys = conf_get_int(term->conf, CONF_app_cursor);
term->app_keypad_keys = conf_get_int(term->conf, CONF_app_keypad);
@@ -1987,6 +1988,7 @@ static void swap_screen(Terminal *term, int which, int reset, int keep_cur_pos)
{
int t;
pos tp;
+ truecolour ttc;
tree234 *ttr;
if (!which)
@@ -2051,6 +2053,10 @@ static void swap_screen(Terminal *term, int which, int reset, int keep_cur_pos)
if (!reset && !keep_cur_pos)
term->save_attr = term->alt_save_attr;
term->alt_save_attr = t;
+ ttc = term->save_truecolour;
+ if (!reset && !keep_cur_pos)
+ term->save_truecolour = term->alt_save_truecolour;
+ term->alt_save_truecolour = ttc;
t = term->save_utf;
if (!reset && !keep_cur_pos)
term->save_utf = term->alt_save_utf;
@@ -2346,6 +2352,7 @@ static void save_cursor(Terminal *term, int save)
if (save) {
term->savecurs = term->curs;
term->save_attr = term->curr_attr;
+ term->save_truecolour = term->curr_truecolour;
term->save_cset = term->cset;
term->save_utf = term->utf;
term->save_wnext = term->wrapnext;
@@ -2360,6 +2367,7 @@ static void save_cursor(Terminal *term, int save)
term->curs.y = term->rows - 1;
term->curr_attr = term->save_attr;
+ term->curr_truecolour = term->save_truecolour;
term->cset = term->save_cset;
term->utf = term->save_utf;
term->wrapnext = term->save_wnext;
diff --git a/terminal.h b/terminal.h
index 4a205a77..fcb321c4 100644
--- a/terminal.h
+++ b/terminal.h
@@ -140,6 +140,7 @@ struct terminal_tag {
/* ESC 7 saved state for the alternate screen */
pos alt_savecurs;
int alt_save_attr;
+ truecolour alt_save_truecolour;
int alt_save_cset, alt_save_csattr;
int alt_save_utf, alt_save_wnext;
int alt_save_sco_acs;
From f353e2219e4371cda176bc37a11ddc4bb7e32516 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 8 Oct 2017 13:47:39 +0100
Subject: [PATCH 082/607] Turn off true colour on SCO and VT52 colour
sequences.
After fixing the previous two bugs, I thought it was probably a good
idea to re-check _everywhere_ in terminal.c where curr_attr is used,
to make sure that if curr_truecolour also needed updating at the same
time then that was being done.
---
terminal.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/terminal.c b/terminal.c
index 7a7207a0..a55b6e6a 100644
--- a/terminal.c
+++ b/terminal.c
@@ -4370,6 +4370,7 @@ static void term_out(Terminal *term)
ATTR_FGSHIFT;
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |= colour;
+ term->curr_truecolour.fg = optionalrgb_none;
term->default_attr &= ~ATTR_FGMASK;
term->default_attr |= colour;
set_erase_char(term);
@@ -4384,6 +4385,7 @@ static void term_out(Terminal *term)
ATTR_BGSHIFT;
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |= colour;
+ term->curr_truecolour.bg = optionalrgb_none;
term->default_attr &= ~ATTR_BGMASK;
term->default_attr |= colour;
set_erase_char(term);
@@ -4811,6 +4813,8 @@ static void term_out(Terminal *term)
/* compatibility(OTHER) */
term->vt52_bold = FALSE;
term->curr_attr = ATTR_DEFAULT;
+ term->curr_truecolour.fg = optionalrgb_none;
+ term->curr_truecolour.bg = optionalrgb_none;
set_erase_char(term);
break;
case 'S':
From 916a2574d5fb40ee9ce899ac93e8d31738f5bed4 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 8 Oct 2017 13:49:54 +0100
Subject: [PATCH 083/607] Make reverse video interact correctly with true
colour.
ATTR_REVERSE was being handled in the front ends, and was causing the
foreground and background colours to be switched. (I'm not completely
sure why I made that design decision; it might be purely historical,
but then again, it might also be because reverse video is one effect
on the fg and bg colours that must still be performed even in unusual
frontend-specific situations like display-driven monochrome mode.)
This affected both explicit reverse video enabled using SGR 7, and
also the transient reverse video arising from mouse selection. Thanks
to Markus Gans for reporting the bug in the latter, which when I
investigated it turned out to affect the former as well.
---
terminal.h | 2 +-
unix/gtkwin.c | 6 ++++++
windows/window.c | 6 ++++++
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/terminal.h b/terminal.h
index fcb321c4..03659744 100644
--- a/terminal.h
+++ b/terminal.h
@@ -103,7 +103,7 @@ struct terminal_tag {
#endif /* OPTIMISE_SCROLL */
int default_attr, curr_attr, save_attr;
- truecolour curr_truecolour;
+ truecolour curr_truecolour, save_truecolour;
termchar basic_erase_char, erase_char;
bufchain inbuf; /* terminal input buffer */
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 5c629519..3e12a4ba 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -3298,9 +3298,15 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
nfg = ((monochrome ? ATTR_DEFFG : (attr & ATTR_FGMASK)) >> ATTR_FGSHIFT);
nbg = ((monochrome ? ATTR_DEFBG : (attr & ATTR_BGMASK)) >> ATTR_BGSHIFT);
if (!!(attr & ATTR_REVERSE) ^ (monochrome && (attr & TATTR_ACTCURS))) {
+ struct optionalrgb trgb;
+
t = nfg;
nfg = nbg;
nbg = t;
+
+ trgb = truecolour.fg;
+ truecolour.fg = truecolour.bg;
+ truecolour.bg = trgb;
}
if ((inst->bold_style & 2) && (attr & ATTR_BOLD)) {
if (nfg < 16) nfg |= 8;
diff --git a/windows/window.c b/windows/window.c
index be7b4379..73f0751c 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -3521,9 +3521,15 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
if (!fonts[nfont])
nfont = FONT_NORMAL;
if (attr & ATTR_REVERSE) {
+ struct optionalrgb trgb;
+
t = nfg;
nfg = nbg;
nbg = t;
+
+ trgb = truecolour.fg;
+ truecolour.fg = truecolour.bg;
+ truecolour.bg = trgb;
}
if (bold_colours && (attr & ATTR_BOLD) && !is_cursor) {
if (nfg < 16) nfg |= 8;
From 298b9fd4d40a86b192a23a6bceac1f06f89f9eaf Mon Sep 17 00:00:00 2001
From: Jeff Smith
Date: Fri, 13 Oct 2017 22:49:25 -0500
Subject: [PATCH 084/607] Setting an 8-bit colour should cancel a 24-bit colour
---
terminal.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/terminal.c b/terminal.c
index a55b6e6a..2106d10a 100644
--- a/terminal.c
+++ b/terminal.c
@@ -4031,6 +4031,8 @@ static void term_out(Terminal *term)
term->curr_attr |=
((term->esc_args[i+2] & 0xFF)
<< ATTR_FGSHIFT);
+ term->curr_truecolour.fg =
+ optionalrgb_none;
i += 2;
}
if (i + 4 < term->esc_nargs &&
@@ -4048,6 +4050,8 @@ static void term_out(Terminal *term)
term->curr_attr |=
((term->esc_args[i+2] & 0xFF)
<< ATTR_BGSHIFT);
+ term->curr_truecolour.bg =
+ optionalrgb_none;
i += 2;
}
if (i + 4 < term->esc_nargs &&
From 7bdfdabb5e1c7418074ff43d9b436033c96aa00c Mon Sep 17 00:00:00 2001
From: Jeff Smith
Date: Wed, 14 Jun 2017 08:11:05 -0500
Subject: [PATCH 085/607] Update clipping interface for true-colour
---
fuzzterm.c | 2 +-
putty.h | 2 +-
terminal.c | 19 ++++++++++++++-----
unix/gtkwin.c | 8 ++++----
windows/window.c | 3 ++-
5 files changed, 22 insertions(+), 12 deletions(-)
diff --git a/fuzzterm.c b/fuzzterm.c
index c57412c4..255fc6fb 100644
--- a/fuzzterm.c
+++ b/fuzzterm.c
@@ -82,7 +82,7 @@ void free_ctx(Context ctx) { }
void palette_set(void *frontend, int a, int b, int c, int d) { }
void palette_reset(void *frontend) { }
int palette_get(void *frontend, int n, int *r, int *g, int *b) {return FALSE;}
-void write_clip(void *frontend, wchar_t *a, int *b, int c, int d) { }
+void write_clip(void *frontend, wchar_t *a, int *b, truecolour *c, int d, int e) { }
void get_clip(void *frontend, wchar_t **w, int *i) { }
void set_raw_mouse_mode(void *frontend, int m) { }
void request_paste(void *frontend) { }
diff --git a/putty.h b/putty.h
index a9433fa7..ac35eb73 100644
--- a/putty.h
+++ b/putty.h
@@ -636,7 +636,7 @@ void palette_set(void *frontend, int, int, int, int);
void palette_reset(void *frontend);
int palette_get(void *frontend, int n, int *r, int *g, int *b);
void write_aclip(void *frontend, char *, int, int);
-void write_clip(void *frontend, wchar_t *, int *, int, int);
+void write_clip(void *frontend, wchar_t *, int *, truecolour *, int, int);
void get_clip(void *frontend, wchar_t **, int *);
void optimised_move(void *frontend, int, int, int);
void set_raw_mouse_mode(void *frontend, int);
diff --git a/terminal.c b/terminal.c
index 2106d10a..d32d4f42 100644
--- a/terminal.c
+++ b/terminal.c
@@ -5616,9 +5616,11 @@ typedef struct {
wchar_t *textptr; /* = textbuf + bufpos (current insertion point) */
int *attrbuf; /* buffer for copied attributes */
int *attrptr; /* = attrbuf + bufpos */
+ truecolour *tcbuf; /* buffer for copied colours */
+ truecolour *tcptr; /* = tcbuf + bufpos */
} clip_workbuf;
-static void clip_addchar(clip_workbuf *b, wchar_t chr, int attr)
+static void clip_addchar(clip_workbuf *b, wchar_t chr, int attr, truecolour tc)
{
if (b->bufpos >= b->buflen) {
b->buflen *= 2;
@@ -5626,9 +5628,12 @@ static void clip_addchar(clip_workbuf *b, wchar_t chr, int attr)
b->textptr = b->textbuf + b->bufpos;
b->attrbuf = sresize(b->attrbuf, b->buflen, int);
b->attrptr = b->attrbuf + b->bufpos;
+ b->tcbuf = sresize(b->tcbuf, b->buflen, truecolour);
+ b->tcptr = b->tcbuf + b->bufpos;
}
*b->textptr++ = chr;
*b->attrptr++ = attr;
+ *b->tcptr++ = tc;
b->bufpos++;
}
@@ -5637,11 +5642,13 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
clip_workbuf buf;
int old_top_x;
int attr;
+ truecolour tc;
buf.buflen = 5120;
buf.bufpos = 0;
buf.textptr = buf.textbuf = snewn(buf.buflen, wchar_t);
buf.attrptr = buf.attrbuf = snewn(buf.buflen, int);
+ buf.tcptr = buf.tcbuf = snewn(buf.buflen, truecolour);
old_top_x = top.x; /* needed for rect==1 */
@@ -5707,6 +5714,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
while (1) {
int uc = ldata->chars[x].chr;
attr = ldata->chars[x].attr;
+ tc = ldata->chars[x].truecolour;
switch (uc & CSET_MASK) {
case CSET_LINEDRW:
@@ -5767,7 +5775,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
#endif
for (p = cbuf; *p; p++)
- clip_addchar(&buf, *p, attr);
+ clip_addchar(&buf, *p, attr, tc);
if (ldata->chars[x].cc_next)
x += ldata->chars[x].cc_next;
@@ -5779,7 +5787,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
if (nl) {
int i;
for (i = 0; i < sel_nl_sz; i++)
- clip_addchar(&buf, sel_nl[i], 0);
+ clip_addchar(&buf, sel_nl[i], 0, term->basic_erase_char.truecolour);
}
top.y++;
top.x = rect ? old_top_x : 0;
@@ -5787,12 +5795,13 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
unlineptr(ldata);
}
#if SELECTION_NUL_TERMINATED
- clip_addchar(&buf, 0, 0);
+ clip_addchar(&buf, 0, 0, term->basic_erase_char.truecolour);
#endif
/* Finally, transfer all that to the clipboard. */
- write_clip(term->frontend, buf.textbuf, buf.attrbuf, buf.bufpos, desel);
+ write_clip(term->frontend, buf.textbuf, buf.attrbuf, buf.tcbuf, buf.bufpos, desel);
sfree(buf.textbuf);
sfree(buf.attrbuf);
+ sfree(buf.tcbuf);
}
void term_copyall(Terminal *term)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 3e12a4ba..3a3c2a54 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -2360,8 +2360,8 @@ static void clipboard_clear(GtkClipboard *clipboard, gpointer data)
sfree(cdi);
}
-void write_clip(void *frontend, wchar_t *data, int *attr, int len,
- int must_deselect)
+void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour,
+ int len, int must_deselect)
{
struct gui_data *inst = (struct gui_data *)frontend;
struct clipboard_data_instance *cdi;
@@ -2488,8 +2488,8 @@ static char *retrieve_cutbuffer(int *nbytes)
#endif
}
-void write_clip(void *frontend, wchar_t *data, int *attr, int len,
- int must_deselect)
+void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour,
+ int len, int must_deselect)
{
struct gui_data *inst = (struct gui_data *)frontend;
if (inst->pasteout_data)
diff --git a/windows/window.c b/windows/window.c
index 73f0751c..8faa57e9 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -4989,7 +4989,8 @@ void write_aclip(void *frontend, char *data, int len, int must_deselect)
/*
* Note: unlike write_aclip() this will not append a nul.
*/
-void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect)
+void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour,
+ int len, int must_deselect)
{
HGLOBAL clipdata, clipdata2, clipdata3;
int len2;
From 891d909b3d7fb206e1055feaafbb25e771393ce2 Mon Sep 17 00:00:00 2001
From: Jeff Smith
Date: Mon, 12 Jun 2017 23:55:57 -0500
Subject: [PATCH 086/607] Implement true-colour in write_clip for Windows
This allows text copied from PuTTY to a rich-text program to retain the
true-colour attribute.
---
windows/window.c | 126 +++++++++++++++++++++++++++++++++++++++++------
1 file changed, 111 insertions(+), 15 deletions(-)
diff --git a/windows/window.c b/windows/window.c
index 8faa57e9..88a29dd4 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -24,6 +24,7 @@
#include "storage.h"
#include "win_res.h"
#include "winsecur.h"
+#include "tree234.h"
#ifndef NO_MULTIMON
#include
@@ -4986,6 +4987,18 @@ void write_aclip(void *frontend, char *data, int len, int must_deselect)
SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
}
+typedef struct _rgbindex {
+ int index;
+ COLORREF ref;
+} rgbindex;
+
+int cmpCOLORREF(void *va, void *vb)
+{
+ COLORREF a = ((rgbindex *)va)->ref;
+ COLORREF b = ((rgbindex *)vb)->ref;
+ return (a < b) ? -1 : (a > b) ? +1 : 0;
+}
+
/*
* Note: unlike write_aclip() this will not append a nul.
*/
@@ -5033,12 +5046,15 @@ void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour
int rtfsize = 0;
int multilen, blen, alen, totallen, i;
char before[16], after[4];
- int fgcolour, lastfgcolour = 0;
- int bgcolour, lastbgcolour = 0;
+ int fgcolour, lastfgcolour = -1;
+ int bgcolour, lastbgcolour = -1;
+ COLORREF fg, lastfg = -1;
+ COLORREF bg, lastbg = -1;
int attrBold, lastAttrBold = 0;
int attrUnder, lastAttrUnder = 0;
int palette[NALLCOLOURS];
int numcolours;
+ tree234 *rgbtree = NULL;
FontSpec *font = conf_get_fontspec(conf, CONF_font);
get_unitab(CP_ACP, unitab, 0);
@@ -5076,7 +5092,7 @@ void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour
fgcolour ++;
}
- if (attr[i] & ATTR_BLINK) {
+ if ((attr[i] & ATTR_BLINK)) {
if (bgcolour < 8) /* ANSI colours */
bgcolour += 8;
else if (bgcolour >= 256) /* Default colours */
@@ -5087,6 +5103,28 @@ void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour
palette[bgcolour]++;
}
+ if (truecolour) {
+ rgbtree = newtree234(cmpCOLORREF);
+ for (i = 0; i < (len-1); i++) {
+ if (truecolour[i].fg.enabled) {
+ rgbindex *rgbp = snew(rgbindex);
+ rgbp->ref = RGB(truecolour[i].fg.r,
+ truecolour[i].fg.g,
+ truecolour[i].fg.b);
+ if (add234(rgbtree, rgbp) != rgbp)
+ sfree(rgbp);
+ }
+ if (truecolour[i].bg.enabled) {
+ rgbindex *rgbp = snew(rgbindex);
+ rgbp->ref = RGB(truecolour[i].bg.r,
+ truecolour[i].bg.g,
+ truecolour[i].bg.b);
+ if (add234(rgbtree, rgbp) != rgbp)
+ sfree(rgbp);
+ }
+ }
+ }
+
/*
* Next - Create a reduced palette
*/
@@ -5096,6 +5134,12 @@ void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour
palette[i] = ++numcolours;
}
+ if (rgbtree) {
+ rgbindex *rgbp;
+ for (i = 0; (rgbp = index234(rgbtree, i)) != NULL; i++)
+ rgbp->index = ++numcolours;
+ }
+
/*
* Finally - Write the colour table
*/
@@ -5108,6 +5152,12 @@ void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour
rtflen += sprintf(&rtf[rtflen], "\\red%d\\green%d\\blue%d;", defpal[i].rgbtRed, defpal[i].rgbtGreen, defpal[i].rgbtBlue);
}
}
+ if (rgbtree) {
+ rgbindex *rgbp;
+ for (i = 0; (rgbp = index234(rgbtree, i)) != NULL; i++)
+ rtflen += sprintf(&rtf[rtflen], "\\red%d\\green%d\\blue%d;",
+ GetRValue(rgbp->ref), GetGValue(rgbp->ref), GetBValue(rgbp->ref));
+ }
strcpy(&rtf[rtflen], "}");
rtflen ++;
}
@@ -5151,23 +5201,44 @@ void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour
/*
* Determine foreground and background colours
*/
- fgcolour = ((attr[tindex] & ATTR_FGMASK) >> ATTR_FGSHIFT);
- bgcolour = ((attr[tindex] & ATTR_BGMASK) >> ATTR_BGSHIFT);
+ if (truecolour && truecolour[tindex].fg.enabled) {
+ fgcolour = -1;
+ fg = RGB(truecolour[tindex].fg.r,
+ truecolour[tindex].fg.g,
+ truecolour[tindex].fg.b);
+ } else {
+ fgcolour = ((attr[tindex] & ATTR_FGMASK) >> ATTR_FGSHIFT);
+ fg = -1;
+ }
+
+ if (truecolour && truecolour[tindex].bg.enabled) {
+ bgcolour = -1;
+ bg = RGB(truecolour[tindex].bg.r,
+ truecolour[tindex].bg.g,
+ truecolour[tindex].bg.b);
+ } else {
+ bgcolour = ((attr[tindex] & ATTR_BGMASK) >> ATTR_BGSHIFT);
+ bg = -1;
+ }
if (attr[tindex] & ATTR_REVERSE) {
int tmpcolour = fgcolour; /* Swap foreground and background */
fgcolour = bgcolour;
bgcolour = tmpcolour;
+
+ COLORREF tmpref = fg;
+ fg = bg;
+ bg = tmpref;
}
- if (bold_colours && (attr[tindex] & ATTR_BOLD)) {
+ if (bold_colours && (attr[tindex] & ATTR_BOLD) && (fgcolour >= 0)) {
if (fgcolour < 8) /* ANSI colours */
fgcolour += 8;
else if (fgcolour >= 256) /* Default colours */
fgcolour ++;
}
- if (attr[tindex] & ATTR_BLINK) {
+ if ((attr[tindex] & ATTR_BLINK) && (bgcolour >= 0)) {
if (bgcolour < 8) /* ANSI colours */
bgcolour += 8;
else if (bgcolour >= 256) /* Default colours */
@@ -5206,15 +5277,33 @@ void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour
/*
* Write RTF text attributes
*/
- if (lastfgcolour != fgcolour) {
- lastfgcolour = fgcolour;
- rtflen += sprintf(&rtf[rtflen], "\\cf%d ", (fgcolour >= 0) ? palette[fgcolour] : 0);
- }
+ if ((lastfgcolour != fgcolour) || (lastfg != fg)) {
+ lastfgcolour = fgcolour;
+ lastfg = fg;
+ if (fg == -1)
+ rtflen += sprintf(&rtf[rtflen], "\\cf%d ",
+ (fgcolour >= 0) ? palette[fgcolour] : 0);
+ else {
+ rgbindex rgb, *rgbp;
+ rgb.ref = fg;
+ if ((rgbp = find234(rgbtree, &rgb, NULL)) != NULL)
+ rtflen += sprintf(&rtf[rtflen], "\\cf%d ", rgbp->index);
+ }
+ }
- if (lastbgcolour != bgcolour) {
- lastbgcolour = bgcolour;
- rtflen += sprintf(&rtf[rtflen], "\\highlight%d ", (bgcolour >= 0) ? palette[bgcolour] : 0);
- }
+ if ((lastbgcolour != bgcolour) || (lastbg != bg)) {
+ lastbgcolour = bgcolour;
+ lastbg = bg;
+ if (bg == -1)
+ rtflen += sprintf(&rtf[rtflen], "\\highlight%d ",
+ (bgcolour >= 0) ? palette[bgcolour] : 0);
+ else {
+ rgbindex rgb, *rgbp;
+ rgb.ref = bg;
+ if ((rgbp = find234(rgbtree, &rgb, NULL)) != NULL)
+ rtflen += sprintf(&rtf[rtflen], "\\highlight%d ", rgbp->index);
+ }
+ }
if (lastAttrBold != attrBold) {
lastAttrBold = attrBold;
@@ -5295,6 +5384,13 @@ void write_clip(void *frontend, wchar_t *data, int *attr, truecolour *truecolour
GlobalUnlock(clipdata3);
}
sfree(rtf);
+
+ if (rgbtree) {
+ rgbindex *rgbp;
+ while ((rgbp = delpos234(rgbtree, 0)) != NULL)
+ sfree(rgbp);
+ freetree234(rgbtree);
+ }
} else
clipdata3 = NULL;
From b4e5485caa1ba9286db3f51ba1b8a5498db52411 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 19 Oct 2017 18:27:03 +0100
Subject: [PATCH 087/607] Add Jeff Smith as a copyright holder.
---
LICENCE | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/LICENCE b/LICENCE
index 30b1fe2b..05fbb7d3 100644
--- a/LICENCE
+++ b/LICENCE
@@ -4,7 +4,7 @@ Portions copyright Robert de Bath, Joris van Rantwijk, Delian
Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian
-Brabandt, and CORE SDI S.A.
+Brabandt, Jeff Smith, and CORE SDI S.A.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
From ea5425939297e0f41de4be3168fbb8d68854bb64 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 19 Oct 2017 18:55:22 +0100
Subject: [PATCH 088/607] sshaes.c: fix file name in header comment.
Apparently I forgot to edit that when I originally imported this AES
implementation into PuTTY's SSH code from the more generically named
source file in which I'd originally developed it.
---
sshaes.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sshaes.c b/sshaes.c
index 904cbdb2..0368d6e9 100644
--- a/sshaes.c
+++ b/sshaes.c
@@ -1,5 +1,5 @@
/*
- * aes.c - implementation of AES / Rijndael
+ * sshaes.c - implementation of AES / Rijndael
*
* AES is a flexible algorithm as regards endianness: it has no
* inherent preference as to which way round you should form words
From 4dfadcfb2643fe3b54bb29277ca73bc5ba647208 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 19 Oct 2017 19:13:02 +0100
Subject: [PATCH 089/607] sshaes.c: remove completely unused #define MAX_NK.
---
sshaes.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/sshaes.c b/sshaes.c
index 0368d6e9..9be58dab 100644
--- a/sshaes.c
+++ b/sshaes.c
@@ -33,7 +33,6 @@
#include "ssh.h"
#define MAX_NR 14 /* max no of rounds */
-#define MAX_NK 8 /* max no of words in input key */
#define MAX_NB 8 /* max no of words in cipher blk */
#define mulby2(x) ( ((x&0x7F) << 1) ^ (x & 0x80 ? 0x1B : 0) )
From 5592312636988869517026b6b8ead0a0dfd45266 Mon Sep 17 00:00:00 2001
From: "Pavel I. Kryukov"
Date: Fri, 20 Oct 2017 19:13:21 +0100
Subject: [PATCH 090/607] AES: remove support for block sizes other than 128
bits.
They're not really part of AES at all, in that they were part of the
Rijndael design but not part of the subset standardised by NIST. More
relevantly, they're not used by any SSH cipher definition, so they're
just adding complexity to the code which is about to get in the way of
refactoring it.
Removing them means there's only one pair of core encrypt/decrypt
functions, so the 'encrypt' and 'decrypt' function pointer fields can
be completely removed from AESContext.
---
sshaes.c | 277 ++++++++-----------------------------------------------
1 file changed, 40 insertions(+), 237 deletions(-)
diff --git a/sshaes.c b/sshaes.c
index 9be58dab..48d0ca16 100644
--- a/sshaes.c
+++ b/sshaes.c
@@ -33,19 +33,17 @@
#include "ssh.h"
#define MAX_NR 14 /* max no of rounds */
-#define MAX_NB 8 /* max no of words in cipher blk */
+#define NB 4 /* no of words in cipher blk */
#define mulby2(x) ( ((x&0x7F) << 1) ^ (x & 0x80 ? 0x1B : 0) )
typedef struct AESContext AESContext;
struct AESContext {
- word32 keysched[(MAX_NR + 1) * MAX_NB];
- word32 invkeysched[(MAX_NR + 1) * MAX_NB];
- void (*encrypt) (AESContext * ctx, word32 * block);
- void (*decrypt) (AESContext * ctx, word32 * block);
- word32 iv[MAX_NB];
- int Nb, Nr;
+ word32 keysched[(MAX_NR + 1) * NB];
+ word32 invkeysched[(MAX_NR + 1) * NB];
+ word32 iv[NB];
+ int Nr; /* number of rounds */
};
static const unsigned char Sbox[256] = {
@@ -652,36 +650,28 @@ static const word32 D3[256] = {
*/
#define ADD_ROUND_KEY_4 (block[0]^=*keysched++, block[1]^=*keysched++, \
block[2]^=*keysched++, block[3]^=*keysched++)
-#define ADD_ROUND_KEY_6 (block[0]^=*keysched++, block[1]^=*keysched++, \
- block[2]^=*keysched++, block[3]^=*keysched++, \
- block[4]^=*keysched++, block[5]^=*keysched++)
-#define ADD_ROUND_KEY_8 (block[0]^=*keysched++, block[1]^=*keysched++, \
- block[2]^=*keysched++, block[3]^=*keysched++, \
- block[4]^=*keysched++, block[5]^=*keysched++, \
- block[6]^=*keysched++, block[7]^=*keysched++)
#define MOVEWORD(i) ( block[i] = newstate[i] )
/*
- * Macros for the encryption routine. There are three encryption
- * cores, for Nb=4,6,8.
+ * Macros for the encryption routine.
*/
#define MAKEWORD(i) ( newstate[i] = (E0[(block[i] >> 24) & 0xFF] ^ \
- E1[(block[(i+C1)%Nb] >> 16) & 0xFF] ^ \
- E2[(block[(i+C2)%Nb] >> 8) & 0xFF] ^ \
- E3[block[(i+C3)%Nb] & 0xFF]) )
+ E1[(block[(i+C1)%NB] >> 16) & 0xFF] ^ \
+ E2[(block[(i+C2)%NB] >> 8) & 0xFF] ^ \
+ E3[block[(i+C3)%NB] & 0xFF]) )
#define LASTWORD(i) ( newstate[i] = (Sbox[(block[i] >> 24) & 0xFF] << 24) | \
- (Sbox[(block[(i+C1)%Nb] >> 16) & 0xFF] << 16) | \
- (Sbox[(block[(i+C2)%Nb] >> 8) & 0xFF] << 8) | \
- (Sbox[(block[(i+C3)%Nb] ) & 0xFF] ) )
+ (Sbox[(block[(i+C1)%NB] >> 16) & 0xFF] << 16) | \
+ (Sbox[(block[(i+C2)%NB] >> 8) & 0xFF] << 8) | \
+ (Sbox[(block[(i+C3)%NB] ) & 0xFF] ) )
/*
- * Core encrypt routines, expecting word32 inputs read big-endian
+ * Core encrypt routine, expecting word32 inputs read big-endian
* from the byte-oriented input stream.
*/
-static void aes_encrypt_nb_4(AESContext * ctx, word32 * block)
+static void aes_encrypt(AESContext * ctx, word32 * block)
{
int i;
- static const int C1 = 1, C2 = 2, C3 = 3, Nb = 4;
+ static const int C1 = 1, C2 = 2, C3 = 3;
word32 *keysched = ctx->keysched;
word32 newstate[4];
for (i = 0; i < ctx->Nr - 1; i++) {
@@ -706,111 +696,30 @@ static void aes_encrypt_nb_4(AESContext * ctx, word32 * block)
MOVEWORD(3);
ADD_ROUND_KEY_4;
}
-static void aes_encrypt_nb_6(AESContext * ctx, word32 * block)
-{
- int i;
- static const int C1 = 1, C2 = 2, C3 = 3, Nb = 6;
- word32 *keysched = ctx->keysched;
- word32 newstate[6];
- for (i = 0; i < ctx->Nr - 1; i++) {
- ADD_ROUND_KEY_6;
- MAKEWORD(0);
- MAKEWORD(1);
- MAKEWORD(2);
- MAKEWORD(3);
- MAKEWORD(4);
- MAKEWORD(5);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- MOVEWORD(4);
- MOVEWORD(5);
- }
- ADD_ROUND_KEY_6;
- LASTWORD(0);
- LASTWORD(1);
- LASTWORD(2);
- LASTWORD(3);
- LASTWORD(4);
- LASTWORD(5);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- MOVEWORD(4);
- MOVEWORD(5);
- ADD_ROUND_KEY_6;
-}
-static void aes_encrypt_nb_8(AESContext * ctx, word32 * block)
-{
- int i;
- static const int C1 = 1, C2 = 3, C3 = 4, Nb = 8;
- word32 *keysched = ctx->keysched;
- word32 newstate[8];
- for (i = 0; i < ctx->Nr - 1; i++) {
- ADD_ROUND_KEY_8;
- MAKEWORD(0);
- MAKEWORD(1);
- MAKEWORD(2);
- MAKEWORD(3);
- MAKEWORD(4);
- MAKEWORD(5);
- MAKEWORD(6);
- MAKEWORD(7);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- MOVEWORD(4);
- MOVEWORD(5);
- MOVEWORD(6);
- MOVEWORD(7);
- }
- ADD_ROUND_KEY_8;
- LASTWORD(0);
- LASTWORD(1);
- LASTWORD(2);
- LASTWORD(3);
- LASTWORD(4);
- LASTWORD(5);
- LASTWORD(6);
- LASTWORD(7);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- MOVEWORD(4);
- MOVEWORD(5);
- MOVEWORD(6);
- MOVEWORD(7);
- ADD_ROUND_KEY_8;
-}
#undef MAKEWORD
#undef LASTWORD
/*
- * Macros for the decryption routine. There are three decryption
- * cores, for Nb=4,6,8.
+ * Macros for the decryption routine.
*/
#define MAKEWORD(i) ( newstate[i] = (D0[(block[i] >> 24) & 0xFF] ^ \
- D1[(block[(i+C1)%Nb] >> 16) & 0xFF] ^ \
- D2[(block[(i+C2)%Nb] >> 8) & 0xFF] ^ \
- D3[block[(i+C3)%Nb] & 0xFF]) )
+ D1[(block[(i+C1)%NB] >> 16) & 0xFF] ^ \
+ D2[(block[(i+C2)%NB] >> 8) & 0xFF] ^ \
+ D3[block[(i+C3)%NB] & 0xFF]) )
#define LASTWORD(i) (newstate[i] = (Sboxinv[(block[i] >> 24) & 0xFF] << 24) | \
- (Sboxinv[(block[(i+C1)%Nb] >> 16) & 0xFF] << 16) | \
- (Sboxinv[(block[(i+C2)%Nb] >> 8) & 0xFF] << 8) | \
- (Sboxinv[(block[(i+C3)%Nb] ) & 0xFF] ) )
+ (Sboxinv[(block[(i+C1)%NB] >> 16) & 0xFF] << 16) | \
+ (Sboxinv[(block[(i+C2)%NB] >> 8) & 0xFF] << 8) | \
+ (Sboxinv[(block[(i+C3)%NB] ) & 0xFF] ) )
/*
- * Core decrypt routines, expecting word32 inputs read big-endian
+ * Core decrypt routine, expecting word32 inputs read big-endian
* from the byte-oriented input stream.
*/
-static void aes_decrypt_nb_4(AESContext * ctx, word32 * block)
+static void aes_decrypt(AESContext * ctx, word32 * block)
{
int i;
- static const int C1 = 4 - 1, C2 = 4 - 2, C3 = 4 - 3, Nb = 4;
+ static const int C1 = 4 - 1, C2 = 4 - 2, C3 = 4 - 3;
word32 *keysched = ctx->invkeysched;
word32 newstate[4];
for (i = 0; i < ctx->Nr - 1; i++) {
@@ -835,126 +744,30 @@ static void aes_decrypt_nb_4(AESContext * ctx, word32 * block)
MOVEWORD(3);
ADD_ROUND_KEY_4;
}
-static void aes_decrypt_nb_6(AESContext * ctx, word32 * block)
-{
- int i;
- static const int C1 = 6 - 1, C2 = 6 - 2, C3 = 6 - 3, Nb = 6;
- word32 *keysched = ctx->invkeysched;
- word32 newstate[6];
- for (i = 0; i < ctx->Nr - 1; i++) {
- ADD_ROUND_KEY_6;
- MAKEWORD(0);
- MAKEWORD(1);
- MAKEWORD(2);
- MAKEWORD(3);
- MAKEWORD(4);
- MAKEWORD(5);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- MOVEWORD(4);
- MOVEWORD(5);
- }
- ADD_ROUND_KEY_6;
- LASTWORD(0);
- LASTWORD(1);
- LASTWORD(2);
- LASTWORD(3);
- LASTWORD(4);
- LASTWORD(5);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- MOVEWORD(4);
- MOVEWORD(5);
- ADD_ROUND_KEY_6;
-}
-static void aes_decrypt_nb_8(AESContext * ctx, word32 * block)
-{
- int i;
- static const int C1 = 8 - 1, C2 = 8 - 3, C3 = 8 - 4, Nb = 8;
- word32 *keysched = ctx->invkeysched;
- word32 newstate[8];
- for (i = 0; i < ctx->Nr - 1; i++) {
- ADD_ROUND_KEY_8;
- MAKEWORD(0);
- MAKEWORD(1);
- MAKEWORD(2);
- MAKEWORD(3);
- MAKEWORD(4);
- MAKEWORD(5);
- MAKEWORD(6);
- MAKEWORD(7);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- MOVEWORD(4);
- MOVEWORD(5);
- MOVEWORD(6);
- MOVEWORD(7);
- }
- ADD_ROUND_KEY_8;
- LASTWORD(0);
- LASTWORD(1);
- LASTWORD(2);
- LASTWORD(3);
- LASTWORD(4);
- LASTWORD(5);
- LASTWORD(6);
- LASTWORD(7);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- MOVEWORD(4);
- MOVEWORD(5);
- MOVEWORD(6);
- MOVEWORD(7);
- ADD_ROUND_KEY_8;
-}
#undef MAKEWORD
#undef LASTWORD
/*
- * Set up an AESContext. `keylen' and `blocklen' are measured in
- * bytes; each can be either 16 (128-bit), 24 (192-bit), or 32
+ * Set up an AESContext. `keylen' is measured in
+ * bytes; it can be either 16 (128-bit), 24 (192-bit), or 32
* (256-bit).
*/
-static void aes_setup(AESContext * ctx, int blocklen,
- unsigned char *key, int keylen)
+static void aes_setup(AESContext * ctx, unsigned char *key, int keylen)
{
int i, j, Nk, rconst;
- assert(blocklen == 16 || blocklen == 24 || blocklen == 32);
- assert(keylen == 16 || keylen == 24 || keylen == 32);
-
- /*
- * Basic parameters. Words per block, words in key, rounds.
- */
- Nk = keylen / 4;
- ctx->Nb = blocklen / 4;
- ctx->Nr = 6 + (ctx->Nb > Nk ? ctx->Nb : Nk);
+ ctx->Nr = 6 + (keylen / 4); /* Number of rounds */
- /*
- * Assign core-function pointers.
- */
- if (ctx->Nb == 8)
- ctx->encrypt = aes_encrypt_nb_8, ctx->decrypt = aes_decrypt_nb_8;
- else if (ctx->Nb == 6)
- ctx->encrypt = aes_encrypt_nb_6, ctx->decrypt = aes_decrypt_nb_6;
- else if (ctx->Nb == 4)
- ctx->encrypt = aes_encrypt_nb_4, ctx->decrypt = aes_decrypt_nb_4;
+ assert(keylen == 16 || keylen == 24 || keylen == 32);
/*
* Now do the key setup itself.
*/
+ Nk = keylen / 4;
rconst = 1;
- for (i = 0; i < (ctx->Nr + 1) * ctx->Nb; i++) {
+ for (i = 0; i < (ctx->Nr + 1) * NB; i++) {
if (i < Nk)
ctx->keysched[i] = GET_32BIT_MSB_FIRST(key + 4 * i);
else {
@@ -989,9 +802,9 @@ static void aes_setup(AESContext * ctx, int blocklen,
* Now prepare the modified keys for the inverse cipher.
*/
for (i = 0; i <= ctx->Nr; i++) {
- for (j = 0; j < ctx->Nb; j++) {
+ for (j = 0; j < NB; j++) {
word32 temp;
- temp = ctx->keysched[(ctx->Nr - i) * ctx->Nb + j];
+ temp = ctx->keysched[(ctx->Nr - i) * NB + j];
if (i != 0 && i != ctx->Nr) {
/*
* Perform the InvMixColumn operation on i. The D
@@ -1009,21 +822,11 @@ static void aes_setup(AESContext * ctx, int blocklen,
temp ^= D2[Sbox[c]];
temp ^= D3[Sbox[d]];
}
- ctx->invkeysched[i * ctx->Nb + j] = temp;
+ ctx->invkeysched[i * NB + j] = temp;
}
}
}
-static void aes_encrypt(AESContext * ctx, word32 * block)
-{
- ctx->encrypt(ctx, block);
-}
-
-static void aes_decrypt(AESContext * ctx, word32 * block)
-{
- ctx->decrypt(ctx, block);
-}
-
static void aes_encrypt_cbc(unsigned char *blk, int len, AESContext * ctx)
{
word32 iv[4];
@@ -1109,19 +912,19 @@ void aes_free_context(void *handle)
void aes128_key(void *handle, unsigned char *key)
{
AESContext *ctx = (AESContext *)handle;
- aes_setup(ctx, 16, key, 16);
+ aes_setup(ctx, key, 16);
}
void aes192_key(void *handle, unsigned char *key)
{
AESContext *ctx = (AESContext *)handle;
- aes_setup(ctx, 16, key, 24);
+ aes_setup(ctx, key, 24);
}
void aes256_key(void *handle, unsigned char *key)
{
AESContext *ctx = (AESContext *)handle;
- aes_setup(ctx, 16, key, 32);
+ aes_setup(ctx, key, 32);
}
void aes_iv(void *handle, unsigned char *iv)
@@ -1153,7 +956,7 @@ static void aes_ssh2_sdctr(void *handle, unsigned char *blk, int len)
void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len)
{
AESContext ctx;
- aes_setup(&ctx, 16, key, 32);
+ aes_setup(&ctx, key, 32);
memset(ctx.iv, 0, sizeof(ctx.iv));
aes_encrypt_cbc(blk, len, &ctx);
smemclr(&ctx, sizeof(ctx));
@@ -1162,7 +965,7 @@ void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len)
void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len)
{
AESContext ctx;
- aes_setup(&ctx, 16, key, 32);
+ aes_setup(&ctx, key, 32);
memset(ctx.iv, 0, sizeof(ctx.iv));
aes_decrypt_cbc(blk, len, &ctx);
smemclr(&ctx, sizeof(ctx));
From 0816e2b1a019e97b37eb6007c34f0c58c257635c Mon Sep 17 00:00:00 2001
From: "Pavel I. Kryukov"
Date: Fri, 20 Oct 2017 19:13:39 +0100
Subject: [PATCH 091/607] AES: fold the core and outer routines together.
The outer routines are the ones which handle the CBC encrypt, CBC
decrypt and SDCTR cipher modes. Previously each of those had to be
able to dispatch to one of the per-block-size core routines, which
made it worth dividing the system up into two layers. But now there's
only one set of core routines, they may as well be inlined into the
outer ones.
Also as part of this commit, the nasty undef/redef of MAKEWORD and
LASTWORD have been removed, and the different macro definitions now
have different macro _names_, to make it clearer which one is used
where.
---
sshaes.c | 277 ++++++++++++++++++++++++++++---------------------------
1 file changed, 142 insertions(+), 135 deletions(-)
diff --git a/sshaes.c b/sshaes.c
index 48d0ca16..44303184 100644
--- a/sshaes.c
+++ b/sshaes.c
@@ -645,110 +645,6 @@ static const word32 D3[256] = {
0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0,
};
-/*
- * Common macros in both the encryption and decryption routines.
- */
-#define ADD_ROUND_KEY_4 (block[0]^=*keysched++, block[1]^=*keysched++, \
- block[2]^=*keysched++, block[3]^=*keysched++)
-#define MOVEWORD(i) ( block[i] = newstate[i] )
-
-/*
- * Macros for the encryption routine.
- */
-#define MAKEWORD(i) ( newstate[i] = (E0[(block[i] >> 24) & 0xFF] ^ \
- E1[(block[(i+C1)%NB] >> 16) & 0xFF] ^ \
- E2[(block[(i+C2)%NB] >> 8) & 0xFF] ^ \
- E3[block[(i+C3)%NB] & 0xFF]) )
-#define LASTWORD(i) ( newstate[i] = (Sbox[(block[i] >> 24) & 0xFF] << 24) | \
- (Sbox[(block[(i+C1)%NB] >> 16) & 0xFF] << 16) | \
- (Sbox[(block[(i+C2)%NB] >> 8) & 0xFF] << 8) | \
- (Sbox[(block[(i+C3)%NB] ) & 0xFF] ) )
-
-/*
- * Core encrypt routine, expecting word32 inputs read big-endian
- * from the byte-oriented input stream.
- */
-static void aes_encrypt(AESContext * ctx, word32 * block)
-{
- int i;
- static const int C1 = 1, C2 = 2, C3 = 3;
- word32 *keysched = ctx->keysched;
- word32 newstate[4];
- for (i = 0; i < ctx->Nr - 1; i++) {
- ADD_ROUND_KEY_4;
- MAKEWORD(0);
- MAKEWORD(1);
- MAKEWORD(2);
- MAKEWORD(3);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- }
- ADD_ROUND_KEY_4;
- LASTWORD(0);
- LASTWORD(1);
- LASTWORD(2);
- LASTWORD(3);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- ADD_ROUND_KEY_4;
-}
-
-#undef MAKEWORD
-#undef LASTWORD
-
-/*
- * Macros for the decryption routine.
- */
-#define MAKEWORD(i) ( newstate[i] = (D0[(block[i] >> 24) & 0xFF] ^ \
- D1[(block[(i+C1)%NB] >> 16) & 0xFF] ^ \
- D2[(block[(i+C2)%NB] >> 8) & 0xFF] ^ \
- D3[block[(i+C3)%NB] & 0xFF]) )
-#define LASTWORD(i) (newstate[i] = (Sboxinv[(block[i] >> 24) & 0xFF] << 24) | \
- (Sboxinv[(block[(i+C1)%NB] >> 16) & 0xFF] << 16) | \
- (Sboxinv[(block[(i+C2)%NB] >> 8) & 0xFF] << 8) | \
- (Sboxinv[(block[(i+C3)%NB] ) & 0xFF] ) )
-
-/*
- * Core decrypt routine, expecting word32 inputs read big-endian
- * from the byte-oriented input stream.
- */
-static void aes_decrypt(AESContext * ctx, word32 * block)
-{
- int i;
- static const int C1 = 4 - 1, C2 = 4 - 2, C3 = 4 - 3;
- word32 *keysched = ctx->invkeysched;
- word32 newstate[4];
- for (i = 0; i < ctx->Nr - 1; i++) {
- ADD_ROUND_KEY_4;
- MAKEWORD(0);
- MAKEWORD(1);
- MAKEWORD(2);
- MAKEWORD(3);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- }
- ADD_ROUND_KEY_4;
- LASTWORD(0);
- LASTWORD(1);
- LASTWORD(2);
- LASTWORD(3);
- MOVEWORD(0);
- MOVEWORD(1);
- MOVEWORD(2);
- MOVEWORD(3);
- ADD_ROUND_KEY_4;
-}
-
-#undef MAKEWORD
-#undef LASTWORD
-
-
/*
* Set up an AESContext. `keylen' is measured in
* bytes; it can be either 16 (128-bit), 24 (192-bit), or 32
@@ -762,9 +658,6 @@ static void aes_setup(AESContext * ctx, unsigned char *key, int keylen)
assert(keylen == 16 || keylen == 24 || keylen == 32);
- /*
- * Now do the key setup itself.
- */
Nk = keylen / 4;
rconst = 1;
for (i = 0; i < (ctx->Nr + 1) * NB; i++) {
@@ -827,73 +720,187 @@ static void aes_setup(AESContext * ctx, unsigned char *key, int keylen)
}
}
+/*
+ * Software encrypt/decrypt macros
+ */
+#define ADD_ROUND_KEY (block[0]^=*keysched++, \
+ block[1]^=*keysched++, \
+ block[2]^=*keysched++, \
+ block[3]^=*keysched++)
+#define MOVEWORD(i) ( block[i] = newstate[i] )
+
+#define ENCWORD(i) ( newstate[i] = (E0[(block[i ] >> 24) & 0xFF] ^ \
+ E1[(block[(i+1)%NB] >> 16) & 0xFF] ^ \
+ E2[(block[(i+2)%NB] >> 8) & 0xFF] ^ \
+ E3[ block[(i+3)%NB] & 0xFF]) )
+#define ENCROUND { ENCWORD(0); ENCWORD(1); ENCWORD(2); ENCWORD(3); \
+ MOVEWORD(0); MOVEWORD(1); MOVEWORD(2); MOVEWORD(3); ADD_ROUND_KEY; }
+
+#define ENCLASTWORD(i) ( newstate[i] = \
+ (Sbox[(block[i] >> 24) & 0xFF] << 24) | \
+ (Sbox[(block[(i+1)%NB] >> 16) & 0xFF] << 16) | \
+ (Sbox[(block[(i+2)%NB] >> 8) & 0xFF] << 8) | \
+ (Sbox[(block[(i+3)%NB] ) & 0xFF] ) )
+#define ENCLASTROUND { ENCLASTWORD(0); ENCLASTWORD(1); ENCLASTWORD(2); ENCLASTWORD(3); \
+ MOVEWORD(0); MOVEWORD(1); MOVEWORD(2); MOVEWORD(3); ADD_ROUND_KEY; }
+
+#define DECWORD(i) ( newstate[i] = (D0[(block[i] >> 24) & 0xFF] ^ \
+ D1[(block[(i+3)%NB] >> 16) & 0xFF] ^ \
+ D2[(block[(i+2)%NB] >> 8) & 0xFF] ^ \
+ D3[ block[(i+1)%NB] & 0xFF]) )
+#define DECROUND { DECWORD(0); DECWORD(1); DECWORD(2); DECWORD(3); \
+ MOVEWORD(0); MOVEWORD(1); MOVEWORD(2); MOVEWORD(3); ADD_ROUND_KEY; }
+
+#define DECLASTWORD(i) (newstate[i] = \
+ (Sboxinv[(block[i] >> 24) & 0xFF] << 24) | \
+ (Sboxinv[(block[(i+3)%NB] >> 16) & 0xFF] << 16) | \
+ (Sboxinv[(block[(i+2)%NB] >> 8) & 0xFF] << 8) | \
+ (Sboxinv[(block[(i+1)%NB] ) & 0xFF] ) )
+#define DECLASTROUND { DECLASTWORD(0); DECLASTWORD(1); DECLASTWORD(2); DECLASTWORD(3); \
+ MOVEWORD(0); MOVEWORD(1); MOVEWORD(2); MOVEWORD(3); ADD_ROUND_KEY; }
+
+/*
+ * Software AES encrypt/decrypt core
+ */
static void aes_encrypt_cbc(unsigned char *blk, int len, AESContext * ctx)
{
- word32 iv[4];
+ word32 block[4];
+ unsigned char* finish = blk + len;
int i;
assert((len & 15) == 0);
- memcpy(iv, ctx->iv, sizeof(iv));
+ memcpy(block, ctx->iv, sizeof(block));
- while (len > 0) {
+ while (blk < finish) {
+ word32 *keysched = ctx->keysched;
+ word32 newstate[4];
for (i = 0; i < 4; i++)
- iv[i] ^= GET_32BIT_MSB_FIRST(blk + 4 * i);
- aes_encrypt(ctx, iv);
+ block[i] ^= GET_32BIT_MSB_FIRST(blk + 4 * i);
+ ADD_ROUND_KEY;
+ switch (ctx->Nr) {
+ case 14:
+ ENCROUND;
+ ENCROUND;
+ case 12:
+ ENCROUND;
+ ENCROUND;
+ case 10:
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCLASTROUND;
+ break;
+ default:
+ assert(0);
+ }
for (i = 0; i < 4; i++)
- PUT_32BIT_MSB_FIRST(blk + 4 * i, iv[i]);
+ PUT_32BIT_MSB_FIRST(blk + 4 * i, block[i]);
blk += 16;
- len -= 16;
}
- memcpy(ctx->iv, iv, sizeof(iv));
+ memcpy(ctx->iv, block, sizeof(block));
}
-static void aes_decrypt_cbc(unsigned char *blk, int len, AESContext * ctx)
+static void aes_sdctr(unsigned char *blk, int len, AESContext *ctx)
{
- word32 iv[4], x[4], ct[4];
+ word32 iv[4];
+ unsigned char* finish = blk + len;
int i;
assert((len & 15) == 0);
memcpy(iv, ctx->iv, sizeof(iv));
- while (len > 0) {
- for (i = 0; i < 4; i++)
- x[i] = ct[i] = GET_32BIT_MSB_FIRST(blk + 4 * i);
- aes_decrypt(ctx, x);
+ while (blk < finish) {
+ word32 *keysched = ctx->keysched;
+ word32 newstate[4], block[4], tmp;
+ memcpy(block, iv, sizeof(block));
+ ADD_ROUND_KEY;
+ switch (ctx->Nr) {
+ case 14:
+ ENCROUND;
+ ENCROUND;
+ case 12:
+ ENCROUND;
+ ENCROUND;
+ case 10:
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCROUND;
+ ENCLASTROUND;
+ break;
+ default:
+ assert(0);
+ }
for (i = 0; i < 4; i++) {
- PUT_32BIT_MSB_FIRST(blk + 4 * i, iv[i] ^ x[i]);
- iv[i] = ct[i];
+ tmp = GET_32BIT_MSB_FIRST(blk + 4 * i);
+ PUT_32BIT_MSB_FIRST(blk + 4 * i, tmp ^ block[i]);
}
+ for (i = 3; i >= 0; i--)
+ if ((iv[i] = (iv[i] + 1) & 0xffffffff) != 0)
+ break;
blk += 16;
- len -= 16;
}
memcpy(ctx->iv, iv, sizeof(iv));
}
-static void aes_sdctr(unsigned char *blk, int len, AESContext *ctx)
+static void aes_decrypt_cbc(unsigned char *blk, int len, AESContext * ctx)
{
- word32 iv[4], b[4], tmp;
+ word32 iv[4];
+ unsigned char* finish = blk + len;
int i;
assert((len & 15) == 0);
memcpy(iv, ctx->iv, sizeof(iv));
- while (len > 0) {
- memcpy(b, iv, sizeof(b));
- aes_encrypt(ctx, b);
+ while (blk < finish) {
+ word32 *keysched = ctx->invkeysched;
+ word32 newstate[4], ct[4], block[4];
+ for (i = 0; i < 4; i++)
+ block[i] = ct[i] = GET_32BIT_MSB_FIRST(blk + 4 * i);
+ ADD_ROUND_KEY;
+ switch (ctx->Nr) {
+ case 14:
+ DECROUND;
+ DECROUND;
+ case 12:
+ DECROUND;
+ DECROUND;
+ case 10:
+ DECROUND;
+ DECROUND;
+ DECROUND;
+ DECROUND;
+ DECROUND;
+ DECROUND;
+ DECROUND;
+ DECROUND;
+ DECROUND;
+ DECLASTROUND;
+ break;
+ default:
+ assert(0);
+ }
for (i = 0; i < 4; i++) {
- tmp = GET_32BIT_MSB_FIRST(blk + 4 * i);
- PUT_32BIT_MSB_FIRST(blk + 4 * i, tmp ^ b[i]);
+ PUT_32BIT_MSB_FIRST(blk + 4 * i, iv[i] ^ block[i]);
+ iv[i] = ct[i];
}
- for (i = 3; i >= 0; i--)
- if ((iv[i] = (iv[i] + 1) & 0xffffffff) != 0)
- break;
blk += 16;
- len -= 16;
}
memcpy(ctx->iv, iv, sizeof(iv));
From e8be7ea98a4c21a839dcd4b75dc62688778bdde8 Mon Sep 17 00:00:00 2001
From: "Pavel I. Kryukov"
Date: Fri, 20 Oct 2017 19:13:47 +0100
Subject: [PATCH 092/607] AES: 16-byte align the key schedule arrays.
This is going to be important in the next commit, when we start
accessing them using x86 SSE instructions.
---
sshaes.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/sshaes.c b/sshaes.c
index 44303184..488e32a3 100644
--- a/sshaes.c
+++ b/sshaes.c
@@ -40,8 +40,9 @@
typedef struct AESContext AESContext;
struct AESContext {
- word32 keysched[(MAX_NR + 1) * NB];
- word32 invkeysched[(MAX_NR + 1) * NB];
+ word32 keysched_buf[(MAX_NR + 1) * NB + 3];
+ word32 invkeysched_buf[(MAX_NR + 1) * NB + 3];
+ word32 *keysched, *invkeysched;
word32 iv[NB];
int Nr; /* number of rounds */
};
@@ -653,9 +654,20 @@ static const word32 D3[256] = {
static void aes_setup(AESContext * ctx, unsigned char *key, int keylen)
{
int i, j, Nk, rconst;
+ size_t bufaddr;
ctx->Nr = 6 + (keylen / 4); /* Number of rounds */
+ /* Ensure the key schedule arrays are 16-byte aligned */
+ bufaddr = (size_t)ctx->keysched_buf;
+ ctx->keysched = ctx->keysched_buf +
+ (0xF & -bufaddr) / sizeof(word32);
+ assert((size_t)ctx->keysched % 16 == 0);
+ bufaddr = (size_t)ctx->invkeysched_buf;
+ ctx->invkeysched = ctx->invkeysched_buf +
+ (0xF & -bufaddr) / sizeof(word32);
+ assert((size_t)ctx->invkeysched % 16 == 0);
+
assert(keylen == 16 || keylen == 24 || keylen == 32);
Nk = keylen / 4;
From 2d31305af9d3bf4096bb0c30e8a8336caaa70673 Mon Sep 17 00:00:00 2001
From: "Pavel I. Kryukov"
Date: Fri, 20 Oct 2017 19:13:54 +0100
Subject: [PATCH 093/607] Alternative AES routines, using x86 hardware support.
The new AES routines are compiled into the code on any platform where
the compiler can be made to generate the necessary AES-NI and SSE
instructions. But not every CPU will support those instructions, so
the pure-software routines haven't gone away: both sets of functions
sit side by side in the code, and at key setup time we check the CPUID
bitmap to decide which set to select.
(This reintroduces function pointers into AESContext, replacing the
ones that we managed to remove a few commits ago.)
---
sshaes.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 661 insertions(+), 6 deletions(-)
diff --git a/sshaes.c b/sshaes.c
index 488e32a3..7ebeb5be 100644
--- a/sshaes.c
+++ b/sshaes.c
@@ -37,6 +37,17 @@
#define mulby2(x) ( ((x&0x7F) << 1) ^ (x & 0x80 ? 0x1B : 0) )
+/*
+ * Select appropriate inline keyword for the compiler
+ */
+#if defined __GNUC__ || defined __clang__
+# define INLINE __inline__
+#elif defined (_MSC_VER)
+# define INLINE __forceinline
+#else
+# define INLINE
+#endif
+
typedef struct AESContext AESContext;
struct AESContext {
@@ -45,8 +56,37 @@ struct AESContext {
word32 *keysched, *invkeysched;
word32 iv[NB];
int Nr; /* number of rounds */
+ void (*encrypt_cbc)(unsigned char*, int, AESContext*);
+ void (*decrypt_cbc)(unsigned char*, int, AESContext*);
+ void (*sdctr)(unsigned char*, int, AESContext*);
+ int isNI;
};
+static void aes_encrypt_cbc_sw(unsigned char*, int, AESContext*);
+static void aes_decrypt_cbc_sw(unsigned char*, int, AESContext*);
+static void aes_sdctr_sw(unsigned char*, int, AESContext*);
+
+INLINE static int supports_aes_ni();
+static void aes_setup_ni(AESContext * ctx, unsigned char *key, int keylen);
+
+INLINE static void aes_encrypt_cbc(unsigned char *blk, int len, AESContext * ctx)
+{
+ ctx->encrypt_cbc(blk, len, ctx);
+}
+
+INLINE static void aes_decrypt_cbc(unsigned char *blk, int len, AESContext * ctx)
+{
+ ctx->decrypt_cbc(blk, len, ctx);
+}
+
+INLINE static void aes_sdctr(unsigned char *blk, int len, AESContext * ctx)
+{
+ ctx->sdctr(blk, len, ctx);
+}
+
+/*
+ * SW AES lookup tables
+ */
static const unsigned char Sbox[256] = {
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
@@ -668,8 +708,19 @@ static void aes_setup(AESContext * ctx, unsigned char *key, int keylen)
(0xF & -bufaddr) / sizeof(word32);
assert((size_t)ctx->invkeysched % 16 == 0);
+ ctx->isNI = supports_aes_ni();
+
+ if (ctx->isNI) {
+ aes_setup_ni(ctx, key, keylen);
+ return;
+ }
+
assert(keylen == 16 || keylen == 24 || keylen == 32);
+ ctx->encrypt_cbc = aes_encrypt_cbc_sw;
+ ctx->decrypt_cbc = aes_decrypt_cbc_sw;
+ ctx->sdctr = aes_sdctr_sw;
+
Nk = keylen / 4;
rconst = 1;
for (i = 0; i < (ctx->Nr + 1) * NB; i++) {
@@ -774,7 +825,7 @@ static void aes_setup(AESContext * ctx, unsigned char *key, int keylen)
/*
* Software AES encrypt/decrypt core
*/
-static void aes_encrypt_cbc(unsigned char *blk, int len, AESContext * ctx)
+static void aes_encrypt_cbc_sw(unsigned char *blk, int len, AESContext * ctx)
{
word32 block[4];
unsigned char* finish = blk + len;
@@ -820,7 +871,7 @@ static void aes_encrypt_cbc(unsigned char *blk, int len, AESContext * ctx)
memcpy(ctx->iv, block, sizeof(block));
}
-static void aes_sdctr(unsigned char *blk, int len, AESContext *ctx)
+static void aes_sdctr_sw(unsigned char *blk, int len, AESContext *ctx)
{
word32 iv[4];
unsigned char* finish = blk + len;
@@ -870,7 +921,7 @@ static void aes_sdctr(unsigned char *blk, int len, AESContext *ctx)
memcpy(ctx->iv, iv, sizeof(iv));
}
-static void aes_decrypt_cbc(unsigned char *blk, int len, AESContext * ctx)
+static void aes_decrypt_cbc_sw(unsigned char *blk, int len, AESContext * ctx)
{
word32 iv[4];
unsigned char* finish = blk + len;
@@ -949,9 +1000,14 @@ void aes256_key(void *handle, unsigned char *key)
void aes_iv(void *handle, unsigned char *iv)
{
AESContext *ctx = (AESContext *)handle;
- int i;
- for (i = 0; i < 4; i++)
- ctx->iv[i] = GET_32BIT_MSB_FIRST(iv + 4 * i);
+ if (ctx->isNI) {
+ memcpy(ctx->iv, iv, sizeof(ctx->iv));
+ }
+ else {
+ int i;
+ for (i = 0; i < 4; i++)
+ ctx->iv[i] = GET_32BIT_MSB_FIRST(iv + 4 * i);
+ }
}
void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)
@@ -1060,3 +1116,602 @@ const struct ssh2_ciphers ssh2_aes = {
sizeof(aes_list) / sizeof(*aes_list),
aes_list
};
+
+/*
+ * Implementation of AES for PuTTY using AES-NI
+ * instuction set expansion was made by:
+ * @author Pavel Kryukov
+ * @author Maxim Kuznetsov
+ * @author Svyatoslav Kuzmich
+ *
+ * For Putty AES NI project
+ * http://pavelkryukov.github.io/putty-aes-ni/
+ */
+
+/*
+ * Check of compiler version
+ */
+#ifdef _FORCE_AES_NI
+# define COMPILER_SUPPORTS_AES_NI
+#elif defined(__clang__)
+# if (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 8)) && (defined(__x86_64__) || defined(__i386))
+# define COMPILER_SUPPORTS_AES_NI
+# endif
+#elif defined(__GNUC__)
+# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) && (defined(__x86_64__) || defined(__i386))
+# define COMPILER_SUPPORTS_AES_NI
+# endif
+#elif defined (_MSC_VER)
+# if (defined(_M_X64) || defined(_M_IX86)) && _MSC_FULL_VER >= 150030729
+# define COMPILER_SUPPORTS_AES_NI
+# endif
+#endif
+
+#ifdef _FORCE_SOFTWARE_AES
+# undef COMPILER_SUPPORTS_AES_NI
+#endif
+
+#ifdef COMPILER_SUPPORTS_AES_NI
+
+/*
+ * Set target architecture for Clang and GCC
+ */
+#if !defined(__clang__) && defined(__GNUC__)
+# pragma GCC target("aes")
+# pragma GCC target("sse4.1")
+#endif
+
+#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
+# define FUNC_ISA __attribute__ ((target("sse4.1,aes")))
+#else
+# define FUNC_ISA
+#endif
+
+#include
+#include
+
+/*
+ * Determinators of CPU type
+ */
+#if defined(__clang__) || defined(__GNUC__)
+
+#include
+INLINE static int supports_aes_ni()
+{
+ unsigned int CPUInfo[4];
+ __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
+ return (CPUInfo[2] & (1 << 25)) && (CPUInfo[2] & (1 << 19)); /* Check AES and SSE4.1 */
+}
+
+#else /* defined(__clang__) || defined(__GNUC__) */
+
+INLINE static int supports_aes_ni()
+{
+ unsigned int CPUInfo[4];
+ __cpuid(CPUInfo, 1);
+ return (CPUInfo[2] & (1 << 25)) && (CPUInfo[2] & (1 << 19)); /* Check AES and SSE4.1 */
+}
+
+#endif /* defined(__clang__) || defined(__GNUC__) */
+
+/*
+ * Wrapper of SHUFPD instruction for MSVC
+ */
+#ifdef _MSC_VER
+INLINE static __m128i mm_shuffle_pd_i0(__m128i a, __m128i b)
+{
+ union {
+ __m128i i;
+ __m128d d;
+ } au, bu, ru;
+ au.i = a;
+ bu.i = b;
+ ru.d = _mm_shuffle_pd(au.d, bu.d, 0);
+ return ru.i;
+}
+
+INLINE static __m128i mm_shuffle_pd_i1(__m128i a, __m128i b)
+{
+ union {
+ __m128i i;
+ __m128d d;
+ } au, bu, ru;
+ au.i = a;
+ bu.i = b;
+ ru.d = _mm_shuffle_pd(au.d, bu.d, 1);
+ return ru.i;
+}
+#else
+#define mm_shuffle_pd_i0(a, b) ((__m128i)_mm_shuffle_pd((__m128d)a, (__m128d)b, 0));
+#define mm_shuffle_pd_i1(a, b) ((__m128i)_mm_shuffle_pd((__m128d)a, (__m128d)b, 1));
+#endif
+
+/*
+ * AES-NI key expansion assist functions
+ */
+FUNC_ISA
+INLINE static __m128i AES_128_ASSIST (__m128i temp1, __m128i temp2)
+{
+ __m128i temp3;
+ temp2 = _mm_shuffle_epi32 (temp2 ,0xff);
+ temp3 = _mm_slli_si128 (temp1, 0x4);
+ temp1 = _mm_xor_si128 (temp1, temp3);
+ temp3 = _mm_slli_si128 (temp3, 0x4);
+ temp1 = _mm_xor_si128 (temp1, temp3);
+ temp3 = _mm_slli_si128 (temp3, 0x4);
+ temp1 = _mm_xor_si128 (temp1, temp3);
+ temp1 = _mm_xor_si128 (temp1, temp2);
+ return temp1;
+}
+
+FUNC_ISA
+INLINE static void KEY_192_ASSIST(__m128i* temp1, __m128i * temp2, __m128i * temp3)
+{
+ __m128i temp4;
+ *temp2 = _mm_shuffle_epi32 (*temp2, 0x55);
+ temp4 = _mm_slli_si128 (*temp1, 0x4);
+ *temp1 = _mm_xor_si128 (*temp1, temp4);
+ temp4 = _mm_slli_si128 (temp4, 0x4);
+ *temp1 = _mm_xor_si128 (*temp1, temp4);
+ temp4 = _mm_slli_si128 (temp4, 0x4);
+ *temp1 = _mm_xor_si128 (*temp1, temp4);
+ *temp1 = _mm_xor_si128 (*temp1, *temp2);
+ *temp2 = _mm_shuffle_epi32(*temp1, 0xff);
+ temp4 = _mm_slli_si128 (*temp3, 0x4);
+ *temp3 = _mm_xor_si128 (*temp3, temp4);
+ *temp3 = _mm_xor_si128 (*temp3, *temp2);
+}
+
+FUNC_ISA
+INLINE static void KEY_256_ASSIST_1(__m128i* temp1, __m128i * temp2)
+{
+ __m128i temp4;
+ *temp2 = _mm_shuffle_epi32(*temp2, 0xff);
+ temp4 = _mm_slli_si128 (*temp1, 0x4);
+ *temp1 = _mm_xor_si128 (*temp1, temp4);
+ temp4 = _mm_slli_si128 (temp4, 0x4);
+ *temp1 = _mm_xor_si128 (*temp1, temp4);
+ temp4 = _mm_slli_si128 (temp4, 0x4);
+ *temp1 = _mm_xor_si128 (*temp1, temp4);
+ *temp1 = _mm_xor_si128 (*temp1, *temp2);
+}
+
+FUNC_ISA
+INLINE static void KEY_256_ASSIST_2(__m128i* temp1, __m128i * temp3)
+{
+ __m128i temp2,temp4;
+ temp4 = _mm_aeskeygenassist_si128 (*temp1, 0x0);
+ temp2 = _mm_shuffle_epi32(temp4, 0xaa);
+ temp4 = _mm_slli_si128 (*temp3, 0x4);
+ *temp3 = _mm_xor_si128 (*temp3, temp4);
+ temp4 = _mm_slli_si128 (temp4, 0x4);
+ *temp3 = _mm_xor_si128 (*temp3, temp4);
+ temp4 = _mm_slli_si128 (temp4, 0x4);
+ *temp3 = _mm_xor_si128 (*temp3, temp4);
+ *temp3 = _mm_xor_si128 (*temp3, temp2);
+}
+
+/*
+ * AES-NI key expansion core
+ */
+FUNC_ISA
+static void AES_128_Key_Expansion (unsigned char *userkey, __m128i *key)
+{
+ __m128i temp1, temp2;
+ temp1 = _mm_loadu_si128((__m128i*)userkey);
+ key[0] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1 ,0x1);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[1] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1,0x2);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[2] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1,0x4);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[3] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1,0x8);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[4] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1,0x10);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[5] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1,0x20);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[6] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1,0x40);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[7] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1,0x80);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[8] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1,0x1b);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[9] = temp1;
+ temp2 = _mm_aeskeygenassist_si128 (temp1,0x36);
+ temp1 = AES_128_ASSIST(temp1, temp2);
+ key[10] = temp1;
+}
+
+FUNC_ISA
+static void AES_192_Key_Expansion (unsigned char *userkey, __m128i *key)
+{
+ __m128i temp1, temp2, temp3;
+ temp1 = _mm_loadu_si128((__m128i*)userkey);
+ temp3 = _mm_loadu_si128((__m128i*)(userkey+16));
+ key[0]=temp1;
+ key[1]=temp3;
+ temp2=_mm_aeskeygenassist_si128 (temp3,0x1);
+ KEY_192_ASSIST(&temp1, &temp2, &temp3);
+ key[1] = mm_shuffle_pd_i0(key[1], temp1);
+ key[2] = mm_shuffle_pd_i1(temp1, temp3);
+ temp2=_mm_aeskeygenassist_si128 (temp3,0x2);
+ KEY_192_ASSIST(&temp1, &temp2, &temp3);
+ key[3]=temp1;
+ key[4]=temp3;
+ temp2=_mm_aeskeygenassist_si128 (temp3,0x4);
+ KEY_192_ASSIST(&temp1, &temp2, &temp3);
+ key[4] = mm_shuffle_pd_i0(key[4], temp1);
+ key[5] = mm_shuffle_pd_i1(temp1, temp3);
+ temp2=_mm_aeskeygenassist_si128 (temp3,0x8);
+ KEY_192_ASSIST(&temp1, &temp2, &temp3);
+ key[6]=temp1;
+ key[7]=temp3;
+ temp2=_mm_aeskeygenassist_si128 (temp3,0x10);
+ KEY_192_ASSIST(&temp1, &temp2, &temp3);
+ key[7] = mm_shuffle_pd_i0(key[7], temp1);
+ key[8] = mm_shuffle_pd_i1(temp1, temp3);
+ temp2=_mm_aeskeygenassist_si128 (temp3,0x20);
+ KEY_192_ASSIST(&temp1, &temp2, &temp3);
+ key[9]=temp1;
+ key[10]=temp3;
+ temp2=_mm_aeskeygenassist_si128 (temp3,0x40);
+ KEY_192_ASSIST(&temp1, &temp2, &temp3);
+ key[10] = mm_shuffle_pd_i0(key[10], temp1);
+ key[11] = mm_shuffle_pd_i1(temp1, temp3);
+ temp2=_mm_aeskeygenassist_si128 (temp3,0x80);
+ KEY_192_ASSIST(&temp1, &temp2, &temp3);
+ key[12]=temp1;
+ key[13]=temp3;
+}
+
+FUNC_ISA
+static void AES_256_Key_Expansion (unsigned char *userkey, __m128i *key)
+{
+ __m128i temp1, temp2, temp3;
+ temp1 = _mm_loadu_si128((__m128i*)userkey);
+ temp3 = _mm_loadu_si128((__m128i*)(userkey+16));
+ key[0] = temp1;
+ key[1] = temp3;
+ temp2 = _mm_aeskeygenassist_si128 (temp3,0x01);
+ KEY_256_ASSIST_1(&temp1, &temp2);
+ key[2]=temp1;
+ KEY_256_ASSIST_2(&temp1, &temp3);
+ key[3]=temp3;
+ temp2 = _mm_aeskeygenassist_si128 (temp3,0x02);
+ KEY_256_ASSIST_1(&temp1, &temp2);
+ key[4]=temp1;
+ KEY_256_ASSIST_2(&temp1, &temp3);
+ key[5]=temp3;
+ temp2 = _mm_aeskeygenassist_si128 (temp3,0x04);
+ KEY_256_ASSIST_1(&temp1, &temp2);
+ key[6]=temp1;
+ KEY_256_ASSIST_2(&temp1, &temp3);
+ key[7]=temp3;
+ temp2 = _mm_aeskeygenassist_si128 (temp3,0x08);
+ KEY_256_ASSIST_1(&temp1, &temp2);
+ key[8]=temp1;
+ KEY_256_ASSIST_2(&temp1, &temp3);
+ key[9]=temp3;
+ temp2 = _mm_aeskeygenassist_si128 (temp3,0x10);
+ KEY_256_ASSIST_1(&temp1, &temp2);
+ key[10]=temp1;
+ KEY_256_ASSIST_2(&temp1, &temp3);
+ key[11]=temp3;
+ temp2 = _mm_aeskeygenassist_si128 (temp3,0x20);
+ KEY_256_ASSIST_1(&temp1, &temp2);
+ key[12]=temp1;
+ KEY_256_ASSIST_2(&temp1, &temp3);
+ key[13]=temp3;
+ temp2 = _mm_aeskeygenassist_si128 (temp3,0x40);
+ KEY_256_ASSIST_1(&temp1, &temp2);
+ key[14]=temp1;
+}
+
+/*
+ * AES-NI encrypt/decrypt core
+ */
+FUNC_ISA
+static void aes_encrypt_cbc_ni(unsigned char *blk, int len, AESContext * ctx)
+{
+ __m128i enc;
+ __m128i* block = (__m128i*)blk;
+ const __m128i* finish = (__m128i*)(blk + len);
+
+ assert((len & 15) == 0);
+
+ /* Load IV */
+ enc = _mm_loadu_si128((__m128i*)(ctx->iv));
+ while (block < finish) {
+ /* Key schedule ptr */
+ __m128i* keysched = (__m128i*)ctx->keysched;
+
+ /* Xor data with IV */
+ enc = _mm_xor_si128(_mm_loadu_si128(block), enc);
+
+ /* Perform rounds */
+ enc = _mm_xor_si128(enc, *keysched);
+ switch (ctx->Nr) {
+ case 14:
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ case 12:
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ case 10:
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenclast_si128(enc, *(++keysched));
+ break;
+ default:
+ assert(0);
+ }
+
+ /* Store and go to next block */
+ _mm_storeu_si128(block, enc);
+ ++block;
+ }
+
+ /* Update IV */
+ _mm_storeu_si128((__m128i*)(ctx->iv), enc);
+}
+
+FUNC_ISA
+static void aes_decrypt_cbc_ni(unsigned char *blk, int len, AESContext * ctx)
+{
+ __m128i dec = _mm_setzero_si128();
+ __m128i last, iv;
+ __m128i* block = (__m128i*)blk;
+ const __m128i* finish = (__m128i*)(blk + len);
+
+ assert((len & 15) == 0);
+
+ /* Load IV */
+ iv = _mm_loadu_si128((__m128i*)(ctx->iv));
+ while (block < finish) {
+ /* Key schedule ptr */
+ __m128i* keysched = (__m128i*)ctx->invkeysched;
+ last = _mm_loadu_si128(block);
+ dec = _mm_xor_si128(last, *keysched);
+ switch (ctx->Nr) {
+ case 14:
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ case 12:
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ case 10:
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdec_si128(dec, *(++keysched));
+ dec = _mm_aesdeclast_si128(dec, *(++keysched));
+ break;
+ default:
+ assert(0);
+ }
+
+ /* Xor data with IV */
+ dec = _mm_xor_si128(iv, dec);
+
+ /* Store data */
+ _mm_storeu_si128(block, dec);
+ iv = last;
+
+ /* Go to next block */
+ ++block;
+ }
+
+ /* Update IV */
+ _mm_storeu_si128((__m128i*)(ctx->iv), dec);
+}
+
+FUNC_ISA
+static void aes_sdctr_ni(unsigned char *blk, int len, AESContext *ctx)
+{
+ const __m128i BSWAP_EPI64 = _mm_setr_epi8(3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12);
+ const __m128i ONE = _mm_setr_epi32(0,0,0,1);
+ const __m128i ZERO = _mm_setzero_si128();
+ __m128i iv;
+ __m128i* block = (__m128i*)blk;
+ const __m128i* finish = (__m128i*)(blk + len);
+
+ assert((len & 15) == 0);
+
+ iv = _mm_loadu_si128((__m128i*)ctx->iv);
+
+ while (block < finish) {
+ __m128i enc;
+ __m128i* keysched = (__m128i*)ctx->keysched;/* Key schedule ptr */
+
+ /* Perform rounds */
+ enc = _mm_xor_si128(iv, *keysched); /* Note that we use IV */
+ switch (ctx->Nr) {
+ case 14:
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ case 12:
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ case 10:
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenc_si128(enc, *(++keysched));
+ enc = _mm_aesenclast_si128(enc, *(++keysched));
+ break;
+ default:
+ assert(0);
+ }
+
+ /* Xor with block and store result */
+ enc = _mm_xor_si128(enc, _mm_loadu_si128(block));
+ _mm_storeu_si128(block, enc);
+
+ /* Increment of IV */
+ iv = _mm_shuffle_epi8(iv, BSWAP_EPI64); /* Swap endianess */
+ iv = _mm_add_epi64(iv, ONE); /* Inc low part */
+ enc = _mm_cmpeq_epi64(iv, ZERO); /* Check for carry */
+ enc = _mm_unpacklo_epi64(ZERO, enc); /* Pack carry reg */
+ iv = _mm_sub_epi64(iv, enc); /* Sub carry reg */
+ iv = _mm_shuffle_epi8(iv, BSWAP_EPI64); /* Swap enianess back */
+
+ /* Go to next block */
+ ++block;
+ }
+
+ /* Update IV */
+ _mm_storeu_si128((__m128i*)ctx->iv, iv);
+}
+
+FUNC_ISA
+static void aes_inv_key_10(AESContext * ctx)
+{
+ __m128i* keysched = (__m128i*)ctx->keysched;
+ __m128i* invkeysched = (__m128i*)ctx->invkeysched;
+
+ *(invkeysched + 10) = *(keysched + 0);
+ *(invkeysched + 9) = _mm_aesimc_si128(*(keysched + 1));
+ *(invkeysched + 8) = _mm_aesimc_si128(*(keysched + 2));
+ *(invkeysched + 7) = _mm_aesimc_si128(*(keysched + 3));
+ *(invkeysched + 6) = _mm_aesimc_si128(*(keysched + 4));
+ *(invkeysched + 5) = _mm_aesimc_si128(*(keysched + 5));
+ *(invkeysched + 4) = _mm_aesimc_si128(*(keysched + 6));
+ *(invkeysched + 3) = _mm_aesimc_si128(*(keysched + 7));
+ *(invkeysched + 2) = _mm_aesimc_si128(*(keysched + 8));
+ *(invkeysched + 1) = _mm_aesimc_si128(*(keysched + 9));
+ *(invkeysched + 0) = *(keysched + 10);
+}
+
+FUNC_ISA
+static void aes_inv_key_12(AESContext * ctx)
+{
+ __m128i* keysched = (__m128i*)ctx->keysched;
+ __m128i* invkeysched = (__m128i*)ctx->invkeysched;
+
+ *(invkeysched + 12) = *(keysched + 0);
+ *(invkeysched + 11) = _mm_aesimc_si128(*(keysched + 1));
+ *(invkeysched + 10) = _mm_aesimc_si128(*(keysched + 2));
+ *(invkeysched + 9) = _mm_aesimc_si128(*(keysched + 3));
+ *(invkeysched + 8) = _mm_aesimc_si128(*(keysched + 4));
+ *(invkeysched + 7) = _mm_aesimc_si128(*(keysched + 5));
+ *(invkeysched + 6) = _mm_aesimc_si128(*(keysched + 6));
+ *(invkeysched + 5) = _mm_aesimc_si128(*(keysched + 7));
+ *(invkeysched + 4) = _mm_aesimc_si128(*(keysched + 8));
+ *(invkeysched + 3) = _mm_aesimc_si128(*(keysched + 9));
+ *(invkeysched + 2) = _mm_aesimc_si128(*(keysched + 10));
+ *(invkeysched + 1) = _mm_aesimc_si128(*(keysched + 11));
+ *(invkeysched + 0) = *(keysched + 12);
+}
+
+FUNC_ISA
+static void aes_inv_key_14(AESContext * ctx)
+{
+ __m128i* keysched = (__m128i*)ctx->keysched;
+ __m128i* invkeysched = (__m128i*)ctx->invkeysched;
+
+ *(invkeysched + 14) = *(keysched + 0);
+ *(invkeysched + 13) = _mm_aesimc_si128(*(keysched + 1));
+ *(invkeysched + 12) = _mm_aesimc_si128(*(keysched + 2));
+ *(invkeysched + 11) = _mm_aesimc_si128(*(keysched + 3));
+ *(invkeysched + 10) = _mm_aesimc_si128(*(keysched + 4));
+ *(invkeysched + 9) = _mm_aesimc_si128(*(keysched + 5));
+ *(invkeysched + 8) = _mm_aesimc_si128(*(keysched + 6));
+ *(invkeysched + 7) = _mm_aesimc_si128(*(keysched + 7));
+ *(invkeysched + 6) = _mm_aesimc_si128(*(keysched + 8));
+ *(invkeysched + 5) = _mm_aesimc_si128(*(keysched + 9));
+ *(invkeysched + 4) = _mm_aesimc_si128(*(keysched + 10));
+ *(invkeysched + 3) = _mm_aesimc_si128(*(keysched + 11));
+ *(invkeysched + 2) = _mm_aesimc_si128(*(keysched + 12));
+ *(invkeysched + 1) = _mm_aesimc_si128(*(keysched + 13));
+ *(invkeysched + 0) = *(keysched + 14);
+}
+
+/*
+ * Set up an AESContext. `keylen' is measured in
+ * bytes; it can be either 16 (128-bit), 24 (192-bit), or 32
+ * (256-bit).
+ */
+FUNC_ISA
+static void aes_setup_ni(AESContext * ctx, unsigned char *key, int keylen)
+{
+ __m128i *keysched = (__m128i*)ctx->keysched;
+
+ ctx->encrypt_cbc = aes_encrypt_cbc_ni;
+ ctx->decrypt_cbc = aes_decrypt_cbc_ni;
+ ctx->sdctr = aes_sdctr_ni;
+
+ /*
+ * Now do the key setup itself.
+ */
+ switch (keylen) {
+ case 16:
+ AES_128_Key_Expansion (key, keysched);
+ break;
+ case 24:
+ AES_192_Key_Expansion (key, keysched);
+ break;
+ case 32:
+ AES_256_Key_Expansion (key, keysched);
+ break;
+ default:
+ assert(0);
+ }
+
+ /*
+ * Now prepare the modified keys for the inverse cipher.
+ */
+ switch (ctx->Nr) {
+ case 10:
+ aes_inv_key_10(ctx);
+ break;
+ case 12:
+ aes_inv_key_12(ctx);
+ break;
+ case 14:
+ aes_inv_key_14(ctx);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+#else /* COMPILER_SUPPORTS_AES_NI */
+
+static void aes_setup_ni(AESContext * ctx, unsigned char *key, int keylen)
+{
+ assert(0);
+}
+
+INLINE static int supports_aes_ni()
+{
+ return 0;
+}
+
+#endif /* COMPILER_SUPPORTS_AES_NI */
From 0a0a1c01d7cc6ca750d39f43b902e86987c5d376 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 20 Oct 2017 19:14:41 +0100
Subject: [PATCH 094/607] Additional copyright holders, from the AES-NI work.
---
LICENCE | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/LICENCE b/LICENCE
index 05fbb7d3..c473a6a4 100644
--- a/LICENCE
+++ b/LICENCE
@@ -4,7 +4,8 @@ Portions copyright Robert de Bath, Joris van Rantwijk, Delian
Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian
-Brabandt, Jeff Smith, and CORE SDI S.A.
+Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav
+Kuzmich, and CORE SDI S.A.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
From 4d15d46473907515d31cd0dc92b3ce86e5c5cb1e Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 26 Nov 2017 08:45:19 +0000
Subject: [PATCH 095/607] Memory leak: free conn->retbuf in uxagentc.c.
While debugging some new code, I ran valgrind in leak-checking mode
and it pointed out a handful of existing memory leaks, which got in the
way of spotting any _new_ leaks I might be introducing :-)
This was one: in the case where an asynchronous agent query on Unix is
aborted, the dynamically allocated buffer holding the response was not
freed.
---
unix/uxagentc.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/unix/uxagentc.c b/unix/uxagentc.c
index 51f9a1eb..2c748343 100644
--- a/unix/uxagentc.c
+++ b/unix/uxagentc.c
@@ -95,6 +95,8 @@ void agent_cancel_query(agent_pending_query *conn)
uxsel_del(conn->fd);
close(conn->fd);
del234(agent_pending_queries, conn);
+ if (conn->retbuf && conn->retbuf != conn->sizebuf)
+ sfree(conn->retbuf);
sfree(conn);
}
@@ -114,11 +116,12 @@ static void agent_select_result(int fd, int event)
return; /* more data to come */
/*
- * We have now completed the agent query. Do the callback, and
- * clean up. (Of course we don't free retbuf, since ownership
- * of that passes to the callback.)
+ * We have now completed the agent query. Do the callback.
*/
conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen);
+ /* Null out conn->retbuf, since ownership of that buffer has
+ * passed to the callback. */
+ conn->retbuf = NULL;
agent_cancel_query(conn);
}
From 90a402c017b6b262b0b2181cf193dd8188fac5c7 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 26 Nov 2017 08:45:37 +0000
Subject: [PATCH 096/607] Memory leak: free term->answerback in term_free().
Not a large leak as these things go, but valgrind's error dump for a
memory leak is just as annoying regardless of the size of the leaked
object!
---
terminal.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/terminal.c b/terminal.c
index d32d4f42..d5d5314a 100644
--- a/terminal.c
+++ b/terminal.c
@@ -1739,6 +1739,7 @@ void term_free(Terminal *term)
sfree(term->ltemp);
sfree(term->wcFrom);
sfree(term->wcTo);
+ sfree(term->answerback);
for (i = 0; i < term->bidi_cache_size; i++) {
sfree(term->pre_bidi_cache[i].chars);
From f1eeeff8cfea1001a1791ba95782ba7bbed3b976 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 26 Nov 2017 08:45:45 +0000
Subject: [PATCH 097/607] Memory leak: add a columns_finalize() method.
My custom GTK layout class 'Columns' includes a linked list of
dynamically allocated data, and apparently I forgot to write a
destructor that frees it all when the class is deallocated, and have
never noticed until now.
---
unix/gtkcols.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 53 insertions(+), 2 deletions(-)
diff --git a/unix/gtkcols.c b/unix/gtkcols.c
index e8223a72..ef060d63 100644
--- a/unix/gtkcols.c
+++ b/unix/gtkcols.c
@@ -8,6 +8,11 @@
static void columns_init(Columns *cols);
static void columns_class_init(ColumnsClass *klass);
+#if !GTK_CHECK_VERSION(2,0,0)
+static void columns_finalize(GtkObject *object);
+#else
+static void columns_finalize(GObject *object);
+#endif
static void columns_map(GtkWidget *widget);
static void columns_unmap(GtkWidget *widget);
#if !GTK_CHECK_VERSION(2,0,0)
@@ -96,11 +101,11 @@ static gint (*columns_inherited_focus)(GtkContainer *container,
static void columns_class_init(ColumnsClass *klass)
{
#if !GTK_CHECK_VERSION(2,0,0)
- /* GtkObjectClass *object_class = (GtkObjectClass *)klass; */
+ GtkObjectClass *object_class = (GtkObjectClass *)klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
GtkContainerClass *container_class = (GtkContainerClass *)klass;
#else
- /* GObjectClass *object_class = G_OBJECT_CLASS(klass); */
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
#endif
@@ -111,6 +116,7 @@ static void columns_class_init(ColumnsClass *klass)
parent_class = g_type_class_peek_parent(klass);
#endif
+ object_class->finalize = columns_finalize;
widget_class->map = columns_map;
widget_class->unmap = columns_unmap;
#if !GTK_CHECK_VERSION(2,0,0)
@@ -149,6 +155,50 @@ static void columns_init(Columns *cols)
cols->spacing = 0;
}
+static void columns_child_free(gpointer vchild)
+{
+ ColumnsChild *child = (ColumnsChild *)vchild;
+ if (child->percentages)
+ g_free(child->percentages);
+ g_free(child);
+}
+
+static void columns_finalize(
+#if !GTK_CHECK_VERSION(2,0,0)
+ GtkObject *object
+#else
+ GObject *object
+#endif
+ )
+{
+ Columns *cols;
+
+ g_return_if_fail(object != NULL);
+ g_return_if_fail(IS_COLUMNS(object));
+
+ cols = COLUMNS(object);
+
+#if !GTK_CHECK_VERSION(2,0,0)
+ {
+ GList *node;
+ for (node = cols->children; node; node = node->next)
+ if (node->data)
+ columns_child_free(node->data);
+ }
+ g_list_free(cols->children);
+#else
+ g_list_free_full(cols->children, columns_child_free);
+#endif
+
+ cols->children = NULL;
+
+#if !GTK_CHECK_VERSION(2,0,0)
+ GTK_OBJECT_CLASS(parent_class)->finalize(object);
+#else
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+#endif
+}
+
/*
* These appear to be thoroughly tedious functions; the only reason
* we have to reimplement them at all is because we defined our own
@@ -406,6 +456,7 @@ void columns_add(Columns *cols, GtkWidget *child,
childdata->colspan = colspan;
childdata->force_left = FALSE;
childdata->same_height_as = NULL;
+ childdata->percentages = NULL;
cols->children = g_list_append(cols->children, childdata);
cols->taborder = g_list_append(cols->taborder, child);
From 9909077be131c5dc7b3dee14cb25b8a799040616 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 26 Nov 2017 08:56:16 +0000
Subject: [PATCH 098/607] Make the current code compile again under GTK1.
Apparently I haven't tested this compile mode in a while: I had a
couple of compile errors due to new code not properly #ifdeffed (the
true-colour mode has to be effectively disabled in the palette-based
GTK1 graphics model) and one for an unused static function
(get_monitor_geometry is only used in GTK2 and above, and with -Werror
that means I mustn't even _define_ it in GTK1).
With these changes, I still didn't get a clean compile unless I also
configured CFLAGS=-std=gnu89, due to the GTK1 headers having an
outdated set of ifdefs to figure out the compiler's semantics of
'inline'. (They seem to expect old-style gcc, which inconveniently
treats 'inline' and 'extern inline' more or less the opposite way
round from the version standardised by C99.)
---
unix/gtkwin.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 3a3c2a54..a851c258 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -3039,11 +3039,16 @@ static void draw_set_colour(struct draw_ctx *dctx, int col, int dim)
#ifdef DRAW_TEXT_GDK
if (dctx->uctx.type == DRAWTYPE_GDK) {
if (dim) {
+#if GTK_CHECK_VERSION(2,0,0)
GdkColor color;
color.red = dctx->inst->cols[col].red * 2 / 3;
color.green = dctx->inst->cols[col].green * 2 / 3;
color.blue = dctx->inst->cols[col].blue * 2 / 3;
gdk_gc_set_rgb_fg_color(dctx->uctx.u.gdk.gc, &color);
+#else
+ /* Poor GTK1 fallback */
+ gdk_gc_set_foreground(dctx->uctx.u.gdk.gc, &dctx->inst->cols[col]);
+#endif
} else {
gdk_gc_set_foreground(dctx->uctx.u.gdk.gc, &dctx->inst->cols[col]);
}
@@ -3064,6 +3069,7 @@ static void draw_set_colour_rgb(struct draw_ctx *dctx, optionalrgb orgb,
{
#ifdef DRAW_TEXT_GDK
if (dctx->uctx.type == DRAWTYPE_GDK) {
+#if GTK_CHECK_VERSION(2,0,0)
GdkColor color;
color.red = orgb.r * 256;
color.green = orgb.g * 256;
@@ -3074,6 +3080,10 @@ static void draw_set_colour_rgb(struct draw_ctx *dctx, optionalrgb orgb,
color.blue = color.blue * 2 / 3;
}
gdk_gc_set_rgb_fg_color(dctx->uctx.u.gdk.gc, &color);
+#else
+ /* Poor GTK1 fallback */
+ gdk_gc_set_foreground(dctx->uctx.u.gdk.gc, &dctx->inst->cols[256]);
+#endif
}
#endif
#ifdef DRAW_TEXT_CAIRO
@@ -4374,6 +4384,7 @@ static void start_backend(struct gui_data *inst)
gtk_widget_set_sensitive(inst->restartitem, FALSE);
}
+#if GTK_CHECK_VERSION(2,0,0)
static void get_monitor_geometry(GtkWidget *widget, GdkRectangle *geometry)
{
#if GTK_CHECK_VERSION(3,4,0)
@@ -4397,6 +4408,7 @@ static void get_monitor_geometry(GtkWidget *widget, GdkRectangle *geometry)
geometry->height = gdk_screen_height();
#endif
}
+#endif
struct gui_data *new_session_window(Conf *conf, const char *geometry_string)
{
From c74d1e3c6a0aa2c4ef275e18fcdc5844f536bab6 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 26 Nov 2017 09:16:22 +0000
Subject: [PATCH 099/607] GTK1 runtime fix: widen extent of ignore_sbar.
ignore_sbar is a flag that we set while manually changing the
scrollbar settings, so that when those half-finished changes trigger
GTK event callbacks, we know to ignore them, and wait until we've
finished setting everything up before actually updating the window.
But somehow I had managed to leave the functions that actually _have
the effect_ (at least in GTK1) outside the pair of statements that set
and unset the ignore flag.
The effect was that compiling pterm for GTK1, starting it up, and
issuing a command like 'ls -l' that scrolls off the bottom of the
window would lead to the _top_ half of the ls output being visible,
and the scrollbar at the top of the scrollback rather than the bottom.
---
unix/gtkwin.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index a851c258..1038e756 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -2884,13 +2884,13 @@ void set_sbar(void *frontend, int total, int start, int page)
struct gui_data *inst = (struct gui_data *)frontend;
if (!conf_get_int(inst->conf, CONF_scrollbar))
return;
+ inst->ignore_sbar = TRUE;
gtk_adjustment_set_lower(inst->sbar_adjust, 0);
gtk_adjustment_set_upper(inst->sbar_adjust, total);
gtk_adjustment_set_value(inst->sbar_adjust, start);
gtk_adjustment_set_page_size(inst->sbar_adjust, page);
gtk_adjustment_set_step_increment(inst->sbar_adjust, 1);
gtk_adjustment_set_page_increment(inst->sbar_adjust, page/2);
- inst->ignore_sbar = TRUE;
#if !GTK_CHECK_VERSION(3,18,0)
gtk_adjustment_changed(inst->sbar_adjust);
#endif
From 116dac29ccc99ff498edd7dfcda0f456ce47c3ef Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 25 Nov 2017 21:49:31 +0000
Subject: [PATCH 100/607] Reinstate the SIGCHLD handler in ptermapp.
Detecting that the child process in a pterm has terminated is
important for _any_ kind of pterm, so it's a mistake to put the signal
handler setup _solely_ inside the optional pty_pre_init function which
does the privileged setup and forks off a utmp watchdog process. Now
the signal handler is installed even in the GtkApplication-based
multi-window front end to pterm, meaning it will exist even on OS X.
---
unix/uxpty.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/unix/uxpty.c b/unix/uxpty.c
index 618fe9bd..4387ad8f 100644
--- a/unix/uxpty.c
+++ b/unix/uxpty.c
@@ -261,13 +261,20 @@ static void cleanup_utmp(void)
}
#endif
-#ifndef NO_PTY_PRE_INIT
static void sigchld_handler(int signum)
{
if (write(pty_signal_pipe[1], "x", 1) <= 0)
/* not much we can do about it */;
}
-#endif
+
+static void pty_setup_sigchld_handler(void)
+{
+ static int setup = FALSE;
+ if (!setup) {
+ putty_signal(SIGCHLD, sigchld_handler);
+ setup = TRUE;
+ }
+}
#ifndef OMIT_UTMP
static void fatal_sig_handler(int signum)
@@ -433,7 +440,7 @@ void pty_pre_init(void)
/* set the child signal handler straight away; it needs to be set
* before we ever fork. */
- putty_signal(SIGCHLD, sigchld_handler);
+ pty_setup_sigchld_handler();
pty->master_fd = pty->slave_fd = -1;
#ifndef OMIT_UTMP
pty_stamped_utmp = FALSE;
@@ -790,6 +797,12 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf,
windowid = get_windowid(pty->frontend);
#endif
+ /*
+ * Set up the signal handler to catch SIGCHLD, if pty_pre_init
+ * didn't already do it.
+ */
+ pty_setup_sigchld_handler();
+
/*
* Fork and execute the command.
*/
From 5b13a1b01518c23f38525ec2ae9b4ac9ca3110cd Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 25 Nov 2017 21:51:45 +0000
Subject: [PATCH 101/607] Add a missing conf_copy in gtkapp's Duplicate
Session.
Without this, the Conf objects in a session and its duplicate were
aliases of each other, which could lead to confusing semantic effects
if one of the sessions was reconfigured in mid-run, and worse still, a
crash if one session got cleaned up and called conf_free on a Conf
that the other was still using.
None of that was intentional; it was just a matter of forgetting to
clone the Conf for the duplicated session. Now we do.
---
unix/gtkapp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unix/gtkapp.c b/unix/gtkapp.c
index 8b2a794f..b544684e 100644
--- a/unix/gtkapp.c
+++ b/unix/gtkapp.c
@@ -196,7 +196,7 @@ void launch_duplicate_session(Conf *conf)
{
extern const int dup_check_launchable;
assert(!dup_check_launchable || conf_launchable(conf));
- new_session_window(conf, NULL);
+ new_session_window(conf_copy(conf), NULL);
}
void launch_new_session(void)
From b6b91b8e177958d870caa116e10cec537892eba8 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 26 Nov 2017 10:58:56 +0000
Subject: [PATCH 102/607] OS X makefile: stop depending on JHBUILD_PREFIX.
People who use a packaging system other than jhbuild still ought to be
able to run the OS X GTK3 build, so now the gtk-mac-bundler command
finds out the locations of things by a more portable method.
(I've had this change lurking around uncommitted in a working tree for
a while, and only just found it in the course of doing other OS X-
related work. Oops.)
---
Recipe | 4 ++--
unix/pterm.bundle | 6 +++++-
unix/putty.bundle | 36 +++++++++++++++++++++++++++++++++++-
3 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/Recipe b/Recipe
index 5715938b..33d0f2f7 100644
--- a/Recipe
+++ b/Recipe
@@ -208,9 +208,9 @@ endif
if HAVE_QUARTZ
noinst_SCRIPTS = unix/PuTTY.app unix/Pterm.app
unix/PuTTY.app: unix/putty.bundle puttyapp osxlaunch
- rm -rf $@ && gtk-mac-bundler $<
+ rm -rf $@ && PUTTY_GTK_PREFIX_FROM_MAKEFILE=$$(pkg-config --variable=prefix gtk+-3.0) gtk-mac-bundler $<
unix/Pterm.app: unix/pterm.bundle ptermapp osxlaunch
- rm -rf $@ && gtk-mac-bundler $<
+ rm -rf $@ && PUTTY_GTK_PREFIX_FROM_MAKEFILE=$$(pkg-config --variable=prefix gtk+-3.0) gtk-mac-bundler $<
endif
!end
diff --git a/unix/pterm.bundle b/unix/pterm.bundle
index 377fee0d..0d701216 100644
--- a/unix/pterm.bundle
+++ b/unix/pterm.bundle
@@ -2,7 +2,11 @@
- ${env:JHBUILD_PREFIX}
+
+ ${env:PUTTY_GTK_PREFIX_FROM_MAKEFILE}
+
gtk+-3.0
+ ${env:PUTTY_GTK_PREFIX_FROM_MAKEFILE}
+
gtk+-3.0
@@ -518,6 +527,7 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/dd391569(v=vs.85).aspx
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -22,11 +35,8 @@
-
-
-
-
-
+
+
@@ -42,6 +52,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -63,7 +107,7 @@
Buildscr.
-->
Date: Sat, 2 Jun 2018 07:52:26 +0100
Subject: [PATCH 321/607] Fix some missing void * and const in existing APIs.
Several changes here that should have been in commit 7babe66a8 but I
missed them.
---
portfwd.c | 2 +-
ssh.c | 4 ++--
ssh.h | 6 +++---
sshecc.c | 6 ++++--
x11fwd.c | 4 +++-
5 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/portfwd.c b/portfwd.c
index d27abb41..1e4d2d1f 100644
--- a/portfwd.c
+++ b/portfwd.c
@@ -605,7 +605,7 @@ void pfd_override_throttle(struct PortForwarding *pf, int enable)
/*
* Called to send data down the raw connection.
*/
-int pfd_send(struct PortForwarding *pf, char *data, int len)
+int pfd_send(struct PortForwarding *pf, const void *data, int len)
{
if (pf == NULL)
return 0;
diff --git a/ssh.c b/ssh.c
index edf813da..4e87dc0a 100644
--- a/ssh.c
+++ b/ssh.c
@@ -5890,7 +5890,7 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
/*
* Handle incoming data on an SSH-1 or SSH-2 agent-forwarding channel.
*/
-static int ssh_agent_channel_data(struct ssh_channel *c, char *data,
+static int ssh_agent_channel_data(struct ssh_channel *c, const void *data,
int length)
{
bufchain_add(&c->u.a.inbuffer, data, length);
@@ -5908,7 +5908,7 @@ static int ssh_agent_channel_data(struct ssh_channel *c, char *data,
}
static int ssh_channel_data(struct ssh_channel *c, int is_stderr,
- char *data, int length)
+ const void *data, int length)
{
switch (c->type) {
case CHAN_MAINSESSION:
diff --git a/ssh.h b/ssh.h
index 398f8352..de31df4a 100644
--- a/ssh.h
+++ b/ssh.h
@@ -226,7 +226,7 @@ struct ec_key *ssh_ecdhkex_newkey(const struct ssh_kex *kex);
void ssh_ecdhkex_freekey(struct ec_key *key);
void ssh_ecdhkex_getpublic(struct ec_key *key, BinarySink *bs);
Bignum ssh_ecdhkex_getkey(struct ec_key *key,
- char *remoteKey, int remoteKeyLen);
+ const void *remoteKey, int remoteKeyLen);
/*
* Helper function for k generation in DSA, reused in ECDSA
@@ -542,7 +542,7 @@ void ssh_send_port_open(void *channel, const char *hostname, int port,
extern char *pfd_connect(struct PortForwarding **pf, char *hostname, int port,
void *c, Conf *conf, int addressfamily);
extern void pfd_close(struct PortForwarding *);
-extern int pfd_send(struct PortForwarding *, char *data, int len);
+extern int pfd_send(struct PortForwarding *, const void *data, int len);
extern void pfd_send_eof(struct PortForwarding *);
extern void pfd_confirm(struct PortForwarding *);
extern void pfd_unthrottle(struct PortForwarding *);
@@ -622,7 +622,7 @@ void x11_free_fake_auth(struct X11FakeAuth *auth);
struct X11Connection; /* opaque outside x11fwd.c */
struct X11Connection *x11_init(tree234 *authtree, void *, const char *, int);
extern void x11_close(struct X11Connection *);
-extern int x11_send(struct X11Connection *, char *, int);
+extern int x11_send(struct X11Connection *, const void *, int);
extern void x11_send_eof(struct X11Connection *s);
extern void x11_unthrottle(struct X11Connection *s);
extern void x11_override_throttle(struct X11Connection *s, int enable);
diff --git a/sshecc.c b/sshecc.c
index 9ce2784b..57bd3595 100644
--- a/sshecc.c
+++ b/sshecc.c
@@ -2689,7 +2689,8 @@ void ssh_ecdhkex_getpublic(struct ec_key *ec, BinarySink *bs)
}
}
-Bignum ssh_ecdhkex_getkey(struct ec_key *ec, char *remoteKey, int remoteKeyLen)
+Bignum ssh_ecdhkex_getkey(struct ec_key *ec,
+ const void *remoteKey, int remoteKeyLen)
{
struct ec_point remote;
Bignum ret;
@@ -2708,7 +2709,8 @@ Bignum ssh_ecdhkex_getkey(struct ec_key *ec, char *remoteKey, int remoteKeyLen)
remote.curve = ec->publicKey.curve;
remote.infinity = 0;
- remote.x = bignum_from_bytes_le((unsigned char*)remoteKey, remoteKeyLen);
+ remote.x = bignum_from_bytes_le((const unsigned char *)remoteKey,
+ remoteKeyLen);
remote.y = NULL;
remote.z = NULL;
}
diff --git a/x11fwd.c b/x11fwd.c
index 493eca67..eee73095 100644
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -800,8 +800,10 @@ static int x11_parse_ip(const char *addr_string, unsigned long *ip)
/*
* Called to send data down the raw connection.
*/
-int x11_send(struct X11Connection *xconn, char *data, int len)
+int x11_send(struct X11Connection *xconn, const void *vdata, int len)
{
+ const char *data = (const char *)vdata;
+
if (!xconn)
return 0;
From 9e96af59ce7cd56aa181c1d3da40cedf0ecbaaf5 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 27 May 2018 16:56:51 +0100
Subject: [PATCH 322/607] Introduce a new 'ptrlen' type.
This wraps up a (pointer, length) pair into a convenient struct that
lets me return it by value from a function, and also pass it through
to other functions in one go.
Ideally quite a lot of this code base could be switched over to using
ptrlen in place of separate pointer and length variables or function
parameters. (In fact, in my personal ideal conception of C, the usual
string type would be of this form, and all the string.h functions
would operate on ptrlens instead of zero-terminated 'char *'.)
For the moment, I'm just introducing it to make some upcoming
refactoring less inconvenient. Bulk migration of existing code to
ptrlen is a project for another time.
Along with the type itself, I've provided a convenient system of
including the contents of a ptrlen in a printf; a constructor function
that wraps up a pointer and length so you can make a ptrlen on the fly
in mid-expression; a function to compare a ptrlen against an ordinary
C string (which I mostly expect to use with string literals); and a
function 'mkstr' to make a dynamically allocated C string out of one.
That last function replaces a function of the same name in sftp.c,
which I'm promoting to a whole-codebase facility and adjusting its
API.
---
defs.h | 11 +++++++++++
marshal.c | 5 +++++
marshal.h | 3 +++
misc.c | 32 ++++++++++++++++++++++++++++++++
misc.h | 8 ++++++++
pageant.c | 2 +-
sftp.c | 22 +++++-----------------
ssh.h | 2 +-
sshpubk.c | 21 +++++++++++----------
9 files changed, 77 insertions(+), 29 deletions(-)
diff --git a/defs.h b/defs.h
index 3946e1ed..87b48499 100644
--- a/defs.h
+++ b/defs.h
@@ -11,6 +11,8 @@
#ifndef PUTTY_DEFS_H
#define PUTTY_DEFS_H
+#include
+
#ifndef FALSE
#define FALSE 0
#endif
@@ -50,6 +52,15 @@ typedef struct Plug_vtable Plug_vtable;
typedef const Socket_vtable **Socket;
typedef const Plug_vtable **Plug;
+/*
+ * A small structure wrapping up a (pointer, length) pair so that it
+ * can be conveniently passed to or from a function.
+ */
+typedef struct ptrlen {
+ const void *ptr;
+ size_t len;
+} ptrlen;
+
/* Do a compile-time type-check of 'to_check' (without evaluating it),
* as a side effect of returning the value 'to_return'. Note that
* although this macro double-*expands* to_return, it always
diff --git a/marshal.c b/marshal.c
index 4c1f1bcc..48bbef78 100644
--- a/marshal.c
+++ b/marshal.c
@@ -52,6 +52,11 @@ void BinarySink_put_string(BinarySink *bs, const void *data, size_t len)
bs->write(bs, data, len);
}
+void BinarySink_put_stringpl(BinarySink *bs, ptrlen pl)
+{
+ BinarySink_put_string(bs, pl.ptr, pl.len);
+}
+
void BinarySink_put_stringz(BinarySink *bs, const char *str)
{
BinarySink_put_string(bs, str, strlen(str));
diff --git a/marshal.h b/marshal.h
index 95ec3769..ca5a009f 100644
--- a/marshal.h
+++ b/marshal.h
@@ -88,6 +88,8 @@ struct BinarySink {
* that then gets wrapped into a string container in an outer one). */
#define put_string(bs, val, len) \
BinarySink_put_string(BinarySink_UPCAST(bs),val,len)
+#define put_stringpl(bs, ptrlen) \
+ BinarySink_put_stringpl(BinarySink_UPCAST(bs),ptrlen)
#define put_stringz(bs, val) \
BinarySink_put_stringz(BinarySink_UPCAST(bs), val)
#define put_stringsb(bs, val) \
@@ -129,6 +131,7 @@ void BinarySink_put_bool(BinarySink *, int);
void BinarySink_put_uint16(BinarySink *, unsigned long);
void BinarySink_put_uint32(BinarySink *, unsigned long);
void BinarySink_put_string(BinarySink *, const void *data, size_t len);
+void BinarySink_put_stringpl(BinarySink *, ptrlen);
void BinarySink_put_stringz(BinarySink *, const char *str);
struct strbuf;
void BinarySink_put_stringsb(BinarySink *, struct strbuf *);
diff --git a/misc.c b/misc.c
index b14f45d2..30bfec90 100644
--- a/misc.c
+++ b/misc.c
@@ -360,6 +360,16 @@ int toint(unsigned u)
return INT_MIN; /* fallback; should never occur on binary machines */
}
+int string_length_for_printf(size_t s)
+{
+ /* Truncate absurdly long strings (should one show up) to fit
+ * within a positive 'int', which is what the "%.*s" format will
+ * expect. */
+ if (s > INT_MAX)
+ return INT_MAX;
+ return s;
+}
+
/*
* Do an sprintf(), but into a custom-allocated buffer.
*
@@ -1177,6 +1187,28 @@ int match_ssh_id(int stringlen, const void *string, const char *id)
return (idlen == stringlen && !memcmp(string, id, idlen));
}
+ptrlen make_ptrlen(const void *ptr, size_t len)
+{
+ ptrlen pl;
+ pl.ptr = ptr;
+ pl.len = len;
+ return pl;
+}
+
+int ptrlen_eq_string(ptrlen pl, const char *str)
+{
+ size_t len = strlen(str);
+ return (pl.len == len && !memcmp(pl.ptr, str, len));
+}
+
+char *mkstr(ptrlen pl)
+{
+ char *p = snewn(pl.len + 1, char);
+ memcpy(p, pl.ptr, pl.len);
+ p[pl.len] = '\0';
+ return p;
+}
+
void *get_ssh_string(int *datalen, const void **data, int *stringlen)
{
void *ret;
diff --git a/misc.h b/misc.h
index c4da3d6d..de662a87 100644
--- a/misc.h
+++ b/misc.h
@@ -87,6 +87,14 @@ int validate_manual_hostkey(char *key);
struct tm ltime(void);
+ptrlen make_ptrlen(const void *ptr, size_t len);
+int ptrlen_eq_string(ptrlen pl, const char *str);
+char *mkstr(ptrlen pl);
+int string_length_for_printf(size_t);
+/* Derive two printf arguments from a ptrlen, suitable for "%.*s" */
+#define PTRLEN_PRINTF(pl) \
+ string_length_for_printf((pl).len), (const char *)(pl).ptr
+
/* Wipe sensitive data out of memory that's about to be freed. Simpler
* than memset because we don't need the fill char parameter; also
* attempts (by fiddly use of volatile) to inhibit the compiler from
diff --git a/pageant.c b/pageant.c
index c541101f..8528cc02 100644
--- a/pageant.c
+++ b/pageant.c
@@ -556,7 +556,7 @@ void pageant_handle_msg(BinarySink *bs,
key = snew(struct ssh2_userkey);
key->data = NULL;
key->comment = NULL;
- key->alg = find_pubkey_alg_len(alglen, alg);
+ key->alg = find_pubkey_alg_len(make_ptrlen(alg, alglen));
if (!key->alg) {
pageant_failure_msg(bs, "algorithm unknown", logctx, logfn);
goto add2_cleanup;
diff --git a/sftp.c b/sftp.c
index 77162157..ad153396 100644
--- a/sftp.c
+++ b/sftp.c
@@ -341,18 +341,6 @@ struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
return req;
}
-/* ----------------------------------------------------------------------
- * String handling routines.
- */
-
-static char *mkstr(char *s, int len)
-{
- char *p = snewn(len + 1, char);
- memcpy(p, s, len);
- p[len] = '\0';
- return p;
-}
-
/* ----------------------------------------------------------------------
* SFTP primitives.
*/
@@ -500,7 +488,7 @@ char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
sftp_pkt_free(pktin);
return NULL;
}
- path = mkstr(path, len);
+ path = mkstr(make_ptrlen(path, len));
sftp_pkt_free(pktin);
return path;
} else {
@@ -548,7 +536,7 @@ struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
return NULL;
}
handle = snew(struct fxp_handle);
- handle->hstring = mkstr(hstring, len);
+ handle->hstring = mkstr(make_ptrlen(hstring, len));
handle->hlen = len;
sftp_pkt_free(pktin);
return handle;
@@ -590,7 +578,7 @@ struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
return NULL;
}
handle = snew(struct fxp_handle);
- handle->hstring = mkstr(hstring, len);
+ handle->hstring = mkstr(make_ptrlen(hstring, len));
handle->hlen = len;
sftp_pkt_free(pktin);
return handle;
@@ -977,8 +965,8 @@ struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
sfree(pktin);
return NULL;
}
- ret->names[i].filename = mkstr(str1, len1);
- ret->names[i].longname = mkstr(str2, len2);
+ ret->names[i].filename = mkstr(make_ptrlen(str1, len1));
+ ret->names[i].longname = mkstr(make_ptrlen(str2, len2));
}
sftp_pkt_free(pktin);
return ret;
diff --git a/ssh.h b/ssh.h
index de31df4a..7a1c5766 100644
--- a/ssh.h
+++ b/ssh.h
@@ -736,7 +736,7 @@ int ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
char *passphrase);
const ssh_keyalg *find_pubkey_alg(const char *name);
-const ssh_keyalg *find_pubkey_alg_len(int namelen, const char *name);
+const ssh_keyalg *find_pubkey_alg_len(ptrlen name);
enum {
SSH_KEYTYPE_UNOPENABLE,
diff --git a/sshpubk.c b/sshpubk.c
index 7ff7c081..68cf6945 100644
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -584,19 +584,19 @@ struct ssh2_userkey ssh2_wrong_passphrase = {
NULL, NULL, NULL
};
-const ssh_keyalg *find_pubkey_alg_len(int namelen, const char *name)
+const ssh_keyalg *find_pubkey_alg_len(ptrlen name)
{
- if (match_ssh_id(namelen, name, "ssh-rsa"))
+ if (ptrlen_eq_string(name, "ssh-rsa"))
return &ssh_rsa;
- else if (match_ssh_id(namelen, name, "ssh-dss"))
+ else if (ptrlen_eq_string(name, "ssh-dss"))
return &ssh_dss;
- else if (match_ssh_id(namelen, name, "ecdsa-sha2-nistp256"))
+ else if (ptrlen_eq_string(name, "ecdsa-sha2-nistp256"))
return &ssh_ecdsa_nistp256;
- else if (match_ssh_id(namelen, name, "ecdsa-sha2-nistp384"))
+ else if (ptrlen_eq_string(name, "ecdsa-sha2-nistp384"))
return &ssh_ecdsa_nistp384;
- else if (match_ssh_id(namelen, name, "ecdsa-sha2-nistp521"))
+ else if (ptrlen_eq_string(name, "ecdsa-sha2-nistp521"))
return &ssh_ecdsa_nistp521;
- else if (match_ssh_id(namelen, name, "ssh-ed25519"))
+ else if (ptrlen_eq_string(name, "ssh-ed25519"))
return &ssh_ecdsa_ed25519;
else
return NULL;
@@ -604,7 +604,7 @@ const ssh_keyalg *find_pubkey_alg_len(int namelen, const char *name)
const ssh_keyalg *find_pubkey_alg(const char *name)
{
- return find_pubkey_alg_len(strlen(name), name);
+ return find_pubkey_alg_len(make_ptrlen(name, strlen(name)));
}
struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
@@ -1535,7 +1535,7 @@ char *ssh2_fingerprint_blob(const void *blob, int bloblen)
* If we can actually identify the algorithm as one we know
* about, get hold of the key's bit count too.
*/
- alg = find_pubkey_alg_len(alglen, algstr);
+ alg = find_pubkey_alg_len(make_ptrlen(algstr, alglen));
if (alg) {
int bits = alg->pubkey_bits(alg, blob, bloblen);
return dupprintf("%.*s %d %s", alglen, algstr,
@@ -1600,7 +1600,8 @@ static int key_type_fp(FILE *fp)
(p = p+1 + strspn(p+1, "0123456789"), *p == ' ') &&
(p = p+1 + strspn(p+1, "0123456789"), *p == ' ' || *p == '\n' || !*p))
return SSH_KEYTYPE_SSH1_PUBLIC;
- if ((p = buf + strcspn(buf, " "), find_pubkey_alg_len(p-buf, buf)) &&
+ if ((p = buf + strcspn(buf, " "),
+ find_pubkey_alg_len(make_ptrlen(buf, p-buf))) &&
(p = p+1 + strspn(p+1, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij"
"klmnopqrstuvwxyz+/="),
*p == ' ' || *p == '\n' || !*p))
From 005ca6b25713682305502e2f1a5e8154a87e8625 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 2 Jun 2018 08:25:19 +0100
Subject: [PATCH 323/607] Introduce a centralised unmarshaller, 'BinarySource'.
This is the companion to the BinarySink system I introduced a couple
of weeks ago, and provides the same type-genericity which will let me
use the same get_* routines on an SSH packet, an SFTP packet or
anything else that chooses to include an implementing substructure.
However, unlike BinarySink which contained a (one-function) vtable,
BinarySource contains only mutable data fields - so another thing you
might very well want to do is to simply instantiate a bare one without
any containing object at all. I couldn't quite coerce C into letting
me use the same setup macro in both cases, so I've arranged a
BinarySource_INIT you can use on larger implementing objects and a
BinarySource_BARE_INIT you can use on a BinarySource not contained in
anything.
The API follows the general principle that even if decoding fails, the
decode functions will always return _some_ kind of value, with the
same dynamically-allocated-ness they would have used for a completely
successful value. But they also set an error flag in the BinarySource
which can be tested later. So instead of having to decode a 10-field
packet by means of 10 separate 'if (!get_foo(src)) throw error'
clauses, you can just write 10 'variable = get_foo(src)' statements
followed by a single check of get_err(src), and if the error check
fails, you have to do exactly the same set of frees you would have
after a successful decode.
---
defs.h | 1 +
int64.h | 1 +
marshal.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
marshal.h | 123 +++++++++++++++++++++++++++++++++++++++++++++++
ssh.h | 2 +
sshbn.c | 42 ++++++++++++++--
6 files changed, 306 insertions(+), 4 deletions(-)
diff --git a/defs.h b/defs.h
index 87b48499..55468241 100644
--- a/defs.h
+++ b/defs.h
@@ -37,6 +37,7 @@ struct RSAKey;
typedef uint32_t uint32;
typedef struct BinarySink BinarySink;
+typedef struct BinarySource BinarySource;
typedef struct SockAddr_tag *SockAddr;
diff --git a/int64.h b/int64.h
index 6ac7f3fc..17122974 100644
--- a/int64.h
+++ b/int64.h
@@ -24,5 +24,6 @@ uint64 uint64_shift_left(uint64 x, int shift);
uint64 uint64_from_decimal(char *str);
void BinarySink_put_uint64(BinarySink *, uint64);
+uint64 BinarySource_get_uint64(BinarySource *);
#endif
diff --git a/marshal.c b/marshal.c
index 48bbef78..76c7c05f 100644
--- a/marshal.c
+++ b/marshal.c
@@ -82,3 +82,144 @@ int BinarySink_put_pstring(BinarySink *bs, const char *str)
bs->write(bs, str, len);
return TRUE;
}
+
+/* ---------------------------------------------------------------------- */
+
+static int BinarySource_data_avail(BinarySource *src, size_t wanted)
+{
+ if (src->err)
+ return FALSE;
+
+ if (wanted <= src->len - src->pos)
+ return TRUE;
+
+ src->err = BSE_OUT_OF_DATA;
+ return FALSE;
+}
+
+#define avail(wanted) BinarySource_data_avail(src, wanted)
+#define advance(dist) (src->pos += dist)
+#define here ((const void *)((const unsigned char *)src->data + src->pos))
+#define consume(dist) \
+ ((const void *)((const unsigned char *)src->data + \
+ ((src->pos += dist) - dist)))
+
+ptrlen BinarySource_get_data(BinarySource *src, size_t wanted)
+{
+ if (!avail(wanted))
+ return make_ptrlen("", 0);
+
+ return make_ptrlen(consume(wanted), wanted);
+}
+
+unsigned char BinarySource_get_byte(BinarySource *src)
+{
+ const unsigned char *ucp;
+
+ if (!avail(1))
+ return 0;
+
+ ucp = consume(1);
+ return *ucp;
+}
+
+int BinarySource_get_bool(BinarySource *src)
+{
+ const unsigned char *ucp;
+
+ if (!avail(1))
+ return 0;
+
+ ucp = consume(1);
+ return *ucp != 0;
+}
+
+unsigned BinarySource_get_uint16(BinarySource *src)
+{
+ const unsigned char *ucp;
+
+ if (!avail(2))
+ return 0;
+
+ ucp = consume(2);
+ return GET_16BIT_MSB_FIRST(ucp);
+}
+
+unsigned long BinarySource_get_uint32(BinarySource *src)
+{
+ const unsigned char *ucp;
+
+ if (!avail(4))
+ return 0;
+
+ ucp = consume(4);
+ return GET_32BIT_MSB_FIRST(ucp);
+}
+
+uint64 BinarySource_get_uint64(BinarySource *src)
+{
+ const unsigned char *ucp;
+ uint64 toret;
+
+ if (!avail(8)) {
+ toret.hi = toret.lo = 0;
+ return toret;
+ }
+
+ ucp = consume(8);
+ toret.hi = GET_32BIT_MSB_FIRST(ucp);
+ toret.lo = GET_32BIT_MSB_FIRST(ucp + 4);
+ return toret;
+}
+
+ptrlen BinarySource_get_string(BinarySource *src)
+{
+ const unsigned char *ucp;
+ size_t len;
+
+ if (!avail(4))
+ return make_ptrlen("", 0);
+
+ ucp = consume(4);
+ len = GET_32BIT_MSB_FIRST(ucp);
+
+ if (!avail(len))
+ return make_ptrlen("", 0);
+
+ return make_ptrlen(consume(len), len);
+}
+
+const char *BinarySource_get_asciz(BinarySource *src)
+{
+ const char *start, *end;
+
+ if (src->err)
+ return "";
+
+ start = here;
+ end = memchr(start, '\0', src->len - src->pos);
+ if (!end) {
+ src->err = BSE_OUT_OF_DATA;
+ return "";
+ }
+
+ advance(end + 1 - start);
+ return start;
+}
+
+ptrlen BinarySource_get_pstring(BinarySource *src)
+{
+ const unsigned char *ucp;
+ size_t len;
+
+ if (!avail(1))
+ return make_ptrlen("", 0);
+
+ ucp = consume(1);
+ len = *ucp;
+
+ if (!avail(len))
+ return make_ptrlen("", 0);
+
+ return make_ptrlen(consume(len), len);
+}
diff --git a/marshal.h b/marshal.h
index ca5a009f..e7603adb 100644
--- a/marshal.h
+++ b/marshal.h
@@ -138,4 +138,127 @@ void BinarySink_put_stringsb(BinarySink *, struct strbuf *);
void BinarySink_put_asciz(BinarySink *, const char *str);
int BinarySink_put_pstring(BinarySink *, const char *str);
+/* ---------------------------------------------------------------------- */
+
+/*
+ * A complementary trait structure for _un_-marshalling.
+ *
+ * This structure contains client-visible data fields rather than
+ * methods, because that seemed more useful than leaving it totally
+ * opaque. But it's still got the self-pointer system that will allow
+ * the set of get_* macros to target one of these itself or any other
+ * type that 'derives' from it. So, for example, an SSH packet
+ * structure can act as a BinarySource while also having additional
+ * fields like the packet type.
+ */
+typedef enum BinarySourceError {
+ BSE_NO_ERROR,
+ BSE_OUT_OF_DATA,
+ BSE_INVALID
+} BinarySourceError;
+struct BinarySource {
+ /*
+ * (data, len) is the data block being decoded. pos is the current
+ * position within the block.
+ */
+ const void *data;
+ size_t pos, len;
+
+ /*
+ * 'err' indicates whether a decoding error has happened at any
+ * point. Once this has been set to something other than
+ * BSE_NO_ERROR, it shouldn't be changed by any unmarshalling
+ * function. So you can safely do a long sequence of get_foo()
+ * operations and then test err just once at the end, rather than
+ * having to conditionalise every single get.
+ *
+ * The unmarshalling functions should always return some value,
+ * even if a decoding error occurs. Generally on error they'll
+ * return zero (if numeric) or the empty string (if string-based),
+ * or some other appropriate default value for more complicated
+ * types.
+ *
+ * If the usual return value is dynamically allocated (e.g. a
+ * Bignum, or a normal C 'char *' string), then the error value is
+ * also dynamic in the same way. So you have to free exactly the
+ * same set of things whether or not there was a decoding error,
+ * which simplifies exit paths - for example, you could call a big
+ * pile of get_foo functions, then put the actual handling of the
+ * results under 'if (!get_err(src))', and then free everything
+ * outside that if.
+ */
+ BinarySourceError err;
+
+ /*
+ * Self-pointer for the implicit derivation trick, same as
+ * BinarySink above.
+ */
+ BinarySource *binarysource_;
+};
+
+/*
+ * Implementation macros, similar to BinarySink.
+ */
+#define BinarySource_IMPLEMENTATION BinarySource binarysource_[1]
+#define BinarySource_INIT__(obj, data_, len_) \
+ ((obj)->data = (data_), \
+ (obj)->len = (len_), \
+ (obj)->pos = 0, \
+ (obj)->err = BSE_NO_ERROR, \
+ (obj)->binarysource_ = (obj))
+#define BinarySource_BARE_INIT(obj, data_, len_) \
+ TYPECHECK(&(obj)->binarysource_ == (BinarySource **)0, \
+ BinarySource_INIT__(obj, data_, len_))
+#define BinarySource_INIT(obj, data_, len_) \
+ TYPECHECK(&(obj)->binarysource_ == (BinarySource (*)[1])0, \
+ BinarySource_INIT__(BinarySource_UPCAST(obj), data_, len_))
+#define BinarySource_DOWNCAST(object, type) \
+ TYPECHECK((object) == ((type *)0)->binarysource_, \
+ ((type *)(((char *)(object)) - offsetof(type, binarysource_))))
+#define BinarySource_UPCAST(object) \
+ TYPECHECK((object)->binarysource_ == (BinarySource *)0, \
+ (object)->binarysource_)
+#define BinarySource_COPIED(obj) \
+ ((obj)->binarysource_->binarysource_ = (obj)->binarysource_)
+
+#define get_data(src, len) \
+ BinarySource_get_data(BinarySource_UPCAST(src), len)
+#define get_byte(src) \
+ BinarySource_get_byte(BinarySource_UPCAST(src))
+#define get_bool(src) \
+ BinarySource_get_bool(BinarySource_UPCAST(src))
+#define get_uint16(src) \
+ BinarySource_get_uint16(BinarySource_UPCAST(src))
+#define get_uint32(src) \
+ BinarySource_get_uint32(BinarySource_UPCAST(src))
+#define get_uint64(src) \
+ BinarySource_get_uint64(BinarySource_UPCAST(src))
+#define get_string(src) \
+ BinarySource_get_string(BinarySource_UPCAST(src))
+#define get_asciz(src) \
+ BinarySource_get_asciz(BinarySource_UPCAST(src))
+#define get_pstring(src) \
+ BinarySource_get_pstring(BinarySource_UPCAST(src))
+#define get_mp_ssh1(src) \
+ BinarySource_get_mp_ssh1(BinarySource_UPCAST(src))
+#define get_mp_ssh2(src) \
+ BinarySource_get_mp_ssh2(BinarySource_UPCAST(src))
+
+#define get_err(src) (BinarySource_UPCAST(src)->err)
+#define get_avail(src) (BinarySource_UPCAST(src)->len - \
+ BinarySource_UPCAST(src)->pos)
+#define get_ptr(src) \
+ ((const void *)( \
+ (const unsigned char *)(BinarySource_UPCAST(src)->data) + \
+ BinarySource_UPCAST(src)->pos))
+
+ptrlen BinarySource_get_data(BinarySource *, size_t);
+unsigned char BinarySource_get_byte(BinarySource *);
+int BinarySource_get_bool(BinarySource *);
+unsigned BinarySource_get_uint16(BinarySource *);
+unsigned long BinarySource_get_uint32(BinarySource *);
+ptrlen BinarySource_get_string(BinarySource *);
+const char *BinarySource_get_asciz(BinarySource *);
+ptrlen BinarySource_get_pstring(BinarySource *);
+
#endif /* PUTTY_MARSHAL_H */
diff --git a/ssh.h b/ssh.h
index 7a1c5766..3ab3a526 100644
--- a/ssh.h
+++ b/ssh.h
@@ -695,6 +695,8 @@ Bignum bignum_from_decimal(const char *decimal);
void BinarySink_put_mp_ssh1(BinarySink *, Bignum);
void BinarySink_put_mp_ssh2(BinarySink *, Bignum);
+Bignum BinarySource_get_mp_ssh1(BinarySource *);
+Bignum BinarySource_get_mp_ssh2(BinarySource *);
#ifdef DEBUG
void diagbn(char *prefix, Bignum md);
diff --git a/sshbn.c b/sshbn.c
index 39f8dfd8..1f0213c0 100644
--- a/sshbn.c
+++ b/sshbn.c
@@ -1624,12 +1624,12 @@ int ssh1_write_bignum(void *data, Bignum bn)
void BinarySink_put_mp_ssh1(BinarySink *bs, Bignum bn)
{
- int len = ssh1_bignum_length(bn);
+ int bits = bignum_bitcount(bn);
+ int bytes = (bits + 7) / 8;
int i;
- int bitc = bignum_bitcount(bn);
- put_uint16(bs, bitc);
- for (i = len - 2; i--;)
+ put_uint16(bs, bits);
+ for (i = bytes; i--;)
put_byte(bs, bignum_byte(bn, i));
}
@@ -1643,6 +1643,40 @@ void BinarySink_put_mp_ssh2(BinarySink *bs, Bignum bn)
put_byte(bs, bignum_byte(bn, i));
}
+Bignum BinarySource_get_mp_ssh1(BinarySource *src)
+{
+ unsigned bitc = get_uint16(src);
+ ptrlen bytes = get_data(src, (bitc + 7) / 8);
+ if (get_err(src)) {
+ return bignum_from_long(0);
+ } else {
+ Bignum toret = bignum_from_bytes(bytes.ptr, bytes.len);
+ if (bignum_bitcount(toret) != bitc) {
+ src->err = BSE_INVALID;
+ freebn(toret);
+ toret = bignum_from_long(0);
+ }
+ return toret;
+ }
+}
+
+Bignum BinarySource_get_mp_ssh2(BinarySource *src)
+{
+ ptrlen bytes = get_string(src);
+ if (get_err(src)) {
+ return bignum_from_long(0);
+ } else {
+ const unsigned char *p = bytes.ptr;
+ if ((bytes.len > 0 &&
+ ((p[0] & 0x80) ||
+ (p[0] == 0 && (bytes.len <= 1 || !(p[1] & 0x80)))))) {
+ src->err = BSE_INVALID;
+ return bignum_from_long(0);
+ }
+ return bignum_from_bytes(bytes.ptr, bytes.len);
+ }
+}
+
/*
* Compare two bignums. Returns like strcmp.
*/
From 7d8312e71f225d3f50bfbad4bd3032ad0f72eb55 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 27 May 2018 21:51:36 +0100
Subject: [PATCH 324/607] Rewrite SSH-1 RSA handling functions using
BinarySource.
The SSH-1 RSA key reading functions now have BinarySource-shaped get_*
forms, although for the moment I'm still supporting the old API as a
wrapper on the new one, because I haven't switched over the client
code yet. Also, rsa_public_blob_len uses the new system internally,
although its API is unchanged.
---
marshal.h | 4 ++
ssh.h | 5 +++
sshrsa.c | 120 ++++++++++++++++++++++++++++++++----------------------
3 files changed, 80 insertions(+), 49 deletions(-)
diff --git a/marshal.h b/marshal.h
index e7603adb..3b7a089f 100644
--- a/marshal.h
+++ b/marshal.h
@@ -243,6 +243,10 @@ struct BinarySource {
BinarySource_get_mp_ssh1(BinarySource_UPCAST(src))
#define get_mp_ssh2(src) \
BinarySource_get_mp_ssh2(BinarySource_UPCAST(src))
+#define get_rsa_ssh1_pub(src, rsa, keystr, order) \
+ BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, keystr, order)
+#define get_rsa_ssh1_priv(src, rsa) \
+ BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa)
#define get_err(src) (BinarySource_UPCAST(src)->err)
#define get_avail(src) (BinarySource_UPCAST(src)->len - \
diff --git a/ssh.h b/ssh.h
index 3ab3a526..21ab1593 100644
--- a/ssh.h
+++ b/ssh.h
@@ -182,8 +182,13 @@ typedef enum { RSA_SSH1_EXPONENT_FIRST, RSA_SSH1_MODULUS_FIRST } RsaSsh1Order;
int rsa_ssh1_readpub(const unsigned char *data, int len, struct RSAKey *result,
const unsigned char **keystr, RsaSsh1Order order);
+void BinarySource_get_rsa_ssh1_pub(
+ BinarySource *src, struct RSAKey *result,
+ ptrlen *keystr, RsaSsh1Order order);
int rsa_ssh1_readpriv(const unsigned char *data, int len,
struct RSAKey *result);
+void BinarySource_get_rsa_ssh1_priv(
+ BinarySource *src, struct RSAKey *rsa);
int rsa_ssh1_encrypt(unsigned char *data, int length, struct RSAKey *key);
Bignum rsa_ssh1_decrypt(Bignum input, struct RSAKey *key);
void rsasanitise(struct RSAKey *key);
diff --git a/sshrsa.c b/sshrsa.c
index 2fcf5e6b..c0008dc1 100644
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -10,53 +10,79 @@
#include "ssh.h"
#include "misc.h"
-int rsa_ssh1_readpub(const unsigned char *data, int len, struct RSAKey *result,
- const unsigned char **keystr, RsaSsh1Order order)
+void BinarySource_get_rsa_ssh1_pub(
+ BinarySource *src, struct RSAKey *rsa, ptrlen *keystr, RsaSsh1Order order)
{
- const unsigned char *p = data;
- int i, n;
-
- if (len < 4)
- return -1;
+ const unsigned char *start, *end;
+ unsigned bits;
+ Bignum e, m;
- if (result) {
- result->bits = 0;
- for (i = 0; i < 4; i++)
- result->bits = (result->bits << 8) + *p++;
- } else
- p += 4;
+ bits = get_uint32(src);
+ if (order == RSA_SSH1_EXPONENT_FIRST) {
+ e = get_mp_ssh1(src);
+ start = get_ptr(src);
+ m = get_mp_ssh1(src);
+ end = get_ptr(src);
+ } else {
+ start = get_ptr(src);
+ m = get_mp_ssh1(src);
+ end = get_ptr(src);
+ e = get_mp_ssh1(src);
+ }
- len -= 4;
+ if (keystr) {
+ start += (end-start >= 2 ? 2 : end-start);
+ keystr->ptr = start;
+ keystr->len = end - start;
+ }
- if (order == RSA_SSH1_EXPONENT_FIRST) {
- n = ssh1_read_bignum(p, len, result ? &result->exponent : NULL);
- if (n < 0) return -1;
- p += n;
- len -= n;
+ if (rsa) {
+ rsa->bits = bits;
+ rsa->exponent = e;
+ rsa->modulus = m;
+ rsa->bytes = (bignum_bitcount(m) + 7) / 8;
+ } else {
+ freebn(e);
+ freebn(m);
}
+}
+
+int rsa_ssh1_readpub(const unsigned char *data, int len, struct RSAKey *result,
+ const unsigned char **keystr, RsaSsh1Order order)
+{
+ BinarySource src;
+ ptrlen key_pl;
+
+ BinarySource_BARE_INIT(&src, data, len);
+ get_rsa_ssh1_pub(&src, result, &key_pl, order);
- n = ssh1_read_bignum(p, len, result ? &result->modulus : NULL);
- if (n < 0 || (result && bignum_bitcount(result->modulus) == 0)) return -1;
- if (result)
- result->bytes = n - 2;
if (keystr)
- *keystr = p + 2;
- p += n;
- len -= n;
-
- if (order == RSA_SSH1_MODULUS_FIRST) {
- n = ssh1_read_bignum(p, len, result ? &result->exponent : NULL);
- if (n < 0) return -1;
- p += n;
- len -= n;
- }
- return p - data;
+ *keystr = key_pl.ptr;
+
+ if (get_err(&src))
+ return -1;
+ else
+ return key_pl.len;
+}
+
+void BinarySource_get_rsa_ssh1_priv(
+ BinarySource *src, struct RSAKey *rsa)
+{
+ rsa->private_exponent = get_mp_ssh1(src);
}
int rsa_ssh1_readpriv(const unsigned char *data, int len,
struct RSAKey *result)
{
- return ssh1_read_bignum(data, len, &result->private_exponent);
+ BinarySource src;
+
+ BinarySource_BARE_INIT(&src, data, len);
+ get_rsa_ssh1_priv(&src, result);
+
+ if (get_err(&src))
+ return -1;
+ else
+ return src.pos;
}
int rsa_ssh1_encrypt(unsigned char *data, int length, struct RSAKey *key)
@@ -455,25 +481,21 @@ void rsa_ssh1_public_blob(BinarySink *bs, struct RSAKey *key,
/* Given a public blob, determine its length. */
int rsa_public_blob_len(void *data, int maxlen)
{
- unsigned char *p = (unsigned char *)data;
- int n;
+ BinarySource src[1];
- if (maxlen < 4)
- return -1;
- p += 4; /* length word */
- maxlen -= 4;
+ BinarySource_BARE_INIT(src, data, maxlen);
- n = ssh1_read_bignum(p, maxlen, NULL); /* exponent */
- if (n < 0)
- return -1;
- p += n;
+ /* Expect a length word, then exponent and modulus. (It doesn't
+ * even matter which order.) */
+ get_uint32(src);
+ freebn(get_mp_ssh1(src));
+ freebn(get_mp_ssh1(src));
- n = ssh1_read_bignum(p, maxlen, NULL); /* modulus */
- if (n < 0)
+ if (get_err(src))
return -1;
- p += n;
- return p - (unsigned char *)data;
+ /* Return the number of bytes consumed. */
+ return src->pos;
}
void freersakey(struct RSAKey *key)
From 2cb4d8913515d1254b0d1760e3c61ae353c424da Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 2 Jun 2018 09:41:39 +0100
Subject: [PATCH 325/607] Replace sftp_pkt_get* with BinarySource.
This is the first major piece of code converted to the new
unmarshalling system, and allows me to remove all the sftp_pkt_get*
functions in sftp.c that were previously duplicating standard decode
logic.
---
sftp.c | 253 +++++++++++++++++++++++----------------------------------
sftp.h | 1 +
2 files changed, 103 insertions(+), 151 deletions(-)
diff --git a/sftp.c b/sftp.c
index ad153396..7e7f1ffa 100644
--- a/sftp.c
+++ b/sftp.c
@@ -19,6 +19,7 @@ struct sftp_packet {
unsigned savedpos;
int type;
BinarySink_IMPLEMENTATION;
+ BinarySource_IMPLEMENTATION;
};
static const char *fxp_error_message;
@@ -63,10 +64,8 @@ static struct sftp_packet *sftp_pkt_init(int pkt_type)
static void BinarySink_put_fxp_attrs(BinarySink *bs, struct fxp_attrs attrs)
{
put_uint32(bs, attrs.flags);
- if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
- put_uint32(bs, attrs.size.hi);
- put_uint32(bs, attrs.size.lo);
- }
+ if (attrs.flags & SSH_FILEXFER_ATTR_SIZE)
+ put_uint64(bs, attrs.size);
if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
put_uint32(bs, attrs.uid);
put_uint32(bs, attrs.gid);
@@ -93,81 +92,39 @@ static void BinarySink_put_fxp_attrs(BinarySink *bs, struct fxp_attrs attrs)
* SFTP packet decode functions.
*/
-static int sftp_pkt_getbyte(struct sftp_packet *pkt, unsigned char *ret)
-{
- if (pkt->length - pkt->savedpos < 1)
- return 0;
- *ret = (unsigned char) pkt->data[pkt->savedpos];
- pkt->savedpos++;
- return 1;
-}
-static int sftp_pkt_getuint32(struct sftp_packet *pkt, unsigned long *ret)
-{
- if (pkt->length - pkt->savedpos < 4)
- return 0;
- *ret = GET_32BIT(pkt->data + pkt->savedpos);
- pkt->savedpos += 4;
- return 1;
-}
-static int sftp_pkt_getstring(struct sftp_packet *pkt,
- char **p, int *length)
-{
- *p = NULL;
- if (pkt->length - pkt->savedpos < 4)
- return 0;
- *length = toint(GET_32BIT(pkt->data + pkt->savedpos));
- pkt->savedpos += 4;
- if ((int)(pkt->length - pkt->savedpos) < *length || *length < 0) {
- *length = 0;
- return 0;
- }
- *p = pkt->data + pkt->savedpos;
- pkt->savedpos += *length;
- return 1;
-}
-static int sftp_pkt_getattrs(struct sftp_packet *pkt, struct fxp_attrs *ret)
+static int BinarySource_get_fxp_attrs(BinarySource *src,
+ struct fxp_attrs *attrs)
{
- if (!sftp_pkt_getuint32(pkt, &ret->flags))
- return 0;
- if (ret->flags & SSH_FILEXFER_ATTR_SIZE) {
- unsigned long hi, lo;
- if (!sftp_pkt_getuint32(pkt, &hi) ||
- !sftp_pkt_getuint32(pkt, &lo))
- return 0;
- ret->size = uint64_make(hi, lo);
- }
- if (ret->flags & SSH_FILEXFER_ATTR_UIDGID) {
- if (!sftp_pkt_getuint32(pkt, &ret->uid) ||
- !sftp_pkt_getuint32(pkt, &ret->gid))
- return 0;
- }
- if (ret->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
- if (!sftp_pkt_getuint32(pkt, &ret->permissions))
- return 0;
+ attrs->flags = get_uint32(src);
+ if (attrs->flags & SSH_FILEXFER_ATTR_SIZE)
+ attrs->size = get_uint64(src);
+ if (attrs->flags & SSH_FILEXFER_ATTR_UIDGID) {
+ attrs->uid = get_uint32(src);
+ attrs->gid = get_uint32(src);
}
- if (ret->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
- if (!sftp_pkt_getuint32(pkt, &ret->atime) ||
- !sftp_pkt_getuint32(pkt, &ret->mtime))
- return 0;
+ if (attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS)
+ attrs->permissions = get_uint32(src);
+ if (attrs->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
+ attrs->atime = get_uint32(src);
+ attrs->mtime = get_uint32(src);
}
- if (ret->flags & SSH_FILEXFER_ATTR_EXTENDED) {
- unsigned long count;
- if (!sftp_pkt_getuint32(pkt, &count))
- return 0;
+ if (attrs->flags & SSH_FILEXFER_ATTR_EXTENDED) {
+ unsigned long count = get_uint32(src);
while (count--) {
- char *str;
- int len;
/*
* We should try to analyse these, if we ever find one
* we recognise.
*/
- if (!sftp_pkt_getstring(pkt, &str, &len) ||
- !sftp_pkt_getstring(pkt, &str, &len))
- return 0;
+ get_string(src);
+ get_string(src);
}
}
return 1;
}
+
+#define get_fxp_attrs(bs, attrs) \
+ BinarySource_get_fxp_attrs(BinarySource_UPCAST(bs), attrs)
+
static void sftp_pkt_free(struct sftp_packet *pkt)
{
if (pkt->data)
@@ -190,7 +147,6 @@ struct sftp_packet *sftp_recv(void)
{
struct sftp_packet *pkt;
char x[4];
- unsigned char uc;
if (!sftp_recvdata(x, 4))
return NULL;
@@ -205,11 +161,12 @@ struct sftp_packet *sftp_recv(void)
return NULL;
}
- if (!sftp_pkt_getbyte(pkt, &uc)) {
+ BinarySource_INIT(pkt, pkt->data, pkt->length);
+ pkt->type = get_byte(pkt);
+
+ if (get_err(pkt)) {
sftp_pkt_free(pkt);
return NULL;
- } else {
- pkt->type = uc;
}
return pkt;
@@ -315,8 +272,7 @@ void sftp_register(struct sftp_request *req)
struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
{
- unsigned long id;
- unsigned fid;
+ unsigned id;
struct sftp_request *req;
if (!pktin) {
@@ -324,13 +280,13 @@ struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
return NULL;
}
- if (!sftp_pkt_getuint32(pktin, &id)) {
+ id = get_uint32(pktin);
+ if (get_err(pktin)) {
fxp_internal_error("did not receive a valid SFTP packet\n");
return NULL;
}
- fid = (unsigned)id;
- req = find234(sftp_requests, &fid, sftp_reqfind);
+ req = find234(sftp_requests, &id, sftp_reqfind);
if (!req || !req->registered) {
fxp_internal_error("request ID mismatch\n");
return NULL;
@@ -371,12 +327,11 @@ static int fxp_got_status(struct sftp_packet *pktin)
fxp_error_message = "expected FXP_STATUS packet";
fxp_errtype = -1;
} else {
- unsigned long ul;
- if (!sftp_pkt_getuint32(pktin, &ul)) {
+ fxp_errtype = get_uint32(pktin);
+ if (get_err(pktin)) {
fxp_error_message = "malformed FXP_STATUS packet";
fxp_errtype = -1;
} else {
- fxp_errtype = ul;
if (fxp_errtype < 0 ||
fxp_errtype >= sizeof(messages) / sizeof(*messages))
fxp_error_message = "unknown error code";
@@ -431,7 +386,8 @@ int fxp_init(void)
sftp_pkt_free(pktin);
return 0;
}
- if (!sftp_pkt_getuint32(pktin, &remotever)) {
+ remotever = get_uint32(pktin);
+ if (get_err(pktin)) {
fxp_internal_error("malformed FXP_VERSION packet");
sftp_pkt_free(pktin);
return 0;
@@ -475,22 +431,24 @@ char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
if (pktin->type == SSH_FXP_NAME) {
unsigned long count;
- char *path;
- int len;
+ char *path;
+ ptrlen name;
- if (!sftp_pkt_getuint32(pktin, &count) || count != 1) {
+ count = get_uint32(pktin);
+ if (get_err(pktin) || count != 1) {
fxp_internal_error("REALPATH did not return name count of 1\n");
sftp_pkt_free(pktin);
return NULL;
}
- if (!sftp_pkt_getstring(pktin, &path, &len)) {
+ name = get_string(pktin);
+ if (get_err(pktin)) {
fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
sftp_pkt_free(pktin);
return NULL;
}
- path = mkstr(make_ptrlen(path, len));
+ path = mkstr(name);
sftp_pkt_free(pktin);
- return path;
+ return path;
} else {
fxp_got_status(pktin);
sftp_pkt_free(pktin);
@@ -520,26 +478,31 @@ struct sftp_request *fxp_open_send(const char *path, int type,
return req;
}
+static struct fxp_handle *fxp_got_handle(struct sftp_packet *pktin)
+{
+ ptrlen id;
+ struct fxp_handle *handle;
+
+ id = get_string(pktin);
+ if (get_err(pktin)) {
+ fxp_internal_error("received malformed FXP_HANDLE");
+ sftp_pkt_free(pktin);
+ return NULL;
+ }
+ handle = snew(struct fxp_handle);
+ handle->hstring = mkstr(id);
+ handle->hlen = id.len;
+ sftp_pkt_free(pktin);
+ return handle;
+}
+
struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
struct sftp_request *req)
{
sfree(req);
if (pktin->type == SSH_FXP_HANDLE) {
- char *hstring;
- struct fxp_handle *handle;
- int len;
-
- if (!sftp_pkt_getstring(pktin, &hstring, &len)) {
- fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
- sftp_pkt_free(pktin);
- return NULL;
- }
- handle = snew(struct fxp_handle);
- handle->hstring = mkstr(make_ptrlen(hstring, len));
- handle->hlen = len;
- sftp_pkt_free(pktin);
- return handle;
+ return fxp_got_handle(pktin);
} else {
fxp_got_status(pktin);
sftp_pkt_free(pktin);
@@ -568,20 +531,7 @@ struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
{
sfree(req);
if (pktin->type == SSH_FXP_HANDLE) {
- char *hstring;
- struct fxp_handle *handle;
- int len;
-
- if (!sftp_pkt_getstring(pktin, &hstring, &len)) {
- fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
- sftp_pkt_free(pktin);
- return NULL;
- }
- handle = snew(struct fxp_handle);
- handle->hstring = mkstr(make_ptrlen(hstring, len));
- handle->hlen = len;
- sftp_pkt_free(pktin);
- return handle;
+ return fxp_got_handle(pktin);
} else {
fxp_got_status(pktin);
sftp_pkt_free(pktin);
@@ -736,18 +686,24 @@ struct sftp_request *fxp_stat_send(const char *fname)
return req;
}
+static int fxp_got_attrs(struct sftp_packet *pktin, struct fxp_attrs *attrs)
+{
+ get_fxp_attrs(pktin, attrs);
+ if (get_err(pktin)) {
+ fxp_internal_error("malformed SSH_FXP_ATTRS packet");
+ sftp_pkt_free(pktin);
+ return 0;
+ }
+ sftp_pkt_free(pktin);
+ return 1;
+}
+
int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
struct fxp_attrs *attrs)
{
sfree(req);
if (pktin->type == SSH_FXP_ATTRS) {
- if (!sftp_pkt_getattrs(pktin, attrs)) {
- fxp_internal_error("malformed SSH_FXP_ATTRS packet");
- sftp_pkt_free(pktin);
- return 0;
- }
- sftp_pkt_free(pktin);
- return 1;
+ return fxp_got_attrs(pktin, attrs);
} else {
fxp_got_status(pktin);
sftp_pkt_free(pktin);
@@ -773,11 +729,7 @@ int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
{
sfree(req);
if (pktin->type == SSH_FXP_ATTRS) {
- if (!sftp_pkt_getattrs(pktin, attrs)) {
- fxp_internal_error("malformed SSH_FXP_ATTRS packet");
- sftp_pkt_free(pktin);
- return 0;
- }
+ return fxp_got_attrs(pktin, attrs);
sftp_pkt_free(pktin);
return 1;
} else {
@@ -871,24 +823,24 @@ int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
{
sfree(req);
if (pktin->type == SSH_FXP_DATA) {
- char *str;
- int rlen;
+ ptrlen data;
- if (!sftp_pkt_getstring(pktin, &str, &rlen)) {
+ data = get_string(pktin);
+ if (get_err(pktin)) {
fxp_internal_error("READ returned malformed SSH_FXP_DATA packet");
sftp_pkt_free(pktin);
return -1;
}
- if (rlen > len || rlen < 0) {
+ if (data.len > len) {
fxp_internal_error("READ returned more bytes than requested");
sftp_pkt_free(pktin);
return -1;
}
- memcpy(buffer, str, rlen);
+ memcpy(buffer, data.ptr, data.len);
sftp_pkt_free(pktin);
- return rlen;
+ return data.len;
} else {
fxp_got_status(pktin);
sftp_pkt_free(pktin);
@@ -920,6 +872,8 @@ struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
struct fxp_names *ret;
unsigned long i;
+ i = get_uint32(pktin);
+
/*
* Sanity-check the number of names. Minimum is obviously
* zero. Maximum is the remaining space in the packet
@@ -928,8 +882,7 @@ struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
* longname, 4 for a set of attribute flags indicating that
* no other attributes are supplied).
*/
- if (!sftp_pkt_getuint32(pktin, &i) ||
- i > (pktin->length-pktin->savedpos)/12) {
+ if (get_err(pktin) || i > get_avail(pktin) / 12) {
fxp_internal_error("malformed FXP_NAME packet");
sftp_pkt_free(pktin);
return NULL;
@@ -950,23 +903,21 @@ struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
ret->nnames = i;
ret->names = snewn(ret->nnames, struct fxp_name);
for (i = 0; i < (unsigned long)ret->nnames; i++) {
- char *str1, *str2;
- int len1, len2;
- if (!sftp_pkt_getstring(pktin, &str1, &len1) ||
- !sftp_pkt_getstring(pktin, &str2, &len2) ||
- !sftp_pkt_getattrs(pktin, &ret->names[i].attrs)) {
- fxp_internal_error("malformed FXP_NAME packet");
- while (i--) {
- sfree(ret->names[i].filename);
- sfree(ret->names[i].longname);
- }
- sfree(ret->names);
- sfree(ret);
- sfree(pktin);
- return NULL;
- }
- ret->names[i].filename = mkstr(make_ptrlen(str1, len1));
- ret->names[i].longname = mkstr(make_ptrlen(str2, len2));
+ ret->names[i].filename = mkstr(get_string(pktin));
+ ret->names[i].longname = mkstr(get_string(pktin));
+ get_fxp_attrs(pktin, &ret->names[i].attrs);
+ }
+
+ if (get_err(pktin)) {
+ fxp_internal_error("malformed FXP_NAME packet");
+ for (i = 0; i < (unsigned long)ret->nnames; i++) {
+ sfree(ret->names[i].filename);
+ sfree(ret->names[i].longname);
+ }
+ sfree(ret->names);
+ sfree(ret);
+ sfree(pktin);
+ return NULL;
}
sftp_pkt_free(pktin);
return ret;
diff --git a/sftp.h b/sftp.h
index b30e49f4..7e93356b 100644
--- a/sftp.h
+++ b/sftp.h
@@ -2,6 +2,7 @@
* sftp.h: definitions for SFTP and the sftp.c routines.
*/
+#include "defs.h"
#include "int64.h"
#define SSH_FXP_INIT 1 /* 0x1 */
From 7535f645aba54c4bdbae0435036bba677c469360 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 27 May 2018 18:13:53 +0100
Subject: [PATCH 326/607] Replace ssh_pkt_get* with BinarySource.
The 'savedpos' field in 'struct Packet', which was already unused on
the output side after I threw away ssh_pkt_addstring_start, is now
unused on the input side too because a BinarySource implementation has
taken over. So it's now completely gone.
---
ssh.c | 951 +++++++++++++++++++++++-----------------------------------
1 file changed, 372 insertions(+), 579 deletions(-)
diff --git a/ssh.c b/ssh.c
index 4e87dc0a..4084f007 100644
--- a/ssh.c
+++ b/ssh.c
@@ -677,20 +677,16 @@ struct Packet {
unsigned long sequence; /* SSH-2 incoming sequence number */
unsigned char *data; /* allocated storage */
unsigned char *body; /* offset of payload within `data' */
- long savedpos; /* dual-purpose saved packet position: see below */
long maxlen; /* amount of storage allocated for `data' */
long encrypted_len; /* for SSH-2 total-size counting */
/*
- * A note on the 'length' and 'savedpos' fields above.
+ * A note on the 'length' field above.
*
* Incoming packets are set up so that pkt->length is measured
* relative to pkt->body, which itself points to a few bytes after
* pkt->data (skipping some uninteresting header fields including
- * the packet type code). The ssh_pkt_get* functions all expect
- * this setup, and they also use pkt->savedpos to indicate how far
- * through the packet being decoded they've got - and that, too,
- * is an offset from pkt->body rather than pkt->data.
+ * the packet type code).
*
* During construction of an outgoing packet, however, pkt->length
* is measured relative to the base pointer pkt->data, and
@@ -707,6 +703,7 @@ struct Packet {
const char *additional_log_text;
BinarySink_IMPLEMENTATION;
+ BinarySource_IMPLEMENTATION;
};
static void ssh1_protocol_setup(Ssh ssh);
@@ -721,9 +718,6 @@ static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, int newwin);
static int ssh_sendbuffer(void *handle);
static int ssh_do_close(Ssh ssh, int notify_exit);
-static unsigned long ssh_pkt_getuint32(struct Packet *pkt);
-static int ssh2_pkt_getbool(struct Packet *pkt);
-static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
static void ssh2_timer(void *ctx, unsigned long now);
static int ssh2_timer_update(Ssh ssh, unsigned long rekey_time);
#ifndef NO_GSSAPI
@@ -1403,14 +1397,14 @@ static struct Packet *ssh_new_packet(void)
return pkt;
}
-static void ssh1_log_incoming_packet(Ssh ssh, struct Packet *pkt)
+static void ssh1_log_incoming_packet(Ssh ssh, const struct Packet *pkt)
{
int nblanks = 0;
struct logblank_t blanks[4];
- char *str;
- int slen;
+ ptrlen str;
+ BinarySource src[1];
- pkt->savedpos = 0;
+ BinarySource_BARE_INIT(src, pkt->body, pkt->length);
if (ssh->logomitdata &&
(pkt->type == SSH1_SMSG_STDOUT_DATA ||
@@ -1418,12 +1412,12 @@ static void ssh1_log_incoming_packet(Ssh ssh, struct Packet *pkt)
pkt->type == SSH1_MSG_CHANNEL_DATA)) {
/* "Session data" packets - omit the data string. */
if (pkt->type == SSH1_MSG_CHANNEL_DATA)
- ssh_pkt_getuint32(pkt); /* skip channel id */
- blanks[nblanks].offset = pkt->savedpos + 4;
- blanks[nblanks].type = PKTLOG_OMIT;
- ssh_pkt_getstring(pkt, &str, &slen);
- if (str) {
- blanks[nblanks].len = slen;
+ get_uint32(src); /* skip channel id */
+ str = get_string(src);
+ if (!get_err(src)) {
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_OMIT;
+ blanks[nblanks].len = str.len;
nblanks++;
}
}
@@ -1433,37 +1427,33 @@ static void ssh1_log_incoming_packet(Ssh ssh, struct Packet *pkt)
0, NULL);
}
-static void ssh1_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
+static void ssh1_log_outgoing_packet(Ssh ssh, const struct Packet *pkt)
{
int nblanks = 0;
struct logblank_t blanks[4];
- char *str;
- int slen;
+ ptrlen str;
+ BinarySource src[1];
/*
* For outgoing packets, pkt->length represents the length of the
* whole packet starting at pkt->data (including some header), and
* pkt->body refers to the point within that where the log-worthy
- * payload begins. However, incoming packets expect pkt->length to
- * represent only the payload length (that is, it's measured from
- * pkt->body not from pkt->data). Temporarily adjust our outgoing
- * packet to conform to the incoming-packet semantics, so that we
- * can analyse it with the ssh_pkt_get functions.
+ * payload begins.
*/
- pkt->length -= (pkt->body - pkt->data);
- pkt->savedpos = 0;
+ BinarySource_BARE_INIT(src, pkt->body,
+ pkt->length - (pkt->body - pkt->data));
if (ssh->logomitdata &&
(pkt->type == SSH1_CMSG_STDIN_DATA ||
pkt->type == SSH1_MSG_CHANNEL_DATA)) {
/* "Session data" packets - omit the data string. */
if (pkt->type == SSH1_MSG_CHANNEL_DATA)
- ssh_pkt_getuint32(pkt); /* skip channel id */
- blanks[nblanks].offset = pkt->savedpos + 4;
- blanks[nblanks].type = PKTLOG_OMIT;
- ssh_pkt_getstring(pkt, &str, &slen);
- if (str) {
- blanks[nblanks].len = slen;
+ get_uint32(src); /* skip channel id */
+ str = get_string(src);
+ if (!get_err(src)) {
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_OMIT;
+ blanks[nblanks].len = str.len;
nblanks++;
}
}
@@ -1489,13 +1479,12 @@ static void ssh1_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
* an X connection without having session blanking enabled is
* likely to leak your cookie into the log.
*/
- pkt->savedpos = 0;
- ssh_pkt_getstring(pkt, &str, &slen);
- blanks[nblanks].offset = pkt->savedpos;
- blanks[nblanks].type = PKTLOG_BLANK;
- ssh_pkt_getstring(pkt, &str, &slen);
- if (str) {
- blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset;
+ get_string(src); /* skip protocol name */
+ str = get_string(src);
+ if (!get_err(src)) {
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_BLANK;
+ blanks[nblanks].len = str.len;
nblanks++;
}
}
@@ -1504,12 +1493,6 @@ static void ssh1_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
ssh1_pkt_type(pkt->data[12]),
pkt->body, pkt->length,
nblanks, blanks, NULL, 0, NULL);
-
- /*
- * Undo the above adjustment of pkt->length, to put the packet
- * back in the state we found it.
- */
- pkt->length += (pkt->body - pkt->data);
}
/*
@@ -1608,7 +1591,7 @@ static void ssh1_rdpkt(Ssh ssh)
if (ssh->logctx)
ssh1_log_incoming_packet(ssh, st->pktin);
- st->pktin->savedpos = 0;
+ BinarySource_INIT(st->pktin, st->pktin->body, st->pktin->length);
pq_push(&ssh->pq_full, st->pktin);
queue_idempotent_callback(&ssh->pq_full_consumer);
@@ -1616,27 +1599,27 @@ static void ssh1_rdpkt(Ssh ssh)
crFinishV;
}
-static void ssh2_log_incoming_packet(Ssh ssh, struct Packet *pkt)
+static void ssh2_log_incoming_packet(Ssh ssh, const struct Packet *pkt)
{
int nblanks = 0;
struct logblank_t blanks[4];
- char *str;
- int slen;
+ ptrlen str;
+ BinarySource src[1];
- pkt->savedpos = 0;
+ BinarySource_BARE_INIT(src, pkt->body, pkt->length);
if (ssh->logomitdata &&
(pkt->type == SSH2_MSG_CHANNEL_DATA ||
pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) {
/* "Session data" packets - omit the data string. */
- ssh_pkt_getuint32(pkt); /* skip channel id */
+ get_uint32(src); /* skip channel id */
if (pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
- ssh_pkt_getuint32(pkt); /* skip extended data type */
- blanks[nblanks].offset = pkt->savedpos + 4;
- blanks[nblanks].type = PKTLOG_OMIT;
- ssh_pkt_getstring(pkt, &str, &slen);
- if (str) {
- blanks[nblanks].len = slen;
+ get_uint32(src); /* skip extended data type */
+ str = get_string(src);
+ if (!get_err(src)) {
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_OMIT;
+ blanks[nblanks].len = str.len;
nblanks++;
}
}
@@ -1647,38 +1630,34 @@ static void ssh2_log_incoming_packet(Ssh ssh, struct Packet *pkt)
0, NULL);
}
-static void ssh2_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
+static void ssh2_log_outgoing_packet(Ssh ssh, const struct Packet *pkt)
{
int nblanks = 0;
struct logblank_t blanks[4];
- char *str;
- int slen;
+ ptrlen str;
+ BinarySource src[1];
/*
* For outgoing packets, pkt->length represents the length of the
* whole packet starting at pkt->data (including some header), and
* pkt->body refers to the point within that where the log-worthy
- * payload begins. However, incoming packets expect pkt->length to
- * represent only the payload length (that is, it's measured from
- * pkt->body not from pkt->data). Temporarily adjust our outgoing
- * packet to conform to the incoming-packet semantics, so that we
- * can analyse it with the ssh_pkt_get functions.
+ * payload begins.
*/
- pkt->length -= (pkt->body - pkt->data);
- pkt->savedpos = 0;
+ BinarySource_BARE_INIT(src, pkt->body,
+ pkt->length - (pkt->body - pkt->data));
if (ssh->logomitdata &&
(pkt->type == SSH2_MSG_CHANNEL_DATA ||
pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) {
/* "Session data" packets - omit the data string. */
- ssh_pkt_getuint32(pkt); /* skip channel id */
+ get_uint32(src); /* skip channel id */
if (pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
- ssh_pkt_getuint32(pkt); /* skip extended data type */
- blanks[nblanks].offset = pkt->savedpos + 4;
- blanks[nblanks].type = PKTLOG_OMIT;
- ssh_pkt_getstring(pkt, &str, &slen);
- if (str) {
- blanks[nblanks].len = slen;
+ get_uint32(src); /* skip extended data type */
+ str = get_string(src);
+ if (!get_err(src)) {
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_OMIT;
+ blanks[nblanks].len = str.len;
nblanks++;
}
}
@@ -1686,25 +1665,23 @@ static void ssh2_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
if (pkt->type == SSH2_MSG_USERAUTH_REQUEST &&
conf_get_int(ssh->conf, CONF_logomitpass)) {
/* If this is a password packet, blank the password(s). */
- pkt->savedpos = 0;
- ssh_pkt_getstring(pkt, &str, &slen);
- ssh_pkt_getstring(pkt, &str, &slen);
- ssh_pkt_getstring(pkt, &str, &slen);
- if (slen == 8 && !memcmp(str, "password", 8)) {
- ssh2_pkt_getbool(pkt);
+ get_string(src); /* username */
+ get_string(src); /* service name */
+ str = get_string(src); /* auth method */
+ if (ptrlen_eq_string(str, "password")) {
+ get_bool(src);
/* Blank the password field. */
- blanks[nblanks].offset = pkt->savedpos;
- blanks[nblanks].type = PKTLOG_BLANK;
- ssh_pkt_getstring(pkt, &str, &slen);
- if (str) {
- blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset;
+ str = get_string(src);
+ if (!get_err(src)) {
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_BLANK;
+ blanks[nblanks].len = str.len;
nblanks++;
/* If there's another password field beyond it (change of
* password), blank that too. */
- ssh_pkt_getstring(pkt, &str, &slen);
- if (str)
- blanks[nblanks-1].len =
- pkt->savedpos - blanks[nblanks].offset;
+ str = get_string(src);
+ if (!get_err(src))
+ blanks[nblanks-1].len = src->pos - blanks[nblanks].offset;
}
}
} else if (ssh->pkt_actx == SSH2_PKTCTX_KBDINTER &&
@@ -1712,16 +1689,13 @@ static void ssh2_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
conf_get_int(ssh->conf, CONF_logomitpass)) {
/* If this is a keyboard-interactive response packet, blank
* the responses. */
- pkt->savedpos = 0;
- ssh_pkt_getuint32(pkt);
- blanks[nblanks].offset = pkt->savedpos;
+ get_uint32(src);
+ blanks[nblanks].offset = src->pos;
blanks[nblanks].type = PKTLOG_BLANK;
- while (1) {
- ssh_pkt_getstring(pkt, &str, &slen);
- if (!str)
- break;
- }
- blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset;
+ do {
+ str = get_string(src);
+ } while (!get_err(src));
+ blanks[nblanks].len = src->pos - blanks[nblanks].offset;
nblanks++;
} else if (pkt->type == SSH2_MSG_CHANNEL_REQUEST &&
conf_get_int(ssh->conf, CONF_logomitpass)) {
@@ -1735,18 +1709,17 @@ static void ssh2_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
* an X connection without having session blanking enabled is
* likely to leak your cookie into the log.
*/
- pkt->savedpos = 0;
- ssh_pkt_getuint32(pkt);
- ssh_pkt_getstring(pkt, &str, &slen);
- if (slen == 7 && !memcmp(str, "x11-req", 0)) {
- ssh2_pkt_getbool(pkt);
- ssh2_pkt_getbool(pkt);
- ssh_pkt_getstring(pkt, &str, &slen);
- blanks[nblanks].offset = pkt->savedpos;
- blanks[nblanks].type = PKTLOG_BLANK;
- ssh_pkt_getstring(pkt, &str, &slen);
- if (str) {
- blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset;
+ get_uint32(src);
+ str = get_string(src);
+ if (ptrlen_eq_string(str, "x11-req")) {
+ get_bool(src);
+ get_bool(src);
+ get_string(src);
+ str = get_string(src);
+ if (!get_err(src)) {
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_BLANK;
+ blanks[nblanks].len = str.len;
nblanks++;
}
}
@@ -1757,12 +1730,6 @@ static void ssh2_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
pkt->body, pkt->length, nblanks, blanks,
&ssh->v2_outgoing_sequence,
pkt->downstream_id, pkt->additional_log_text);
-
- /*
- * Undo the above adjustment of pkt->length, to put the packet
- * back in the state we found it.
- */
- pkt->length += (pkt->body - pkt->data);
}
static void ssh2_rdpkt(Ssh ssh)
@@ -2046,7 +2013,7 @@ static void ssh2_rdpkt(Ssh ssh)
if (ssh->logctx)
ssh2_log_incoming_packet(ssh, st->pktin);
- st->pktin->savedpos = 0;
+ BinarySource_INIT(st->pktin, st->pktin->body, st->pktin->length);
pq_push(&ssh->pq_full, st->pktin);
queue_idempotent_callback(&ssh->pq_full_consumer);
@@ -2110,7 +2077,7 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
if (ssh->logctx)
ssh2_log_incoming_packet(ssh, st->pktin);
- st->pktin->savedpos = 0;
+ BinarySource_INIT(st->pktin, st->pktin->body, st->pktin->length);
pq_push(&ssh->pq_full, st->pktin);
queue_idempotent_callback(&ssh->pq_full_consumer);
@@ -2722,97 +2689,6 @@ void bndebug(char *string, Bignum b)
}
#endif
-/*
- * Packet decode functions for both SSH-1 and SSH-2.
- */
-static unsigned long ssh_pkt_getuint32(struct Packet *pkt)
-{
- unsigned long value;
- if (pkt->length - pkt->savedpos < 4)
- return 0; /* arrgh, no way to decline (FIXME?) */
- value = GET_32BIT(pkt->body + pkt->savedpos);
- pkt->savedpos += 4;
- return value;
-}
-static int ssh2_pkt_getbool(struct Packet *pkt)
-{
- unsigned long value;
- if (pkt->length - pkt->savedpos < 1)
- return 0; /* arrgh, no way to decline (FIXME?) */
- value = pkt->body[pkt->savedpos] != 0;
- pkt->savedpos++;
- return value;
-}
-static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length)
-{
- int len;
- *p = NULL;
- *length = 0;
- if (pkt->length - pkt->savedpos < 4)
- return;
- len = toint(GET_32BIT(pkt->body + pkt->savedpos));
- if (len < 0)
- return;
- *length = len;
- pkt->savedpos += 4;
- if (pkt->length - pkt->savedpos < *length)
- return;
- *p = (char *)(pkt->body + pkt->savedpos);
- pkt->savedpos += *length;
-}
-static void *ssh_pkt_getdata(struct Packet *pkt, int length)
-{
- if (pkt->length - pkt->savedpos < length)
- return NULL;
- pkt->savedpos += length;
- return pkt->body + (pkt->savedpos - length);
-}
-static int ssh1_pkt_getrsakey(struct Packet *pkt, struct RSAKey *key,
- const unsigned char **keystr)
-{
- int j;
-
- j = rsa_ssh1_readpub(pkt->body + pkt->savedpos,
- pkt->length - pkt->savedpos,
- key, keystr, RSA_SSH1_EXPONENT_FIRST);
-
- if (j < 0)
- return FALSE;
-
- pkt->savedpos += j;
- assert(pkt->savedpos < pkt->length);
-
- return TRUE;
-}
-static Bignum ssh1_pkt_getmp(struct Packet *pkt)
-{
- int j;
- Bignum b;
-
- j = ssh1_read_bignum(pkt->body + pkt->savedpos,
- pkt->length - pkt->savedpos, &b);
-
- if (j < 0)
- return NULL;
-
- pkt->savedpos += j;
- return b;
-}
-static Bignum ssh2_pkt_getmp(struct Packet *pkt)
-{
- char *p;
- int length;
- Bignum b;
-
- ssh_pkt_getstring(pkt, &p, &length);
- if (!p)
- return NULL;
- if (p[0] & 0x80)
- return NULL;
- b = bignum_from_bytes(p, length);
- return b;
-}
-
/*
* Helper function to add an SSH-2 signature blob to a packet. Expects
* to be shown the public key blob as well as the signature blob.
@@ -4163,13 +4039,13 @@ static void do_ssh1_login(void *vctx)
struct Packet *pktin;
int i, j, ret;
- unsigned char *ptr;
+ ptrlen pl;
struct MD5Context md5c;
struct do_ssh1_login_state {
int crLine;
int len;
unsigned char *rsabuf;
- const unsigned char *keystr1, *keystr2;
+ ptrlen keystr1, keystr2;
unsigned long supported_ciphers_mask, supported_auths_mask;
int tried_publickey, tried_agent;
int tis_auth_refused, ccard_auth_refused;
@@ -4208,23 +4084,16 @@ static void do_ssh1_login(void *vctx)
logevent("Received public keys");
- ptr = ssh_pkt_getdata(pktin, 8);
- if (!ptr) {
- bombout(("SSH-1 public key packet stopped before random cookie"));
- crStopV;
- }
- memcpy(s->cookie, ptr, 8);
+ pl = get_data(pktin, 8);
+ memcpy(s->cookie, pl.ptr, pl.len);
- if (!ssh1_pkt_getrsakey(pktin, &s->servkey, &s->keystr1) ||
- !ssh1_pkt_getrsakey(pktin, &s->hostkey, &s->keystr2)) {
- bombout(("Failed to read SSH-1 public keys from public key packet"));
- crStopV;
- }
+ get_rsa_ssh1_pub(pktin, &s->servkey, &s->keystr1, RSA_SSH1_EXPONENT_FIRST);
+ get_rsa_ssh1_pub(pktin, &s->hostkey, &s->keystr2, RSA_SSH1_EXPONENT_FIRST);
/*
* Log the host key fingerprint.
*/
- {
+ if (!get_err(pktin)) {
char logmsg[80];
logevent("Host key fingerprint is:");
strcpy(logmsg, " ");
@@ -4234,9 +4103,15 @@ static void do_ssh1_login(void *vctx)
logevent(logmsg);
}
- ssh->v1_remote_protoflags = ssh_pkt_getuint32(pktin);
- s->supported_ciphers_mask = ssh_pkt_getuint32(pktin);
- s->supported_auths_mask = ssh_pkt_getuint32(pktin);
+ ssh->v1_remote_protoflags = get_uint32(pktin);
+ s->supported_ciphers_mask = get_uint32(pktin);
+ s->supported_auths_mask = get_uint32(pktin);
+
+ if (get_err(pktin)) {
+ bombout(("Bad SSH-1 public key packet"));
+ crStopV;
+ }
+
if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA))
s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA);
@@ -4245,8 +4120,8 @@ static void do_ssh1_login(void *vctx)
ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER;
MD5Init(&md5c);
- put_data(&md5c, s->keystr2, s->hostkey.bytes);
- put_data(&md5c, s->keystr1, s->servkey.bytes);
+ put_data(&md5c, s->keystr2.ptr, s->keystr2.len);
+ put_data(&md5c, s->keystr1.ptr, s->keystr1.len);
put_data(&md5c, s->cookie, 8);
MD5Final(s->session_id, &md5c);
@@ -4641,7 +4516,9 @@ static void do_ssh1_login(void *vctx)
continue;
}
logevent("Received RSA challenge");
- if ((s->challenge = ssh1_pkt_getmp(pktin)) == NULL) {
+ s->challenge = get_mp_ssh1(pktin);
+ if (get_err(pktin)) {
+ freebn(s->challenge);
bombout(("Server's RSA challenge was badly formatted"));
crStopV;
}
@@ -4831,7 +4708,9 @@ static void do_ssh1_login(void *vctx)
unsigned char buffer[32];
Bignum challenge, response;
- if ((challenge = ssh1_pkt_getmp(pktin)) == NULL) {
+ challenge = get_mp_ssh1(pktin);
+ if (get_err(pktin)) {
+ freebn(challenge);
bombout(("Server's RSA challenge was badly formatted"));
crStopV;
}
@@ -4890,12 +4769,11 @@ static void do_ssh1_login(void *vctx)
s->tis_auth_refused = 1;
continue;
} else {
- char *challenge;
- int challengelen;
+ ptrlen challenge;
char *instr_suf, *prompt;
- ssh_pkt_getstring(pktin, &challenge, &challengelen);
- if (!challenge) {
+ challenge = get_string(pktin);
+ if (get_err(pktin)) {
bombout(("TIS challenge packet was badly formed"));
crStopV;
}
@@ -4903,11 +4781,11 @@ static void do_ssh1_login(void *vctx)
s->cur_prompt->to_server = TRUE;
s->cur_prompt->name = dupstr("SSH TIS authentication");
/* Prompt heuristic comes from OpenSSH */
- if (memchr(challenge, '\n', challengelen)) {
+ if (memchr(challenge.ptr, '\n', challenge.len)) {
instr_suf = dupstr("");
- prompt = dupprintf("%.*s", challengelen, challenge);
+ prompt = mkstr(challenge);
} else {
- instr_suf = dupprintf("%.*s", challengelen, challenge);
+ instr_suf = mkstr(challenge);
prompt = dupstr("Response: ");
}
s->cur_prompt->instruction =
@@ -4932,12 +4810,11 @@ static void do_ssh1_login(void *vctx)
s->ccard_auth_refused = 1;
continue;
} else {
- char *challenge;
- int challengelen;
+ ptrlen challenge;
char *instr_suf, *prompt;
- ssh_pkt_getstring(pktin, &challenge, &challengelen);
- if (!challenge) {
+ challenge = get_string(pktin);
+ if (get_err(pktin)) {
bombout(("CryptoCard challenge packet was badly formed"));
crStopV;
}
@@ -4946,11 +4823,11 @@ static void do_ssh1_login(void *vctx)
s->cur_prompt->name = dupstr("SSH CryptoCard authentication");
s->cur_prompt->name_reqd = FALSE;
/* Prompt heuristic comes from OpenSSH */
- if (memchr(challenge, '\n', challengelen)) {
+ if (memchr(challenge.ptr, '\n', challenge.len)) {
instr_suf = dupstr("");
- prompt = dupprintf("%.*s", challengelen, challenge);
+ prompt = mkstr(challenge);
} else {
- instr_suf = dupprintf("%.*s", challengelen, challenge);
+ instr_suf = mkstr(challenge);
prompt = dupstr("Response: ");
}
s->cur_prompt->instruction =
@@ -5687,17 +5564,17 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin)
{
- char *string;
- int stringlen, bufsize;
+ ptrlen string;
+ int bufsize;
- ssh_pkt_getstring(pktin, &string, &stringlen);
- if (string == NULL) {
+ string = get_string(pktin);
+ if (get_err(pktin)) {
bombout(("Incoming terminal data packet was badly formed"));
return;
}
bufsize = from_backend(ssh->frontend, pktin->type == SSH1_SMSG_STDERR_DATA,
- string, stringlen);
+ string.ptr, string.len);
if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
ssh->v1_stdout_throttling = 1;
ssh_throttle_conn(ssh, +1);
@@ -5709,7 +5586,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
/* Remote side is trying to open a channel to talk to our
* X-Server. Give them back a local channel number. */
struct ssh_channel *c;
- int remoteid = ssh_pkt_getuint32(pktin);
+ int remoteid = get_uint32(pktin);
logevent("Received X11 connect request");
/* Refuse if X11 forwarding is disabled. */
@@ -5738,7 +5615,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
/* Remote side is trying to open a channel to talk to our
* agent. Give them back a local channel number. */
struct ssh_channel *c;
- int remoteid = ssh_pkt_getuint32(pktin);
+ int remoteid = toint(get_uint32(pktin));
/* Refuse if agent forwarding is disabled. */
if (!ssh->agentfwd_enabled) {
@@ -5765,15 +5642,15 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
* forwarded port. Give them back a local channel number. */
struct ssh_rportfwd pf, *pfp;
int remoteid;
- int hostsize, port;
- char *host;
+ int port;
+ ptrlen host;
char *err;
- remoteid = ssh_pkt_getuint32(pktin);
- ssh_pkt_getstring(pktin, &host, &hostsize);
- port = ssh_pkt_getuint32(pktin);
+ remoteid = toint(get_uint32(pktin));
+ host = get_string(pktin);
+ port = toint(get_uint32(pktin));
- pf.dhost = dupprintf("%.*s", hostsize, NULLTOEMPTY(host));
+ pf.dhost = mkstr(host);
pf.dport = port;
pfp = find234(ssh->rportfwds, &pf, NULL);
@@ -5817,7 +5694,7 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
c = ssh_channel_msg(ssh, pktin);
if (c && c->type == CHAN_SOCKDATA) {
- c->remoteid = ssh_pkt_getuint32(pktin);
+ c->remoteid = get_uint32(pktin);
c->halfopen = FALSE;
c->throttling_conn = 0;
pfd_confirm(c->u.pfd.pf);
@@ -5926,15 +5803,14 @@ static int ssh_channel_data(struct ssh_channel *c, int is_stderr,
static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
{
/* Data sent down one of our channels. */
- char *p;
- int len;
+ ptrlen data;
struct ssh_channel *c;
c = ssh_channel_msg(ssh, pktin);
- ssh_pkt_getstring(pktin, &p, &len);
+ data = get_string(pktin);
if (c) {
- int bufsize = ssh_channel_data(c, FALSE, p, len);
+ int bufsize = ssh_channel_data(c, FALSE, data.ptr, data.len);
if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) {
c->throttling_conn = 1;
ssh_throttle_conn(ssh, +1);
@@ -5944,7 +5820,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
static void ssh1_smsg_exit_status(Ssh ssh, struct Packet *pktin)
{
- ssh->exitcode = ssh_pkt_getuint32(pktin);
+ ssh->exitcode = get_uint32(pktin);
logeventf(ssh, "Server sent command exit status %d", ssh->exitcode);
send_packet(ssh, SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
/*
@@ -6190,22 +6066,14 @@ static void do_ssh1_connection(void *vctx)
*/
static void ssh1_msg_debug(Ssh ssh, struct Packet *pktin)
{
- char *msg;
- int msglen;
-
- ssh_pkt_getstring(pktin, &msg, &msglen);
- logeventf(ssh, "Remote debug message: %.*s", msglen, NULLTOEMPTY(msg));
+ ptrlen msg = get_string(pktin);
+ logeventf(ssh, "Remote debug message: %.*s", PTRLEN_PRINTF(msg));
}
static void ssh1_msg_disconnect(Ssh ssh, struct Packet *pktin)
{
- /* log reason code in disconnect message */
- char *msg;
- int msglen;
-
- ssh_pkt_getstring(pktin, &msg, &msglen);
- bombout(("Server sent disconnect message:\n\"%.*s\"",
- msglen, NULLTOEMPTY(msg)));
+ ptrlen msg = get_string(pktin);
+ bombout(("Server sent disconnect message:\n\"%.*s\"", PTRLEN_PRINTF(msg)));
}
static void ssh_msg_ignore(Ssh ssh, struct Packet *pktin)
@@ -6566,8 +6434,8 @@ static void do_ssh2_transport(void *vctx)
int csmac_etm_tobe, scmac_etm_tobe;
const struct ssh_compress *cscomp_tobe;
const struct ssh_compress *sccomp_tobe;
- char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint;
- int hostkeylen, siglen, rsakeylen;
+ ptrlen hostkeydata, sigdata;
+ char *keystr, *fingerprint;
ssh_key *hkey; /* actual host key */
struct RSAKey *rsakey; /* for RSA kex */
struct ec_key *eckey; /* for ECDH kex */
@@ -6998,8 +6866,8 @@ static void do_ssh2_transport(void *vctx)
* to.
*/
{
- char *str;
- int i, j, len;
+ ptrlen str;
+ int i, j;
if (pktin->type != SSH2_MSG_KEXINIT) {
bombout(("expected key exchange packet from server"));
@@ -7016,12 +6884,12 @@ static void do_ssh2_transport(void *vctx)
s->warn_kex = s->warn_hk = FALSE;
s->warn_cscipher = s->warn_sccipher = FALSE;
- pktin->savedpos += 16; /* skip garbage cookie */
+ get_data(pktin, 16); /* skip garbage cookie */
s->guessok = FALSE;
for (i = 0; i < NKEXLIST; i++) {
- ssh_pkt_getstring(pktin, &str, &len);
- if (!str) {
+ str = get_string(pktin);
+ if (get_err(pktin)) {
bombout(("KEXINIT packet was incomplete"));
crStopV;
}
@@ -7046,12 +6914,13 @@ static void do_ssh2_transport(void *vctx)
for (j = 0; j < MAXKEXLIST; j++) {
struct kexinit_algorithm *alg = &s->kexlists[i][j];
if (alg->name == NULL) break;
- if (in_commasep_string(alg->name, str, len)) {
+ if (in_commasep_string(alg->name, str.ptr, str.len)) {
/* We've found a matching algorithm. */
if (i == KEXLIST_KEX || i == KEXLIST_HOSTKEY) {
/* Check if we might need to ignore first kex pkt */
if (j != 0 ||
- !first_in_commasep_string(alg->name, str, len))
+ !first_in_commasep_string(alg->name,
+ str.ptr, str.len))
s->guessok = FALSE;
}
if (i == KEXLIST_KEX) {
@@ -7089,11 +6958,12 @@ static void do_ssh2_transport(void *vctx)
goto matched;
}
if ((i == KEXLIST_CSCOMP || i == KEXLIST_SCCOMP) &&
- in_commasep_string(alg->u.comp->delayed_name, str, len))
+ in_commasep_string(alg->u.comp->delayed_name,
+ str.ptr, str.len))
s->pending_compression = TRUE; /* try this later */
}
bombout(("Couldn't agree a %s (available: %.*s)",
- kexlist_descr[i], len, str));
+ kexlist_descr[i], PTRLEN_PRINTF(str)));
crStopV;
matched:;
@@ -7118,7 +6988,7 @@ static void do_ssh2_transport(void *vctx)
for (j = 0; j < lenof(hostkey_algs); j++) {
if (hostkey_algs[j].alg != ssh->hostkey &&
in_commasep_string(hostkey_algs[j].alg->name,
- str, len) &&
+ str.ptr, str.len) &&
!have_ssh_host_key(ssh->savedhost, ssh->savedport,
hostkey_algs[j].alg->keytype)) {
ssh->uncert_hostkeys[ssh->n_uncert_hostkeys++] = j;
@@ -7131,9 +7001,9 @@ static void do_ssh2_transport(void *vctx)
logevent("Server supports delayed compression; "
"will try this later");
}
- ssh_pkt_getstring(pktin, &str, &len); /* client->server language */
- ssh_pkt_getstring(pktin, &str, &len); /* server->client language */
- s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok;
+ get_string(pktin); /* client->server language */
+ get_string(pktin); /* server->client language */
+ s->ignorepkt = get_bool(pktin) && !s->guessok;
ssh->exhash = ssh->kex->hash->init();
ssh->exhash_bs = ssh->kex->hash->sink(ssh->exhash);
@@ -7315,9 +7185,11 @@ static void do_ssh2_transport(void *vctx)
bombout(("expected key exchange group packet from server"));
crStopV;
}
- s->p = ssh2_pkt_getmp(pktin);
- s->g = ssh2_pkt_getmp(pktin);
- if (!s->p || !s->g) {
+ s->p = get_mp_ssh2(pktin);
+ s->g = get_mp_ssh2(pktin);
+ if (get_err(pktin)) {
+ freebn(s->p);
+ freebn(s->g);
bombout(("unable to read mp-ints from incoming group packet"));
crStopV;
}
@@ -7351,20 +7223,12 @@ static void do_ssh2_transport(void *vctx)
crStopV;
}
set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
- ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
- if (!s->hostkeydata) {
- bombout(("unable to parse key exchange reply packet"));
- crStopV;
- }
+ s->hostkeydata = get_string(pktin);
s->hkey = ssh->hostkey->newkey(ssh->hostkey,
- s->hostkeydata, s->hostkeylen);
- s->f = ssh2_pkt_getmp(pktin);
- if (!s->f) {
- bombout(("unable to parse key exchange reply packet"));
- crStopV;
- }
- ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
- if (!s->sigdata) {
+ s->hostkeydata.ptr, s->hostkeydata.len);
+ s->f = get_mp_ssh2(pktin);
+ s->sigdata = get_string(pktin);
+ if (get_err(pktin)) {
bombout(("unable to parse key exchange reply packet"));
crStopV;
}
@@ -7382,7 +7246,7 @@ static void do_ssh2_transport(void *vctx)
* involve user interaction. */
set_busy_status(ssh->frontend, BUSY_NOT);
- put_string(ssh->exhash_bs, s->hostkeydata, s->hostkeylen);
+ put_stringpl(ssh->exhash_bs, s->hostkeydata);
if (dh_is_gex(ssh->kex)) {
if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX))
put_uint32(ssh->exhash_bs, DH_MIN_SIZE);
@@ -7430,14 +7294,10 @@ static void do_ssh2_transport(void *vctx)
crStopV;
}
- ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
- if (!s->hostkeydata) {
- bombout(("unable to parse ECDH reply packet"));
- crStopV;
- }
- put_string(ssh->exhash_bs, s->hostkeydata, s->hostkeylen);
+ s->hostkeydata = get_string(pktin);
+ put_stringpl(ssh->exhash_bs, s->hostkeydata);
s->hkey = ssh->hostkey->newkey(ssh->hostkey,
- s->hostkeydata, s->hostkeylen);
+ s->hostkeydata.ptr, s->hostkeydata.len);
{
strbuf *pubpoint = strbuf_new();
@@ -7447,24 +7307,18 @@ static void do_ssh2_transport(void *vctx)
}
{
- char *keydata;
- int keylen;
- ssh_pkt_getstring(pktin, &keydata, &keylen);
- if (!keydata) {
- bombout(("unable to parse ECDH reply packet"));
- crStopV;
- }
- put_string(ssh->exhash_bs, keydata, keylen);
- s->K = ssh_ecdhkex_getkey(s->eckey, keydata, keylen);
- if (!s->K) {
+ ptrlen keydata = get_string(pktin);
+ put_stringpl(ssh->exhash_bs, keydata);
+ s->K = ssh_ecdhkex_getkey(s->eckey, keydata.ptr, keydata.len);
+ if (!get_err(pktin) && !s->K) {
ssh_ecdhkex_freekey(s->eckey);
bombout(("point received in ECDH was not valid"));
crStopV;
}
}
- ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
- if (!s->sigdata) {
+ s->sigdata = get_string(pktin);
+ if (get_err(pktin)) {
bombout(("unable to parse key exchange reply packet"));
crStopV;
}
@@ -7472,14 +7326,11 @@ static void do_ssh2_transport(void *vctx)
ssh_ecdhkex_freekey(s->eckey);
#ifndef NO_GSSAPI
} else if (ssh->kex->main_type == KEXTYPE_GSS) {
- int len;
- char *data;
+ ptrlen data;
ssh->pkt_kctx = SSH2_PKTCTX_GSSKEX;
s->init_token_sent = 0;
s->complete_rcvd = 0;
- s->hostkeydata = NULL;
- s->hostkeylen = 0;
s->hkey = NULL;
s->fingerprint = NULL;
s->keystr = NULL;
@@ -7523,9 +7374,9 @@ static void do_ssh2_transport(void *vctx)
bombout(("expected key exchange group packet from server"));
crStopV;
}
- s->p = ssh2_pkt_getmp(pktin);
- s->g = ssh2_pkt_getmp(pktin);
- if (!s->p || !s->g) {
+ s->p = get_mp_ssh2(pktin);
+ s->g = get_mp_ssh2(pktin);
+ if (get_err(pktin)) {
bombout(("unable to read mp-ints from incoming group packet"));
crStopV;
}
@@ -7625,34 +7476,35 @@ static void do_ssh2_transport(void *vctx)
(pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
switch (pktin->type) {
case SSH2_MSG_KEXGSS_CONTINUE:
- ssh_pkt_getstring(pktin, &data, &len);
- s->gss_rcvtok.value = data;
- s->gss_rcvtok.length = len;
+ data = get_string(pktin);
+ s->gss_rcvtok.value = (char *)data.ptr;
+ s->gss_rcvtok.length = data.len;
continue;
case SSH2_MSG_KEXGSS_COMPLETE:
s->complete_rcvd = 1;
- s->f = ssh2_pkt_getmp(pktin);
- ssh_pkt_getstring(pktin, &data, &len);
- s->mic.value = data;
- s->mic.length = len;
+ s->f = get_mp_ssh2(pktin);
+ data = get_string(pktin);
+ s->mic.value = (char *)data.ptr;
+ s->mic.length = data.len;
/* Save expiration time of cred when delegating */
if (s->gss_delegate && s->gss_cred_expiry != GSS_NO_EXPIRATION)
ssh->gss_cred_expiry = s->gss_cred_expiry;
/* If there's a final token we loop to consume it */
- if (ssh2_pkt_getbool(pktin)) {
- ssh_pkt_getstring(pktin, &data, &len);
- s->gss_rcvtok.value = data;
- s->gss_rcvtok.length = len;
+ if (get_bool(pktin)) {
+ data = get_string(pktin);
+ s->gss_rcvtok.value = (char *)data.ptr;
+ s->gss_rcvtok.length = data.len;
continue;
}
break;
case SSH2_MSG_KEXGSS_HOSTKEY:
- ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+ s->hostkeydata = get_string(pktin);
if (ssh->hostkey) {
s->hkey = ssh->hostkey->newkey(ssh->hostkey,
- s->hostkeydata,
- s->hostkeylen);
- put_string(ssh->exhash_bs, s->hostkeydata, s->hostkeylen);
+ s->hostkeydata.ptr,
+ s->hostkeydata.len);
+ put_string(ssh->exhash_bs,
+ s->hostkeydata.ptr, s->hostkeydata.len);
}
/*
* Can't loop as we have no token to pass to
@@ -7670,13 +7522,13 @@ static void do_ssh2_transport(void *vctx)
* that will produce the most useful information for
* us.
*/
- ssh_pkt_getuint32(pktin); /* server's major status */
- ssh_pkt_getuint32(pktin); /* server's minor status */
- ssh_pkt_getstring(pktin, &data, &len);
+ get_uint32(pktin); /* server's major status */
+ get_uint32(pktin); /* server's minor status */
+ data = get_string(pktin);
logeventf(ssh, "GSSAPI key exchange failed; "
- "server's message: %.*s", len, data);
+ "server's message: %.*s", PTRLEN_PRINTF(data));
/* Language tag, but we have no use for it */
- ssh_pkt_getstring(pktin, &data, &len);
+ get_string(pktin);
/*
* Wait for an error token, if there is one, or the
* server's disconnect. The error token, if there
@@ -7726,6 +7578,8 @@ static void do_ssh2_transport(void *vctx)
}
#endif
} else {
+ ptrlen rsakeydata;
+
assert(ssh->kex->main_type == KEXTYPE_RSA);
logeventf(ssh, "Doing RSA key exchange with hash %s",
ssh->kex->hash->text_name);
@@ -7740,34 +7594,20 @@ static void do_ssh2_transport(void *vctx)
crStopV;
}
- ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
- if (!s->hostkeydata) {
- bombout(("unable to parse RSA public key packet"));
- crStopV;
- }
- put_string(ssh->exhash_bs, s->hostkeydata, s->hostkeylen);
+ s->hostkeydata = get_string(pktin);
+ put_stringpl(ssh->exhash_bs, s->hostkeydata);
s->hkey = ssh->hostkey->newkey(ssh->hostkey,
- s->hostkeydata, s->hostkeylen);
+ s->hostkeydata.ptr, s->hostkeydata.len);
- {
- char *keydata;
- ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);
- if (!keydata) {
- bombout(("unable to parse RSA public key packet"));
- crStopV;
- }
- s->rsakeydata = snewn(s->rsakeylen, char);
- memcpy(s->rsakeydata, keydata, s->rsakeylen);
- }
+ rsakeydata = get_string(pktin);
- s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen);
+ s->rsakey = ssh_rsakex_newkey(rsakeydata.ptr, rsakeydata.len);
if (!s->rsakey) {
- sfree(s->rsakeydata);
bombout(("unable to parse RSA public key from server"));
crStopV;
}
- put_string(ssh->exhash_bs, s->rsakeydata, s->rsakeylen);
+ put_stringpl(ssh->exhash_bs, rsakeydata);
/*
* Next, set up a shared secret K, of precisely KLEN -
@@ -7823,18 +7663,15 @@ static void do_ssh2_transport(void *vctx)
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
- sfree(s->rsakeydata);
bombout(("expected signature packet from server"));
crStopV;
}
- ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
- if (!s->sigdata) {
+ s->sigdata = get_string(pktin);
+ if (get_err(pktin)) {
bombout(("unable to parse signature packet"));
crStopV;
}
-
- sfree(s->rsakeydata);
}
put_mp_ssh2(ssh->exhash_bs, s->K);
@@ -7899,7 +7736,7 @@ static void do_ssh2_transport(void *vctx)
crStopV;
}
- if (!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
+ if (!ssh->hostkey->verifysig(s->hkey, s->sigdata.ptr, s->sigdata.len,
s->exchange_hash,
ssh->kex->hash->hlen)) {
#ifndef FUZZING
@@ -8712,7 +8549,7 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin)
*/
static struct ssh_channel *ssh_channel_msg(Ssh ssh, struct Packet *pktin)
{
- unsigned localid = ssh_pkt_getuint32(pktin);
+ unsigned localid = get_uint32(pktin);
struct ssh_channel *c;
int halfopen_ok;
@@ -8797,31 +8634,30 @@ static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
if (!c)
return;
if (!(c->closes & CLOSES_SENT_EOF)) {
- c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
+ c->v.v2.remwindow += get_uint32(pktin);
ssh2_try_send_and_unthrottle(ssh, c);
}
}
static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
{
- char *data;
- int length;
+ ptrlen data;
unsigned ext_type = 0; /* 0 means not extended */
struct ssh_channel *c;
c = ssh_channel_msg(ssh, pktin);
if (!c)
return;
if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
- ext_type = ssh_pkt_getuint32(pktin);
- ssh_pkt_getstring(pktin, &data, &length);
- if (data) {
+ ext_type = get_uint32(pktin);
+ data = get_string(pktin);
+ if (!get_err(pktin)) {
int bufsize;
- c->v.v2.locwindow -= length;
- c->v.v2.remlocwin -= length;
+ c->v.v2.locwindow -= data.len;
+ c->v.v2.remlocwin -= data.len;
if (ext_type != 0 && ext_type != SSH2_EXTENDED_DATA_STDERR)
- length = 0; /* Don't do anything with unknown extended data. */
+ data.len = 0; /* Don't do anything with unknown extended data. */
bufsize = ssh_channel_data(c, ext_type == SSH2_EXTENDED_DATA_STDERR,
- data, length);
+ data.ptr, data.len);
/*
* If it looks like the remote end hit the end of its window,
* and we didn't want it to do that, think about using a
@@ -9135,10 +8971,10 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
if (!c)
return;
assert(c->halfopen); /* ssh_channel_msg will have enforced this */
- c->remoteid = ssh_pkt_getuint32(pktin);
+ c->remoteid = get_uint32(pktin);
c->halfopen = FALSE;
- c->v.v2.remwindow = ssh_pkt_getuint32(pktin);
- c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
+ c->v.v2.remwindow = get_uint32(pktin);
+ c->v.v2.remmaxpkt = get_uint32(pktin);
if (c->type == CHAN_SOCKDATA) {
assert(c->u.pfd.pf != NULL);
@@ -9183,10 +9019,9 @@ static char *ssh2_channel_open_failure_error_text(struct Packet *pktin)
unsigned reason_code;
const char *reason_code_string;
char reason_code_buf[256];
- char *reason_string;
- int reason_length;
+ ptrlen reason;
- reason_code = ssh_pkt_getuint32(pktin);
+ reason_code = get_uint32(pktin);
if (reason_code < lenof(reasons) && reasons[reason_code]) {
reason_code_string = reasons[reason_code];
} else {
@@ -9194,10 +9029,9 @@ static char *ssh2_channel_open_failure_error_text(struct Packet *pktin)
sprintf(reason_code_buf, "unknown reason code %#x", reason_code);
}
- ssh_pkt_getstring(pktin, &reason_string, &reason_length);
+ reason = get_string(pktin);
- return dupprintf("%s [%.*s]", reason_code_string,
- reason_length, NULLTOEMPTY(reason_string));
+ return dupprintf("%s [%.*s]", reason_code_string, PTRLEN_PRINTF(reason));
}
static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
@@ -9241,8 +9075,8 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
{
- char *type;
- int typelen, want_reply;
+ ptrlen type;
+ int want_reply;
int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
struct ssh_channel *c;
struct Packet *pktout;
@@ -9250,8 +9084,8 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
c = ssh_channel_msg(ssh, pktin);
if (!c)
return;
- ssh_pkt_getstring(pktin, &type, &typelen);
- want_reply = ssh2_pkt_getbool(pktin);
+ type = get_string(pktin);
+ want_reply = get_bool(pktin);
if (c->closes & CLOSES_SENT_CLOSE) {
/*
@@ -9273,78 +9107,34 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
* We recognise "exit-status" and "exit-signal" on
* the primary channel.
*/
- if (typelen == 11 &&
- !memcmp(type, "exit-status", 11)) {
+ if (ptrlen_eq_string(type, "exit-status")) {
- ssh->exitcode = ssh_pkt_getuint32(pktin);
+ ssh->exitcode = get_uint32(pktin);
logeventf(ssh, "Server sent command exit status %d",
ssh->exitcode);
reply = SSH2_MSG_CHANNEL_SUCCESS;
- } else if (typelen == 11 &&
- !memcmp(type, "exit-signal", 11)) {
-
- int is_plausible = TRUE, is_int = FALSE;
+ } else if (ptrlen_eq_string(type, "exit-signal")) {
char *fmt_sig = NULL, *fmt_msg = NULL;
- char *msg;
- int msglen = 0, core = FALSE;
- /* ICK: older versions of OpenSSH (e.g. 3.4p1)
+ ptrlen errmsg;
+ int core = FALSE;
+ int format, exitcode;
+
+ /* ICK: older versions of OpenSSH (e.g. 3.4p1)
* provide an `int' for the signal, despite its
* having been a `string' in the drafts of RFC 4254 since at
* least 2001. (Fixed in session.c 1.147.) Try to
* infer which we can safely parse it as. */
- {
- unsigned char *p = pktin->body +
- pktin->savedpos;
- long len = pktin->length - pktin->savedpos;
- unsigned long num = GET_32BIT(p); /* what is it? */
- /* If it's 0, it hardly matters; assume string */
- if (num == 0) {
- is_int = FALSE;
- } else {
- int maybe_int = FALSE, maybe_str = FALSE;
-#define CHECK_HYPOTHESIS(offset, result) \
- do \
- { \
- int q = toint(offset); \
- if (q >= 0 && q+4 <= len) { \
- q = toint(q + 4 + GET_32BIT(p+q)); \
- if (q >= 0 && q+4 <= len && \
- ((q = toint(q + 4 + GET_32BIT(p+q))) != 0) && \
- q == len) \
- result = TRUE; \
- } \
- } while(0)
- CHECK_HYPOTHESIS(4+1, maybe_int);
- CHECK_HYPOTHESIS(4+num+1, maybe_str);
-#undef CHECK_HYPOTHESIS
- if (maybe_int && !maybe_str)
- is_int = TRUE;
- else if (!maybe_int && maybe_str)
- is_int = FALSE;
- else
- /* Crikey. Either or neither. Panic. */
- is_plausible = FALSE;
- }
- }
- ssh->exitcode = 128; /* means `unknown signal' */
- if (is_plausible) {
- if (is_int) {
- /* Old non-standard OpenSSH. */
- int signum = ssh_pkt_getuint32(pktin);
- fmt_sig = dupprintf(" %d", signum);
- ssh->exitcode = 128 + signum;
- } else {
- /* As per RFC 4254. */
- char *sig;
- int siglen;
- ssh_pkt_getstring(pktin, &sig, &siglen);
- /* Signal name isn't supposed to be blank, but
- * let's cope gracefully if it is. */
- if (siglen) {
- fmt_sig = dupprintf(" \"%.*s\"",
- siglen, sig);
- }
+
+ size_t startpos = BinarySource_UPCAST(pktin)->pos;
+
+ for (format = 0; format < 2; format++) {
+ BinarySource_UPCAST(pktin)->pos = startpos;
+ BinarySource_UPCAST(pktin)->err = BSE_NO_ERROR;
+
+ if (format == 0) { /* standard string-based format */
+ ptrlen signame = get_string(pktin);
+ fmt_sig = dupprintf(" \"%.*s\"", PTRLEN_PRINTF(signame));
/*
* Really hideous method of translating the
@@ -9355,8 +9145,8 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
if (0)
;
#define TRANSLATE_SIGNAL(s) \
- else if (siglen == lenof(#s)-1 && !memcmp(sig, #s, siglen)) \
- ssh->exitcode = 128 + SIG ## s
+ else if (ptrlen_eq_string(signame, #s)) \
+ exitcode = 128 + SIG ## s
#ifdef SIGABRT
TRANSLATE_SIGNAL(ABRT);
#endif
@@ -9398,15 +9188,32 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
#endif
#undef TRANSLATE_SIGNAL
else
- ssh->exitcode = 128;
- }
- core = ssh2_pkt_getbool(pktin);
- ssh_pkt_getstring(pktin, &msg, &msglen);
- if (msglen) {
- fmt_msg = dupprintf(" (\"%.*s\")", msglen, msg);
- }
- /* ignore lang tag */
- } /* else don't attempt to parse */
+ exitcode = 128;
+ } else { /* nonstandard integer format */
+ unsigned signum = get_uint32(pktin);
+ fmt_sig = dupprintf(" %u", signum);
+ exitcode = 128 + signum;
+ }
+
+ core = get_bool(pktin);
+ errmsg = get_string(pktin); /* error message */
+ get_string(pktin); /* language tag */
+ if (!get_err(pktin) && get_avail(pktin) == 0)
+ break; /* successful parse */
+
+ sfree(fmt_sig);
+ }
+
+ if (format == 2) {
+ fmt_sig = NULL;
+ exitcode = 128;
+ }
+
+ ssh->exitcode = exitcode;
+ if (errmsg.len) {
+ fmt_msg = dupprintf(" (\"%.*s\")", PTRLEN_PRINTF(errmsg));
+ }
+
logeventf(ssh, "Server exited on signal%s%s%s",
fmt_sig ? fmt_sig : "",
core ? " (core dumped)" : "",
@@ -9414,7 +9221,6 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
sfree(fmt_sig);
sfree(fmt_msg);
reply = SSH2_MSG_CHANNEL_SUCCESS;
-
}
} else {
/*
@@ -9434,12 +9240,11 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
static void ssh2_msg_global_request(Ssh ssh, struct Packet *pktin)
{
- char *type;
- int typelen, want_reply;
+ int want_reply;
struct Packet *pktout;
- ssh_pkt_getstring(pktin, &type, &typelen);
- want_reply = ssh2_pkt_getbool(pktin);
+ get_string(pktin); /* ignore request type (see below) */
+ want_reply = get_bool(pktin);
/*
* We currently don't support any global requests
@@ -9479,10 +9284,7 @@ void ssh_sharing_remove_x11_display(Ssh ssh, struct X11FakeAuth *auth)
static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
{
- char *type;
- int typelen;
- char *peeraddr;
- int peeraddrlen;
+ ptrlen type;
int peerport;
const char *error = NULL;
struct ssh_channel *c;
@@ -9490,20 +9292,17 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
unsigned our_winsize_override = 0;
struct Packet *pktout;
- ssh_pkt_getstring(pktin, &type, &typelen);
+ type = get_string(pktin);
c = snew(struct ssh_channel);
c->ssh = ssh;
- remid = ssh_pkt_getuint32(pktin);
- winsize = ssh_pkt_getuint32(pktin);
- pktsize = ssh_pkt_getuint32(pktin);
+ remid = get_uint32(pktin);
+ winsize = get_uint32(pktin);
+ pktsize = get_uint32(pktin);
- if (typelen == 3 && !memcmp(type, "x11", 3)) {
- char *addrstr;
-
- ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
- addrstr = dupprintf("%.*s", peeraddrlen, NULLTOEMPTY(peeraddr));
- peerport = ssh_pkt_getuint32(pktin);
+ if (ptrlen_eq_string(type, "x11")) {
+ char *addrstr = mkstr(get_string(pktin));
+ peerport = get_uint32(pktin);
logeventf(ssh, "Received X11 connect request from %s:%d",
addrstr, peerport);
@@ -9531,20 +9330,18 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
}
sfree(addrstr);
- } else if (typelen == 15 &&
- !memcmp(type, "forwarded-tcpip", 15)) {
+ } else if (ptrlen_eq_string(type, "forwarded-tcpip")) {
struct ssh_rportfwd pf, *realpf;
- char *shost;
- int shostlen;
- ssh_pkt_getstring(pktin, &shost, &shostlen);/* skip address */
- pf.shost = dupprintf("%.*s", shostlen, NULLTOEMPTY(shost));
- pf.sport = ssh_pkt_getuint32(pktin);
- ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
- peerport = ssh_pkt_getuint32(pktin);
+ ptrlen peeraddr;
+
+ pf.shost = mkstr(get_string(pktin));
+ pf.sport = get_uint32(pktin);
+ peeraddr = get_string(pktin);
+ peerport = get_uint32(pktin);
realpf = find234(ssh->rportfwds, &pf, NULL);
logeventf(ssh, "Received remote port %s:%d open request "
"from %.*s:%d", pf.shost, pf.sport,
- peeraddrlen, NULLTOEMPTY(peeraddr), peerport);
+ PTRLEN_PRINTF(peeraddr), peerport);
sfree(pf.shost);
if (realpf == NULL) {
@@ -9578,8 +9375,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
c->type = CHAN_SOCKDATA;
}
}
- } else if (typelen == 22 &&
- !memcmp(type, "auth-agent@openssh.com", 22)) {
+ } else if (ptrlen_eq_string(type, "auth-agent@openssh.com")) {
if (!ssh->agentfwd_enabled)
error = "Agent forwarding is not enabled";
else {
@@ -9665,11 +9461,9 @@ static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
/* Arbitrary limit to prevent unbounded inflation of buffer */
if (conf_get_int(ssh->conf, CONF_ssh_show_banner) &&
bufchain_size(&ssh->banner) <= 131072) {
- char *banner = NULL;
- int size = 0;
- ssh_pkt_getstring(pktin, &banner, &size);
- if (banner)
- bufchain_add(&ssh->banner, banner, size);
+ ptrlen banner = get_string(pktin);
+ if (banner.len)
+ bufchain_add(&ssh->banner, banner.ptr, banner.len);
}
}
@@ -10287,8 +10081,10 @@ static void do_ssh2_userauth(void *vctx)
}
while (1) {
- char *methods = NULL;
- int methlen = 0;
+ ptrlen methods;
+
+ methods.ptr = "";
+ methods.len = 0;
/*
* Wait for the result of the last authentication request.
@@ -10337,8 +10133,8 @@ static void do_ssh2_userauth(void *vctx)
* helpfully try next.
*/
if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
- ssh_pkt_getstring(pktin, &methods, &methlen);
- if (!ssh2_pkt_getbool(pktin)) {
+ methods = get_string(pktin);
+ if (!get_bool(pktin)) {
/*
* We have received an unequivocal Access
* Denied. This can translate to a variety of
@@ -10405,19 +10201,23 @@ static void do_ssh2_userauth(void *vctx)
}
s->can_pubkey =
- in_commasep_string("publickey", methods, methlen);
+ in_commasep_string("publickey", methods.ptr, methods.len);
s->can_passwd =
- in_commasep_string("password", methods, methlen);
- s->can_keyb_inter = conf_get_int(ssh->conf, CONF_try_ki_auth) &&
- in_commasep_string("keyboard-interactive", methods, methlen);
+ in_commasep_string("password", methods.ptr, methods.len);
+ s->can_keyb_inter =
+ conf_get_int(ssh->conf, CONF_try_ki_auth) &&
+ in_commasep_string("keyboard-interactive",
+ methods.ptr, methods.len);
#ifndef NO_GSSAPI
s->can_gssapi =
conf_get_int(ssh->conf, CONF_try_gssapi_auth) &&
- in_commasep_string("gssapi-with-mic", methods, methlen) &&
+ in_commasep_string("gssapi-with-mic",
+ methods.ptr, methods.len) &&
ssh->gsslibs->nlibraries > 0;
s->can_gssapi_keyex_auth =
conf_get_int(ssh->conf, CONF_try_gssapi_kex) &&
- in_commasep_string("gssapi-keyex", methods, methlen) &&
+ in_commasep_string("gssapi-keyex",
+ methods.ptr, methods.len) &&
ssh->gsslibs->nlibraries > 0 &&
ssh->gss_ctx;
#endif
@@ -10752,8 +10552,7 @@ static void do_ssh2_userauth(void *vctx)
/* gssapi-with-mic authentication */
- int len;
- char *data;
+ ptrlen data;
s->type = AUTH_TYPE_GSSAPI;
s->tried_gssapi = TRUE;
@@ -10794,9 +10593,9 @@ static void do_ssh2_userauth(void *vctx)
/* check returned packet ... */
- ssh_pkt_getstring(pktin, &data, &len);
- s->gss_rcvtok.value = data;
- s->gss_rcvtok.length = len;
+ data = get_string(pktin);
+ s->gss_rcvtok.value = (char *)data.ptr;
+ s->gss_rcvtok.length = data.len;
if (s->gss_rcvtok.length != s->gss_buf.length + 2 ||
((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE ||
((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length ||
@@ -10886,9 +10685,9 @@ static void do_ssh2_userauth(void *vctx)
pq_push_front(&ssh->pq_ssh2_userauth, pktin);
break;
}
- ssh_pkt_getstring(pktin, &data, &len);
- s->gss_rcvtok.value = data;
- s->gss_rcvtok.length = len;
+ data = get_string(pktin);
+ s->gss_rcvtok.value = (char *)data.ptr;
+ s->gss_rcvtok.length = data.len;
}
} while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
@@ -10946,47 +10745,43 @@ static void do_ssh2_userauth(void *vctx)
*/
while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
- char *name, *inst, *lang;
- int name_len, inst_len, lang_len;
+ ptrlen name, inst;
int i;
/*
* We've got a fresh USERAUTH_INFO_REQUEST.
* Get the preamble and start building a prompt.
*/
- ssh_pkt_getstring(pktin, &name, &name_len);
- ssh_pkt_getstring(pktin, &inst, &inst_len);
- ssh_pkt_getstring(pktin, &lang, &lang_len);
+ name = get_string(pktin);
+ inst = get_string(pktin);
+ get_string(pktin); /* skip language tag */
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
/*
* Get any prompt(s) from the packet.
*/
- s->num_prompts = ssh_pkt_getuint32(pktin);
+ s->num_prompts = get_uint32(pktin);
for (i = 0; i < s->num_prompts; i++) {
- char *prompt;
- int prompt_len;
+ ptrlen prompt;
int echo;
static char noprompt[] =
": ";
- ssh_pkt_getstring(pktin, &prompt, &prompt_len);
- echo = ssh2_pkt_getbool(pktin);
- if (!prompt_len) {
- prompt = noprompt;
- prompt_len = lenof(noprompt)-1;
+ prompt = get_string(pktin);
+ echo = get_bool(pktin);
+ if (!prompt.len) {
+ prompt.ptr = noprompt;
+ prompt.len = lenof(noprompt)-1;
}
- add_prompt(s->cur_prompt,
- dupprintf("%.*s", prompt_len, prompt),
- echo);
+ add_prompt(s->cur_prompt, mkstr(prompt), echo);
}
- if (name_len) {
+ if (name.len) {
/* FIXME: better prefix to distinguish from
* local prompts? */
s->cur_prompt->name =
- dupprintf("SSH server: %.*s", name_len, name);
+ dupprintf("SSH server: %.*s", PTRLEN_PRINTF(name));
s->cur_prompt->name_reqd = TRUE;
} else {
s->cur_prompt->name =
@@ -11000,10 +10795,12 @@ static void do_ssh2_userauth(void *vctx)
/* Special case: for reasons best known to themselves,
* some servers send k-i requests with no prompts and
* nothing to display. Keep quiet in this case. */
- if (s->num_prompts || name_len || inst_len) {
+ if (s->num_prompts || name.len || inst.len) {
s->cur_prompt->instruction =
- dupprintf("Using keyboard-interactive authentication.%s%.*s",
- inst_len ? "\n" : "", inst_len, inst);
+ dupprintf("Using keyboard-interactive "
+ "authentication.%s%.*s",
+ inst.len ? "\n" : "",
+ PTRLEN_PRINTF(inst));
s->cur_prompt->instr_reqd = TRUE;
} else {
s->cur_prompt->instr_reqd = FALSE;
@@ -11155,8 +10952,7 @@ static void do_ssh2_userauth(void *vctx)
*/
int got_new = FALSE; /* not live over crReturn */
- char *prompt; /* not live over crReturn */
- int prompt_len; /* not live over crReturn */
+ ptrlen prompt; /* not live over crReturn */
{
const char *msg;
@@ -11169,13 +10965,12 @@ static void do_ssh2_userauth(void *vctx)
c_write_str(ssh, "\r\n");
}
- ssh_pkt_getstring(pktin, &prompt, &prompt_len);
+ prompt = get_string(pktin);
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
s->cur_prompt->name = dupstr("New SSH password");
- s->cur_prompt->instruction =
- dupprintf("%.*s", prompt_len, NULLTOEMPTY(prompt));
+ s->cur_prompt->instruction = mkstr(prompt);
s->cur_prompt->instr_reqd = TRUE;
/*
* There's no explicit requirement in the protocol
@@ -11308,9 +11103,9 @@ static void do_ssh2_userauth(void *vctx)
sfree(s->password);
} else {
- char *str = dupprintf("No supported authentication methods available"
- " (server sent: %.*s)",
- methlen, methods);
+ char *str = dupprintf(
+ "No supported authentication methods available"
+ " (server sent: %.*s)", PTRLEN_PRINTF(methods));
ssh_disconnect(ssh, str,
"No supported authentication methods available",
@@ -11474,7 +11269,7 @@ static void do_ssh2_connection(void *vctx)
"channel open request", pktin->type));
crStopV;
}
- if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {
+ if (get_uint32(pktin) != ssh->mainchan->localid) {
bombout(("Server's response to main channel open cited wrong"
" channel number"));
crStopV;
@@ -11486,10 +11281,10 @@ static void do_ssh2_connection(void *vctx)
crStopV;
}
- ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
+ ssh->mainchan->remoteid = get_uint32(pktin);
ssh->mainchan->halfopen = FALSE;
- ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
- ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
+ ssh->mainchan->v.v2.remwindow = get_uint32(pktin);
+ ssh->mainchan->v.v2.remmaxpkt = get_uint32(pktin);
update_specials_menu(ssh->frontend);
logevent("Opened main channel");
}
@@ -11700,11 +11495,12 @@ static void ssh2_connection_input(Ssh ssh)
static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin)
{
/* log reason code in disconnect message */
- char *buf, *msg;
- int reason, msglen;
+ char *buf;
+ ptrlen msg;
+ int reason;
- reason = ssh_pkt_getuint32(pktin);
- ssh_pkt_getstring(pktin, &msg, &msglen);
+ reason = get_uint32(pktin);
+ msg = get_string(pktin);
if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
buf = dupprintf("Received disconnect message (%s)",
@@ -11715,28 +11511,25 @@ static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin)
}
logevent(buf);
sfree(buf);
- buf = dupprintf("Disconnection message text: %.*s",
- msglen, NULLTOEMPTY(msg));
+ buf = dupprintf("Disconnection message text: %.*s", PTRLEN_PRINTF(msg));
logevent(buf);
bombout(("Server sent disconnect message\ntype %d (%s):\n\"%.*s\"",
reason,
(reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
- ssh2_disconnect_reasons[reason] : "unknown",
- msglen, NULLTOEMPTY(msg)));
+ ssh2_disconnect_reasons[reason] : "unknown", PTRLEN_PRINTF(msg)));
sfree(buf);
}
static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin)
{
/* log the debug message */
- char *msg;
- int msglen;
+ ptrlen msg;
/* XXX maybe we should actually take notice of the return value */
- ssh2_pkt_getbool(pktin);
- ssh_pkt_getstring(pktin, &msg, &msglen);
+ get_bool(pktin);
+ msg = get_string(pktin);
- logeventf(ssh, "Remote debug message: %.*s", msglen, NULLTOEMPTY(msg));
+ logeventf(ssh, "Remote debug message: %.*s", PTRLEN_PRINTF(msg));
}
static void ssh2_msg_transport(Ssh ssh, struct Packet *pktin)
From e43605ee0501377fb043dbe7ff5f16634d8df54a Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 29 May 2018 20:45:42 +0100
Subject: [PATCH 327/607] Rewrite ssh2_add_sigblob using BinarySource.
This is the function that breaks apart a signature blob (generated
locally or received from an SSH agent) and adds leading zero bytes in
front of the signature integer, if we think we're talking to a server
that will incorrectly insist on that. The breaking-apart process is
just another instance of SSH-style data unmarshalling, so it should be
done by the new centralised routines.
---
ssh.c | 68 +++++++++++++++++++++++++++--------------------------------
1 file changed, 31 insertions(+), 37 deletions(-)
diff --git a/ssh.c b/ssh.c
index 4084f007..515da287 100644
--- a/ssh.c
+++ b/ssh.c
@@ -2697,11 +2697,12 @@ void bndebug(char *string, Bignum b)
* BUG_SSH2_RSA_PADDING.
*/
static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt,
- void *pkblob_v, int pkblob_len,
- void *sigblob_v, int sigblob_len)
+ const void *pkblob, int pkblob_len,
+ const void *sigblob, int sigblob_len)
{
- unsigned char *pkblob = (unsigned char *)pkblob_v;
- unsigned char *sigblob = (unsigned char *)sigblob_v;
+ BinarySource pk[1], sig[1];
+ BinarySource_BARE_INIT(pk, pkblob, pkblob_len);
+ BinarySource_BARE_INIT(sig, sigblob, sigblob_len);
/* dmemdump(pkblob, pkblob_len); */
/* dmemdump(sigblob, sigblob_len); */
@@ -2710,48 +2711,41 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt,
* See if this is in fact an ssh-rsa signature and a buggy
* server; otherwise we can just do this the easy way.
*/
- if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) && pkblob_len > 4+7+4 &&
- (GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) {
- int pos, len, siglen;
+ if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) &&
+ ptrlen_eq_string(get_string(pk), "ssh-rsa") &&
+ ptrlen_eq_string(get_string(sig), "ssh-rsa")) {
+ ptrlen mod_mp, sig_mp;
+ size_t sig_prefix_len;
/*
- * Find the byte length of the modulus.
+ * Find the modulus and signature integers.
*/
-
- pos = 4+7; /* skip over "ssh-rsa" */
- len = toint(GET_32BIT(pkblob+pos)); /* get length of exponent */
- if (len < 0 || len > pkblob_len - pos - 4)
- goto give_up;
- pos += 4 + len; /* skip over exponent */
- if (pkblob_len - pos < 4)
+ get_string(pk); /* skip over exponent */
+ mod_mp = get_string(pk); /* remember modulus */
+ sig_prefix_len = sig->pos;
+ sig_mp = get_string(sig);
+ if (get_err(pk) || get_err(sig))
goto give_up;
- len = toint(GET_32BIT(pkblob+pos)); /* find length of modulus */
- if (len < 0 || len > pkblob_len - pos - 4)
- goto give_up;
- pos += 4; /* find modulus itself */
- while (len > 0 && pkblob[pos] == 0)
- len--, pos++;
- /* debug(("modulus length is %d\n", len)); */
- /*
- * Now find the signature integer.
- */
- pos = 4+7; /* skip over "ssh-rsa" */
- if (sigblob_len < pos+4)
- goto give_up;
- siglen = toint(GET_32BIT(sigblob+pos));
- if (siglen != sigblob_len - pos - 4)
- goto give_up;
+ /*
+ * Find the byte length of the modulus, not counting leading
+ * zeroes.
+ */
+ while (mod_mp.len > 0 && *(const char *)mod_mp.ptr == 0) {
+ mod_mp.len--;
+ mod_mp.ptr = (const char *)mod_mp.ptr + 1;
+ }
+
+ /* debug(("modulus length is %d\n", len)); */
/* debug(("signature length is %d\n", siglen)); */
- if (len != siglen) {
+ if (mod_mp.len != sig_mp.len) {
strbuf *substr = strbuf_new();
- put_data(substr, sigblob, pos);
- put_uint32(substr, len);
- while (len-- > siglen)
+ put_data(substr, sigblob, sig_prefix_len);
+ put_uint32(substr, mod_mp.len);
+ while (mod_mp.len-- > sig_mp.len)
put_byte(substr, 0);
- pos += 4; /* point to start of actual sig */
- put_data(substr, sigblob+pos, siglen);
+ put_data(substr, sig_mp.ptr, sig_mp.len);
put_stringsb(pkt, substr);
return;
}
From 392a8c00f60d3cf655865deca09b72e83d0d143b Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 27 May 2018 23:47:40 +0100
Subject: [PATCH 328/607] Pageant server: parse requests using BinarySource.
pageant_handle_msg was _particularly_ full of painful manual packet
decoding with error checks at every stage, so it's a great relief to
throw it all away and replace it with short sequences of calls to the
shiny new API!
---
pageant.c | 333 ++++++++++++++----------------------------------------
1 file changed, 86 insertions(+), 247 deletions(-)
diff --git a/pageant.c b/pageant.c
index 8528cc02..70b7f89b 100644
--- a/pageant.c
+++ b/pageant.c
@@ -80,7 +80,7 @@ static int cmpkeys_rsa(void *av, void *bv)
*/
static int cmpkeys_ssh2_asymm(void *av, void *bv)
{
- strbuf *ablob = (strbuf *) av;
+ ptrlen *ablob = (ptrlen *) av;
struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
strbuf *bblob;
int i, c;
@@ -93,10 +93,11 @@ static int cmpkeys_ssh2_asymm(void *av, void *bv)
c = 0;
for (i = 0; i < ablob->len && i < bblob->len; i++) {
- if (ablob->u[i] < bblob->u[i]) {
+ unsigned char abyte = ((unsigned char *)ablob->ptr)[i];
+ if (abyte < bblob->u[i]) {
c = -1;
break;
- } else if (ablob->u[i] > bblob->u[i]) {
+ } else if (abyte > bblob->u[i]) {
c = +1;
break;
}
@@ -118,11 +119,14 @@ static int cmpkeys_ssh2(void *av, void *bv)
{
struct ssh2_userkey *a = (struct ssh2_userkey *) av;
strbuf *ablob;
+ ptrlen apl;
int toret;
ablob = strbuf_new();
a->alg->public_blob(a->data, BinarySink_UPCAST(ablob));
- toret = cmpkeys_ssh2_asymm(ablob, bv);
+ apl.ptr = ablob->u;
+ apl.len = ablob->len;
+ toret = cmpkeys_ssh2_asymm(&apl, bv);
strbuf_free(ablob);
return toret;
}
@@ -178,24 +182,20 @@ static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
}
void pageant_handle_msg(BinarySink *bs,
- const void *msg, int msglen,
+ const void *msgdata, int msglen,
void *logctx, pageant_logfn_t logfn)
{
- const unsigned char *p = msg;
- const unsigned char *msgend;
+ BinarySource msg[1];
int type;
- msgend = p + msglen;
+ BinarySource_BARE_INIT(msg, msgdata, msglen);
- /*
- * Get the message type.
- */
- if (msgend < p+1) {
+ type = get_byte(msg);
+ if (get_err(msg)) {
pageant_failure_msg(bs, "message contained no type code",
logctx, logfn);
return;
}
- type = *p++;
switch (type) {
case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
@@ -253,59 +253,34 @@ void pageant_handle_msg(BinarySink *bs,
{
struct RSAKey reqkey, *key;
Bignum challenge, response;
- unsigned char response_source[48], response_md5[16];
+ ptrlen session_id;
+ unsigned response_type;
+ unsigned char response_md5[16];
struct MD5Context md5c;
int i;
plog(logctx, logfn, "request: SSH1_AGENTC_RSA_CHALLENGE");
- reqkey.exponent = reqkey.modulus = challenge = response = NULL;
+ response = NULL;
+ memset(&reqkey, 0, sizeof(reqkey));
- p += 4;
- i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
- if (i < 0) {
- pageant_failure_msg(
- bs, "request truncated before key exponent",
- logctx, logfn);
- goto challenge1_cleanup;
- }
- p += i;
- i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
- if (i < 0) {
- pageant_failure_msg(
- bs, "request truncated before key modulus",
- logctx, logfn);
- goto challenge1_cleanup;
- }
- p += i;
- i = ssh1_read_bignum(p, msgend - p, &challenge);
- if (i < 0) {
- pageant_failure_msg(
- bs, "request truncated before challenge",
- logctx, logfn);
- goto challenge1_cleanup;
- }
- p += i;
- if (msgend < p+16) {
- pageant_failure_msg(
- bs, "request truncated before session id",
- logctx, logfn);
- goto challenge1_cleanup;
- }
- memcpy(response_source + 32, p, 16);
- p += 16;
- if (msgend < p+4) {
- pageant_failure_msg(
- bs, "request truncated before response type",
- logctx, logfn);
+ get_rsa_ssh1_pub(msg, &reqkey, NULL, RSA_SSH1_EXPONENT_FIRST);
+ challenge = get_mp_ssh1(msg);
+ session_id = get_data(msg, 16);
+ response_type = get_uint32(msg);
+
+ if (get_err(msg)) {
+ pageant_failure_msg(bs, "unable to decode request",
+ logctx, logfn);
goto challenge1_cleanup;
}
- if (GET_32BIT(p) != 1) {
+ if (response_type != 1) {
pageant_failure_msg(
bs, "response type other than 1 not supported",
logctx, logfn);
goto challenge1_cleanup;
}
+
if (logfn) {
char fingerprint[128];
reqkey.comment = NULL;
@@ -317,13 +292,12 @@ void pageant_handle_msg(BinarySink *bs,
goto challenge1_cleanup;
}
response = rsa_ssh1_decrypt(challenge, key);
- for (i = 0; i < 32; i++)
- response_source[i] = bignum_byte(response, 31 - i);
MD5Init(&md5c);
- put_data(&md5c, response_source, 48);
+ for (i = 0; i < 32; i++)
+ put_byte(&md5c, bignum_byte(response, 31 - i));
+ put_data(&md5c, session_id.ptr, session_id.len);
MD5Final(response_md5, &md5c);
- smemclr(response_source, 48); /* burn the evidence */
put_byte(bs, SSH1_AGENT_RSA_RESPONSE);
put_data(bs, response_md5, 16);
@@ -331,10 +305,11 @@ void pageant_handle_msg(BinarySink *bs,
plog(logctx, logfn, "reply: SSH1_AGENT_RSA_RESPONSE");
challenge1_cleanup:
- if (response) freebn(response);
- if (challenge) freebn(challenge);
- if (reqkey.exponent) freebn(reqkey.exponent);
- if (reqkey.modulus) freebn(reqkey.modulus);
+ if (response)
+ freebn(response);
+ freebn(challenge);
+ freebn(reqkey.exponent);
+ freebn(reqkey.modulus);
}
break;
case SSH2_AGENTC_SIGN_REQUEST:
@@ -345,56 +320,20 @@ void pageant_handle_msg(BinarySink *bs,
*/
{
struct ssh2_userkey *key;
- const void *blobp;
- int bloblen;
- const unsigned char *data;
+ ptrlen keyblob, sigdata;
strbuf *signature;
- int datalen;
plog(logctx, logfn, "request: SSH2_AGENTC_SIGN_REQUEST");
- if (msgend < p+4) {
- pageant_failure_msg(
- bs, "request truncated before public key",
- logctx, logfn);
- return;
- }
- bloblen = toint(GET_32BIT(p));
- if (bloblen < 0 || bloblen > msgend - (p+4)) {
- pageant_failure_msg(
- bs, "request truncated before public key",
- logctx, logfn);
- return;
- }
- p += 4;
- blobp = p;
- p += bloblen;
- if (msgend < p+4) {
- pageant_failure_msg(
- bs, "request truncated before string to sign",
- logctx, logfn);
- return;
- }
- datalen = toint(GET_32BIT(p));
- p += 4;
- if (datalen < 0 || datalen > msgend - p) {
- pageant_failure_msg(
- bs, "request truncated before string to sign",
- logctx, logfn);
- return;
- }
- data = p;
+ keyblob = get_string(msg);
+ sigdata = get_string(msg);
if (logfn) {
- char *fingerprint = ssh2_fingerprint_blob(blobp, bloblen);
+ char *fingerprint = ssh2_fingerprint_blob(
+ keyblob.ptr, keyblob.len);
plog(logctx, logfn, "requested key: %s", fingerprint);
sfree(fingerprint);
}
- {
- strbuf *blob = strbuf_new();
- put_data(blob, blobp, bloblen);
- key = find234(ssh2keys, blob, cmpkeys_ssh2_asymm);
- strbuf_free(blob);
- }
+ key = find234(ssh2keys, &keyblob, cmpkeys_ssh2_asymm);
if (!key) {
pageant_failure_msg(bs, "key not found", logctx, logfn);
return;
@@ -403,7 +342,7 @@ void pageant_handle_msg(BinarySink *bs,
put_byte(bs, SSH2_AGENT_SIGN_RESPONSE);
signature = strbuf_new();
- key->alg->sign(key->data, data, datalen,
+ key->alg->sign(key->data, sigdata.ptr, sigdata.len,
BinarySink_UPCAST(signature));
put_stringsb(bs, signature);
@@ -417,88 +356,35 @@ void pageant_handle_msg(BinarySink *bs,
*/
{
struct RSAKey *key;
- char *comment;
- int n, commentlen;
plog(logctx, logfn, "request: SSH1_AGENTC_ADD_RSA_IDENTITY");
key = snew(struct RSAKey);
memset(key, 0, sizeof(struct RSAKey));
- n = rsa_ssh1_readpub(p, msgend - p, key, NULL,
- RSA_SSH1_MODULUS_FIRST);
- if (n < 0) {
- pageant_failure_msg(
- bs, "request truncated before public key",
- logctx, logfn);
- goto add1_cleanup;
- }
- p += n;
-
- n = rsa_ssh1_readpriv(p, msgend - p, key);
- if (n < 0) {
- pageant_failure_msg(
- bs, "request truncated before private key",
- logctx, logfn);
- goto add1_cleanup;
- }
- p += n;
+ get_rsa_ssh1_pub(msg, key, NULL, RSA_SSH1_MODULUS_FIRST);
+ get_rsa_ssh1_priv(msg, key);
/* SSH-1 names p and q the other way round, i.e. we have
* the inverse of p mod q and not of q mod p. We swap the
* names, because our internal RSA wants iqmp. */
+ key->iqmp = get_mp_ssh1(msg);
+ key->q = get_mp_ssh1(msg);
+ key->p = get_mp_ssh1(msg);
- n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
- if (n < 0) {
- pageant_failure_msg(bs, "request truncated before iqmp",
- logctx, logfn);
- goto add1_cleanup;
- }
- p += n;
+ key->comment = mkstr(get_string(msg));
- n = ssh1_read_bignum(p, msgend - p, &key->q); /* p */
- if (n < 0) {
- pageant_failure_msg(bs, "request truncated before p",
+ if (get_err(msg)) {
+ pageant_failure_msg(bs, "unable to decode request",
logctx, logfn);
- goto add1_cleanup;
- }
- p += n;
-
- n = ssh1_read_bignum(p, msgend - p, &key->p); /* q */
- if (n < 0) {
- pageant_failure_msg(
- bs, "request truncated before q", logctx, logfn);
- goto add1_cleanup;
- }
- p += n;
-
- if (msgend < p+4) {
- pageant_failure_msg(
- bs, "request truncated before key comment",
- logctx, logfn);
- goto add1_cleanup;
- }
- commentlen = toint(GET_32BIT(p));
-
- if (commentlen < 0 || commentlen > msgend - p) {
- pageant_failure_msg(
- bs, "request truncated before key comment",
- logctx, logfn);
- goto add1_cleanup;
- }
+ goto add1_cleanup;
+ }
if (!rsa_verify(key)) {
pageant_failure_msg(bs, "key is invalid", logctx, logfn);
goto add1_cleanup;
}
- comment = snewn(commentlen+1, char);
- if (comment) {
- memcpy(comment, p + 4, commentlen);
- comment[commentlen] = '\0';
- key->comment = comment;
- }
-
if (logfn) {
char fingerprint[128];
rsa_fingerprint(fingerprint, sizeof(fingerprint), key);
@@ -529,73 +415,42 @@ void pageant_handle_msg(BinarySink *bs,
*/
{
struct ssh2_userkey *key = NULL;
- char *comment;
- const char *alg;
- int alglen, commlen;
- int bloblen;
+ ptrlen alg;
plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY");
- if (msgend < p+4) {
- pageant_failure_msg(
- bs, "request truncated before key algorithm",
- logctx, logfn);
- goto add2_cleanup;
- }
- alglen = toint(GET_32BIT(p));
- p += 4;
- if (alglen < 0 || alglen > msgend - p) {
- pageant_failure_msg(
- bs, "request truncated before key algorithm",
- logctx, logfn);
- goto add2_cleanup;
- }
- alg = (const char *)p;
- p += alglen;
+ alg = get_string(msg);
key = snew(struct ssh2_userkey);
key->data = NULL;
key->comment = NULL;
- key->alg = find_pubkey_alg_len(make_ptrlen(alg, alglen));
+ key->alg = find_pubkey_alg_len(alg);
if (!key->alg) {
pageant_failure_msg(bs, "algorithm unknown", logctx, logfn);
goto add2_cleanup;
}
- bloblen = msgend - p;
- key->data = key->alg->openssh_createkey(key->alg, &p, &bloblen);
+ {
+ const unsigned char *p = get_ptr(msg);
+ int len = get_avail(msg);
+ key->data = key->alg->openssh_createkey(key->alg, &p, &len);
+ assert(len >= 0);
+ assert(len < get_avail(msg));
+ msg->pos += get_avail(msg) - len;
+ }
+
if (!key->data) {
pageant_failure_msg(bs, "key setup failed", logctx, logfn);
goto add2_cleanup;
}
- /*
- * p has been advanced by openssh_createkey, but
- * certainly not _beyond_ the end of the buffer.
- */
- assert(p <= msgend);
+ key->comment = mkstr(get_string(msg));
- if (msgend < p+4) {
- pageant_failure_msg(
- bs, "request truncated before key comment",
- logctx, logfn);
- goto add2_cleanup;
- }
- commlen = toint(GET_32BIT(p));
- p += 4;
-
- if (commlen < 0 || commlen > msgend - p) {
- pageant_failure_msg(
- bs, "request truncated before key comment",
- logctx, logfn);
- goto add2_cleanup;
- }
- comment = snewn(commlen + 1, char);
- if (comment) {
- memcpy(comment, p, commlen);
- comment[commlen] = '\0';
- }
- key->comment = comment;
+ if (get_err(msg)) {
+ pageant_failure_msg(bs, "unable to decode request",
+ logctx, logfn);
+ goto add2_cleanup;
+ }
if (logfn) {
char *fingerprint = ssh2_fingerprint(key->alg, key->data);
@@ -634,17 +489,17 @@ void pageant_handle_msg(BinarySink *bs,
*/
{
struct RSAKey reqkey, *key;
- int n;
plog(logctx, logfn, "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY");
- n = rsa_ssh1_readpub(p, msgend - p, &reqkey, NULL,
- RSA_SSH1_EXPONENT_FIRST);
- if (n < 0) {
- pageant_failure_msg(
- bs, "request truncated before public key",
- logctx, logfn);
- return;
+ get_rsa_ssh1_pub(msg, &reqkey, NULL, RSA_SSH1_EXPONENT_FIRST);
+
+ if (get_err(msg)) {
+ pageant_failure_msg(bs, "unable to decode request",
+ logctx, logfn);
+ freebn(reqkey.exponent);
+ freebn(reqkey.modulus);
+ return;
}
if (logfn) {
@@ -680,41 +535,25 @@ void pageant_handle_msg(BinarySink *bs,
*/
{
struct ssh2_userkey *key;
- const void *blobp;
- int bloblen;
+ ptrlen blob;
plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_IDENTITY");
- if (msgend < p+4) {
- pageant_failure_msg(
- bs, "request truncated before public key",
- logctx, logfn);
- return;
- }
- bloblen = toint(GET_32BIT(p));
- p += 4;
+ blob = get_string(msg);
- if (bloblen < 0 || bloblen > msgend - p) {
- pageant_failure_msg(
- bs, "request truncated before public key",
- logctx, logfn);
+ if (get_err(msg)) {
+ pageant_failure_msg(bs, "unable to decode request",
+ logctx, logfn);
return;
}
- blobp = p;
- p += bloblen;
if (logfn) {
- char *fingerprint = ssh2_fingerprint_blob(blobp, bloblen);
+ char *fingerprint = ssh2_fingerprint_blob(blob.ptr, blob.len);
plog(logctx, logfn, "unwanted key: %s", fingerprint);
sfree(fingerprint);
}
- {
- strbuf *blob = strbuf_new();
- put_data(blob, blobp, bloblen);
- key = find234(ssh2keys, blob, cmpkeys_ssh2_asymm);
- strbuf_free(blob);
- }
+ key = find234(ssh2keys, &blob, cmpkeys_ssh2_asymm);
if (!key) {
pageant_failure_msg(bs, "key not found", logctx, logfn);
return;
From e2431c3ef8d9ad48a882f7e7f9ccfb327cb1cf0d Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 27 May 2018 23:58:20 +0100
Subject: [PATCH 329/607] Pageant client code: parse replies using
BinarySource.
This affects both the client code used by Pageant itself, in
pageant.c, and the client code in ssh.c used during SSH userauth.
---
pageant.c | 118 ++++++++---------------------
ssh.c | 221 ++++++++++++++++++++----------------------------------
2 files changed, 113 insertions(+), 226 deletions(-)
diff --git a/pageant.c b/pageant.c
index 70b7f89b..25d0b9ab 100644
--- a/pageant.c
+++ b/pageant.c
@@ -1307,145 +1307,91 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase,
int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
char **retstr)
{
- unsigned char *keylist, *p;
+ unsigned char *keylist;
int i, nkeys, keylistlen;
- char *comment;
+ ptrlen comment;
struct pageant_pubkey cbkey;
+ BinarySource src[1];
keylist = pageant_get_keylist1(&keylistlen);
- if (keylistlen < 4) {
- *retstr = dupstr("Received broken SSH-1 key list from agent");
- sfree(keylist);
- return PAGEANT_ACTION_FAILURE;
- }
- nkeys = toint(GET_32BIT(keylist));
- if (nkeys < 0) {
- *retstr = dupstr("Received broken SSH-1 key list from agent");
- sfree(keylist);
+ if (!keylist) {
+ *retstr = dupstr("Did not receive an SSH-1 key list from agent");
return PAGEANT_ACTION_FAILURE;
}
- p = keylist + 4;
- keylistlen -= 4;
+ BinarySource_BARE_INIT(src, keylist, keylistlen);
+ nkeys = toint(get_uint32(src));
for (i = 0; i < nkeys; i++) {
struct RSAKey rkey;
char fingerprint[128];
- int n;
/* public blob and fingerprint */
memset(&rkey, 0, sizeof(rkey));
- n = rsa_ssh1_readpub(p, keylistlen, &rkey, NULL,
- RSA_SSH1_EXPONENT_FIRST);
- if (n < 0 || n > keylistlen) {
- freersakey(&rkey);
- *retstr = dupstr("Received broken SSH-1 key list from agent");
- sfree(keylist);
- return PAGEANT_ACTION_FAILURE;
- }
- p += n, keylistlen -= n;
- rsa_fingerprint(fingerprint, sizeof(fingerprint), &rkey);
+ get_rsa_ssh1_pub(src, &rkey, NULL, RSA_SSH1_EXPONENT_FIRST);
+ comment = get_string(src);
- /* comment */
- if (keylistlen < 4) {
+ if (get_err(src)) {
*retstr = dupstr("Received broken SSH-1 key list from agent");
freersakey(&rkey);
sfree(keylist);
return PAGEANT_ACTION_FAILURE;
}
- n = toint(GET_32BIT(p));
- p += 4, keylistlen -= 4;
- if (n < 0 || keylistlen < n) {
- *retstr = dupstr("Received broken SSH-1 key list from agent");
- freersakey(&rkey);
- sfree(keylist);
- return PAGEANT_ACTION_FAILURE;
- }
- comment = dupprintf("%.*s", (int)n, (const char *)p);
- p += n, keylistlen -= n;
+
+ rsa_fingerprint(fingerprint, sizeof(fingerprint), &rkey);
cbkey.blob = strbuf_new();
rsa_ssh1_public_blob(BinarySink_UPCAST(cbkey.blob), &rkey,
RSA_SSH1_EXPONENT_FIRST);
- cbkey.comment = comment;
+ cbkey.comment = mkstr(comment);
cbkey.ssh_version = 1;
- callback(callback_ctx, fingerprint, comment, &cbkey);
+ callback(callback_ctx, fingerprint, cbkey.comment, &cbkey);
strbuf_free(cbkey.blob);
freersakey(&rkey);
- sfree(comment);
+ sfree(cbkey.comment);
}
sfree(keylist);
- if (keylistlen != 0) {
+ if (get_err(src) || get_avail(src) != 0) {
*retstr = dupstr("Received broken SSH-1 key list from agent");
return PAGEANT_ACTION_FAILURE;
}
keylist = pageant_get_keylist2(&keylistlen);
- if (keylistlen < 4) {
- *retstr = dupstr("Received broken SSH-2 key list from agent");
- sfree(keylist);
+ if (!keylist) {
+ *retstr = dupstr("Did not receive an SSH-2 key list from agent");
return PAGEANT_ACTION_FAILURE;
}
- nkeys = toint(GET_32BIT(keylist));
- if (nkeys < 0) {
- *retstr = dupstr("Received broken SSH-2 key list from agent");
- sfree(keylist);
- return PAGEANT_ACTION_FAILURE;
- }
- p = keylist + 4;
- keylistlen -= 4;
+ BinarySource_BARE_INIT(src, keylist, keylistlen);
+ nkeys = toint(get_uint32(src));
for (i = 0; i < nkeys; i++) {
+ ptrlen pubblob;
char *fingerprint;
- int n;
- /* public blob */
- if (keylistlen < 4) {
- *retstr = dupstr("Received broken SSH-2 key list from agent");
- sfree(keylist);
- return PAGEANT_ACTION_FAILURE;
- }
- n = toint(GET_32BIT(p));
- p += 4, keylistlen -= 4;
- if (n < 0 || keylistlen < n) {
- *retstr = dupstr("Received broken SSH-2 key list from agent");
- sfree(keylist);
- return PAGEANT_ACTION_FAILURE;
- }
- fingerprint = ssh2_fingerprint_blob(p, n);
- cbkey.blob = strbuf_new();
- put_data(cbkey.blob, p, n);
- p += n, keylistlen -= n;
+ pubblob = get_string(src);
+ comment = get_string(src);
- /* comment */
- if (keylistlen < 4) {
+ if (get_err(src)) {
*retstr = dupstr("Received broken SSH-2 key list from agent");
- sfree(fingerprint);
sfree(keylist);
return PAGEANT_ACTION_FAILURE;
}
- n = toint(GET_32BIT(p));
- p += 4, keylistlen -= 4;
- if (n < 0 || keylistlen < n) {
- *retstr = dupstr("Received broken SSH-2 key list from agent");
- sfree(fingerprint);
- sfree(keylist);
- return PAGEANT_ACTION_FAILURE;
- }
- comment = dupprintf("%.*s", (int)n, (const char *)p);
- p += n, keylistlen -= n;
+
+ fingerprint = ssh2_fingerprint_blob(pubblob.ptr, pubblob.len);
+ cbkey.blob = strbuf_new();
+ put_data(cbkey.blob, pubblob.ptr, pubblob.len);
cbkey.ssh_version = 2;
- cbkey.comment = comment;
- callback(callback_ctx, fingerprint, comment, &cbkey);
+ cbkey.comment = mkstr(comment);
+ callback(callback_ctx, fingerprint, cbkey.comment, &cbkey);
sfree(fingerprint);
- sfree(comment);
+ sfree(cbkey.comment);
}
sfree(keylist);
- if (keylistlen != 0) {
+ if (get_err(src) || get_avail(src) != 0) {
*retstr = dupstr("Received broken SSH-2 key list from agent");
return PAGEANT_ACTION_FAILURE;
}
diff --git a/ssh.c b/ssh.c
index 515da287..518372ca 100644
--- a/ssh.c
+++ b/ssh.c
@@ -4053,14 +4053,13 @@ static void do_ssh1_login(void *vctx)
int userpass_ret;
char c;
int pwpkt_type;
- unsigned char *response, *p;
- int responselen;
+ unsigned char *agent_response;
+ BinarySource asrc[1]; /* response from SSH agent */
int keyi, nkeys;
int authed;
struct RSAKey key;
Bignum challenge;
- char *commentp;
- int commentlen;
+ ptrlen comment;
int dlgret;
Filename *keyfile;
struct RSAKey servkey, hostkey;
@@ -4425,6 +4424,7 @@ static void do_ssh1_login(void *vctx)
* Attempt RSA authentication using Pageant.
*/
void *r;
+ int rlen;
strbuf *request;
s->authed = FALSE;
@@ -4435,63 +4435,37 @@ static void do_ssh1_login(void *vctx)
request = strbuf_new_for_agent_query();
put_byte(request, SSH1_AGENTC_REQUEST_RSA_IDENTITIES);
ssh->auth_agent_query = agent_query(
- request, &r, &s->responselen, ssh_agent_callback, ssh);
+ request, &r, &rlen, ssh_agent_callback, ssh);
strbuf_free(request);
if (ssh->auth_agent_query) {
ssh->agent_response = NULL;
crWaitUntilV(ssh->agent_response);
r = ssh->agent_response;
- s->responselen = ssh->agent_response_len;
+ rlen = ssh->agent_response_len;
}
- s->response = (unsigned char *) r;
- if (s->response && s->responselen >= 5 &&
- s->response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
- s->p = s->response + 5;
- s->nkeys = toint(GET_32BIT(s->p));
+ s->agent_response = r;
+ BinarySource_BARE_INIT(s->asrc, r, rlen);
+ get_uint32(s->asrc); /* skip length field */
+ if (get_byte(s->asrc) == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
+ s->nkeys = toint(get_uint32(s->asrc));
if (s->nkeys < 0) {
logeventf(ssh, "Pageant reported negative key count %d",
s->nkeys);
s->nkeys = 0;
}
- s->p += 4;
logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys);
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
- unsigned char *pkblob = s->p;
- s->p += 4;
- {
- int n, ok = FALSE;
- do { /* do while (0) to make breaking easy */
- n = ssh1_read_bignum
- (s->p, toint(s->responselen-(s->p-s->response)),
- &s->key.exponent);
- if (n < 0)
- break;
- s->p += n;
- n = ssh1_read_bignum
- (s->p, toint(s->responselen-(s->p-s->response)),
- &s->key.modulus);
- if (n < 0)
- break;
- s->p += n;
- if (s->responselen - (s->p-s->response) < 4)
- break;
- s->commentlen = toint(GET_32BIT(s->p));
- s->p += 4;
- if (s->commentlen < 0 ||
- toint(s->responselen - (s->p-s->response)) <
- s->commentlen)
- break;
- s->commentp = (char *)s->p;
- s->p += s->commentlen;
- ok = TRUE;
- } while (0);
- if (!ok) {
- logevent("Pageant key list packet was truncated");
- break;
- }
- }
+ ptrlen keystr;
+ get_rsa_ssh1_pub(s->asrc, &s->key, &keystr,
+ RSA_SSH1_EXPONENT_FIRST);
+ s->comment = get_string(s->asrc);
+ if (get_err(s->asrc)) {
+ logevent("Pageant key list packet was truncated");
+ break;
+ }
if (s->publickey_blob) {
- if (!memcmp(pkblob, s->publickey_blob->s,
+ if (keystr.len == s->publickey_blob->len &&
+ !memcmp(keystr.ptr, s->publickey_blob->s,
s->publickey_blob->len)) {
logeventf(ssh, "Pageant key #%d matches "
"configured key file", s->keyi);
@@ -4560,8 +4534,8 @@ static void do_ssh1_login(void *vctx)
if (flags & FLAG_VERBOSE) {
c_write_str(ssh, "Authenticated using"
" RSA key \"");
- c_write(ssh, s->commentp,
- s->commentlen);
+ c_write(ssh, s->comment.ptr,
+ s->comment.len);
c_write_str(ssh, "\" from agent\r\n");
}
s->authed = TRUE;
@@ -4583,7 +4557,7 @@ static void do_ssh1_login(void *vctx)
if (s->authed)
break;
}
- sfree(s->response);
+ sfree(s->agent_response);
if (s->publickey_blob && !s->tried_publickey)
logevent("Configured key file not in Pageant");
} else {
@@ -9730,14 +9704,12 @@ static void do_ssh2_userauth(void *vctx)
int privatekey_available, privatekey_encrypted;
char *publickey_algorithm;
char *publickey_comment;
- unsigned char *agent_response, *agentp;
- int agent_responselen;
- unsigned char *pkblob_in_agent;
+ unsigned char *agent_response;
+ BinarySource asrc[1]; /* for reading SSH agent response */
+ size_t pkblob_pos_in_agent;
int keyi, nkeys;
- char *pkblob, *alg, *commentp;
- int pklen, alglen, commentlen;
- int retlen, len;
- char *ret;
+ ptrlen pk, alg, comment;
+ int len;
struct Packet *pktout;
Filename *keyfile;
#ifndef NO_GSSAPI
@@ -9866,10 +9838,10 @@ static void do_ssh2_userauth(void *vctx)
*/
s->nkeys = 0;
s->agent_response = NULL;
- s->pkblob_in_agent = NULL;
+ s->pkblob_pos_in_agent = 0;
if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists()) {
-
void *r;
+ int rlen;
strbuf *agent_request;
logevent("Pageant is running. Requesting keys.");
@@ -9878,23 +9850,22 @@ static void do_ssh2_userauth(void *vctx)
agent_request = strbuf_new_for_agent_query();
put_byte(agent_request, SSH2_AGENTC_REQUEST_IDENTITIES);
ssh->auth_agent_query = agent_query(
- agent_request, &r, &s->agent_responselen,
- ssh_agent_callback, ssh);
+ agent_request, &r, &rlen, ssh_agent_callback, ssh);
strbuf_free(agent_request);
if (ssh->auth_agent_query) {
ssh->agent_response = NULL;
crWaitUntilV(ssh->agent_response);
r = ssh->agent_response;
- s->agent_responselen = ssh->agent_response_len;
+ rlen = ssh->agent_response_len;
}
- s->agent_response = (unsigned char *) r;
- if (s->agent_response && s->agent_responselen >= 5 &&
- s->agent_response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {
+ s->agent_response = r;
+ BinarySource_BARE_INIT(s->asrc, r, rlen);
+ get_uint32(s->asrc); /* skip length field */
+ if (get_byte(s->asrc) == SSH2_AGENT_IDENTITIES_ANSWER) {
int keyi;
- unsigned char *p;
- p = s->agent_response + 5;
- s->nkeys = toint(GET_32BIT(p));
+
+ s->nkeys = toint(get_uint32(s->asrc));
/*
* Vet the Pageant response to ensure that the key
@@ -9906,58 +9877,31 @@ static void do_ssh2_userauth(void *vctx)
s->nkeys = 0;
goto done_agent_query;
} else {
- unsigned char *q = p + 4;
- int lenleft = s->agent_responselen - 5 - 4;
+ logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);
+ /* See if configured key is in agent. */
for (keyi = 0; keyi < s->nkeys; keyi++) {
- int bloblen, commentlen;
- if (lenleft < 4) {
+ size_t pos = s->asrc->pos;
+ ptrlen blob = get_string(s->asrc);
+ get_string(s->asrc); /* skip comment */
+ if (get_err(s->asrc)) {
logeventf(ssh, "Pageant response was truncated");
s->nkeys = 0;
goto done_agent_query;
}
- bloblen = toint(GET_32BIT(q));
- lenleft -= 4;
- q += 4;
- if (bloblen < 0 || bloblen > lenleft) {
- logeventf(ssh, "Pageant response was truncated");
- s->nkeys = 0;
- goto done_agent_query;
- }
- lenleft -= bloblen;
- q += bloblen;
- commentlen = toint(GET_32BIT(q));
- lenleft -= 4;
- q += 4;
- if (commentlen < 0 || commentlen > lenleft) {
- logeventf(ssh, "Pageant response was truncated");
- s->nkeys = 0;
- goto done_agent_query;
- }
- lenleft -= commentlen;
- q += commentlen;
- }
- }
- p += 4;
- logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);
- if (s->publickey_blob) {
- /* See if configured key is in agent. */
- for (keyi = 0; keyi < s->nkeys; keyi++) {
- s->pklen = toint(GET_32BIT(p));
- if (s->pklen == s->publickey_blob->len &&
- !memcmp(p+4, s->publickey_blob->s,
+ if (s->publickey_blob &&
+ blob.len == s->publickey_blob->len &&
+ !memcmp(blob.ptr, s->publickey_blob->s,
s->publickey_blob->len)) {
logeventf(ssh, "Pageant key #%d matches "
"configured key file", keyi);
s->keyi = keyi;
- s->pkblob_in_agent = p;
+ s->pkblob_pos_in_agent = pos;
break;
}
- p += 4 + s->pklen;
- p += toint(GET_32BIT(p)) + 4; /* comment */
}
- if (!s->pkblob_in_agent) {
+ if (s->publickey_blob && !s->pkblob_pos_in_agent) {
logevent("Configured key file not in Pageant");
s->nkeys = 0;
}
@@ -10066,10 +10010,10 @@ static void do_ssh2_userauth(void *vctx)
/* Reset agent request state. */
s->done_agent = FALSE;
if (s->agent_response) {
- if (s->pkblob_in_agent) {
- s->agentp = s->pkblob_in_agent;
+ if (s->pkblob_pos_in_agent) {
+ s->asrc->pos = s->pkblob_pos_in_agent;
} else {
- s->agentp = s->agent_response + 5 + 4;
+ s->asrc->pos = 9; /* skip length + type + key count */
s->keyi = 0;
}
}
@@ -10253,17 +10197,13 @@ static void do_ssh2_userauth(void *vctx)
logeventf(ssh, "Trying Pageant key #%d", s->keyi);
/* Unpack key from agent response */
- s->pklen = toint(GET_32BIT(s->agentp));
- s->agentp += 4;
- s->pkblob = (char *)s->agentp;
- s->agentp += s->pklen;
- s->alglen = toint(GET_32BIT(s->pkblob));
- s->alg = s->pkblob + 4;
- s->commentlen = toint(GET_32BIT(s->agentp));
- s->agentp += 4;
- s->commentp = (char *)s->agentp;
- s->agentp += s->commentlen;
- /* s->agentp now points at next key, if any */
+ s->pk = get_string(s->asrc);
+ s->comment = get_string(s->asrc);
+ {
+ BinarySource src[1];
+ BinarySource_BARE_INIT(src, s->pk.ptr, s->pk.len);
+ s->alg = get_string(src);
+ }
/* See if server will accept it */
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
@@ -10273,8 +10213,8 @@ static void do_ssh2_userauth(void *vctx)
put_stringz(s->pktout, "publickey");
/* method */
put_bool(s->pktout, FALSE); /* no signature included */
- put_string(s->pktout, s->alg, s->alglen);
- put_string(s->pktout, s->pkblob, s->pklen);
+ put_stringpl(s->pktout, s->alg);
+ put_stringpl(s->pktout, s->pk);
ssh2_pkt_send(ssh, s->pktout);
s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET;
@@ -10287,12 +10227,13 @@ static void do_ssh2_userauth(void *vctx)
} else {
strbuf *agentreq, *sigdata;
- void *vret;
+ void *r;
+ int rlen;
if (flags & FLAG_VERBOSE) {
c_write_str(ssh, "Authenticating with "
"public key \"");
- c_write(ssh, s->commentp, s->commentlen);
+ c_write(ssh, s->comment.ptr, s->comment.len);
c_write_str(ssh, "\" from agent\r\n");
}
@@ -10307,13 +10248,13 @@ static void do_ssh2_userauth(void *vctx)
put_stringz(s->pktout, "publickey");
/* method */
put_bool(s->pktout, TRUE); /* signature included */
- put_string(s->pktout, s->alg, s->alglen);
- put_string(s->pktout, s->pkblob, s->pklen);
+ put_stringpl(s->pktout, s->alg);
+ put_stringpl(s->pktout, s->pk);
/* Ask agent for signature. */
agentreq = strbuf_new_for_agent_query();
put_byte(agentreq, SSH2_AGENTC_SIGN_REQUEST);
- put_string(agentreq, s->pkblob, s->pklen);
+ put_stringpl(agentreq, s->pk);
/* Now the data to be signed... */
sigdata = strbuf_new();
if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID) {
@@ -10329,26 +10270,26 @@ static void do_ssh2_userauth(void *vctx)
/* And finally the (zero) flags word. */
put_uint32(agentreq, 0);
ssh->auth_agent_query = agent_query(
- agentreq, &vret, &s->retlen,
- ssh_agent_callback, ssh);
+ agentreq, &r, &rlen, ssh_agent_callback, ssh);
strbuf_free(agentreq);
if (ssh->auth_agent_query) {
ssh->agent_response = NULL;
crWaitUntilV(ssh->agent_response);
- vret = ssh->agent_response;
- s->retlen = ssh->agent_response_len;
+ r = ssh->agent_response;
+ rlen = ssh->agent_response_len;
}
- s->ret = vret;
- if (s->ret) {
- if (s->retlen >= 9 &&
- s->ret[4] == SSH2_AGENT_SIGN_RESPONSE &&
- GET_32BIT(s->ret + 5) <= (unsigned)(s->retlen-9)) {
+ if (r) {
+ ptrlen sigblob;
+ BinarySource src[1];
+ BinarySource_BARE_INIT(src, r, rlen);
+ get_uint32(src); /* skip length field */
+ if (get_byte(src) == SSH2_AGENT_SIGN_RESPONSE &&
+ (sigblob = get_string(src), !get_err(src))) {
logevent("Sending Pageant's response");
ssh2_add_sigblob(ssh, s->pktout,
- s->pkblob, s->pklen,
- s->ret + 9,
- GET_32BIT(s->ret + 5));
+ s->pk.ptr, s->pk.len,
+ sigblob.ptr, sigblob.len);
ssh2_pkt_send(ssh, s->pktout);
s->type = AUTH_TYPE_PUBLICKEY;
} else {
@@ -10360,7 +10301,7 @@ static void do_ssh2_userauth(void *vctx)
}
/* Do we have any keys left to try? */
- if (s->pkblob_in_agent) {
+ if (s->pkblob_pos_in_agent) {
s->done_agent = TRUE;
s->tried_pubkey_config = TRUE;
} else {
From 876e1589f845febb4d4a7a5d29e8c533ed68bea4 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 28 May 2018 15:36:15 +0100
Subject: [PATCH 330/607] Rewrite conf deserialisation using BinarySource.
Like the corresponding rewrite of conf serialisation, this affects not
just conf_deserialise itself but also the per-platform filename and
fontspec deserialisers.
---
conf.c | 84 +++++++++++------------------------------------
putty.h | 6 ++--
unix/gtkmain.c | 56 ++++++++++++++++---------------
unix/uxmisc.c | 20 +++--------
windows/window.c | 5 ++-
windows/winmisc.c | 33 +++++--------------
6 files changed, 69 insertions(+), 135 deletions(-)
diff --git a/conf.c b/conf.c
index 89d6412b..118e5944 100644
--- a/conf.c
+++ b/conf.c
@@ -504,97 +504,53 @@ void conf_serialise(BinarySink *bs, Conf *conf)
put_uint32(bs, 0xFFFFFFFFU);
}
-int conf_deserialise(Conf *conf, void *vdata, int maxsize)
+int conf_deserialise(Conf *conf, BinarySource *src)
{
- unsigned char *data = (unsigned char *)vdata;
- unsigned char *start = data;
struct conf_entry *entry;
unsigned primary;
- int used;
- unsigned char *zero;
- while (maxsize >= 4) {
- primary = GET_32BIT_MSB_FIRST(data);
- data += 4, maxsize -= 4;
+ while (1) {
+ primary = get_uint32(src);
+ if (get_err(src))
+ return FALSE;
+ if (primary == 0xFFFFFFFFU)
+ return TRUE;
if (primary >= N_CONFIG_OPTIONS)
- break;
+ return FALSE;
entry = snew(struct conf_entry);
entry->key.primary = primary;
switch (subkeytypes[entry->key.primary]) {
case TYPE_INT:
- if (maxsize < 4) {
- sfree(entry);
- goto done;
- }
- entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data));
- data += 4, maxsize -= 4;
+ entry->key.secondary.i = toint(get_uint32(src));
break;
case TYPE_STR:
- zero = memchr(data, 0, maxsize);
- if (!zero) {
- sfree(entry);
- goto done;
- }
- entry->key.secondary.s = dupstr((char *)data);
- maxsize -= (zero + 1 - data);
- data = zero + 1;
+ entry->key.secondary.s = dupstr(get_asciz(src));
break;
}
switch (valuetypes[entry->key.primary]) {
case TYPE_INT:
- if (maxsize < 4) {
- if (subkeytypes[entry->key.primary] == TYPE_STR)
- sfree(entry->key.secondary.s);
- sfree(entry);
- goto done;
- }
- entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data));
- data += 4, maxsize -= 4;
+ entry->value.u.intval = toint(get_uint32(src));
break;
case TYPE_STR:
- zero = memchr(data, 0, maxsize);
- if (!zero) {
- if (subkeytypes[entry->key.primary] == TYPE_STR)
- sfree(entry->key.secondary.s);
- sfree(entry);
- goto done;
- }
- entry->value.u.stringval = dupstr((char *)data);
- maxsize -= (zero + 1 - data);
- data = zero + 1;
+ entry->value.u.stringval = dupstr(get_asciz(src));
break;
case TYPE_FILENAME:
- entry->value.u.fileval =
- filename_deserialise(data, maxsize, &used);
- if (!entry->value.u.fileval) {
- if (subkeytypes[entry->key.primary] == TYPE_STR)
- sfree(entry->key.secondary.s);
- sfree(entry);
- goto done;
- }
- data += used;
- maxsize -= used;
+ entry->value.u.fileval = filename_deserialise(src);
break;
case TYPE_FONT:
- entry->value.u.fontval =
- fontspec_deserialise(data, maxsize, &used);
- if (!entry->value.u.fontval) {
- if (subkeytypes[entry->key.primary] == TYPE_STR)
- sfree(entry->key.secondary.s);
- sfree(entry);
- goto done;
- }
- data += used;
- maxsize -= used;
+ entry->value.u.fontval = fontspec_deserialise(src);
break;
}
+
+ if (get_err(src)) {
+ free_entry(entry);
+ return FALSE;
+ }
+
conf_insert(conf, entry);
}
-
- done:
- return (int)(data - start);
}
diff --git a/putty.h b/putty.h
index d7301509..fb9a2d55 100644
--- a/putty.h
+++ b/putty.h
@@ -1015,7 +1015,7 @@ void conf_set_filename(Conf *conf, int key, const Filename *val);
void conf_set_fontspec(Conf *conf, int key, const FontSpec *val);
/* Serialisation functions for Duplicate Session */
void conf_serialise(BinarySink *bs, Conf *conf);
-int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/
+int conf_deserialise(Conf *conf, BinarySource *src);/*returns true on success*/
/*
* Functions to copy, free, serialise and deserialise FontSpecs.
@@ -1029,7 +1029,7 @@ int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/
FontSpec *fontspec_copy(const FontSpec *f);
void fontspec_free(FontSpec *f);
void fontspec_serialise(BinarySink *bs, FontSpec *f);
-FontSpec *fontspec_deserialise(void *data, int maxsize, int *used);
+FontSpec *fontspec_deserialise(BinarySource *src);
/*
* Exports from noise.c.
@@ -1456,7 +1456,7 @@ int filename_is_null(const Filename *fn);
Filename *filename_copy(const Filename *fn);
void filename_free(Filename *fn);
void filename_serialise(BinarySink *bs, const Filename *f);
-Filename *filename_deserialise(void *data, int maxsize, int *used);
+Filename *filename_deserialise(BinarySource *src);
char *get_username(void); /* return value needs freeing */
char *get_random_data(int bytes, const char *device); /* used in cmdgen.c */
char filename_char_sanitise(char c); /* rewrite special pathname chars */
diff --git a/unix/gtkmain.c b/unix/gtkmain.c
index ff6a15cf..906d029a 100644
--- a/unix/gtkmain.c
+++ b/unix/gtkmain.c
@@ -201,8 +201,9 @@ void launch_saved_session(const char *str)
int read_dupsession_data(Conf *conf, char *arg)
{
- int fd, i, ret, size, size_used;
+ int fd, i, ret, size;
char *data;
+ BinarySource src[1];
if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
@@ -222,35 +223,36 @@ int read_dupsession_data(Conf *conf, char *arg)
exit(1);
}
- size_used = conf_deserialise(conf, data, size);
- if (use_pty_argv && size > size_used) {
- int n = 0;
- i = size_used;
- while (i < size) {
- while (i < size && data[i]) i++;
- if (i >= size) {
- fprintf(stderr, "%s: malformed Duplicate Session data\n",
- appname);
- exit(1);
- }
- i++;
- n++;
- }
- pty_argv = snewn(n+1, char *);
- pty_argv[n] = NULL;
- n = 0;
- i = size_used;
- while (i < size) {
- char *p = data + i;
- while (i < size && data[i]) i++;
- assert(i < size);
- i++;
- pty_argv[n++] = dupstr(p);
- }
+ BinarySource_BARE_INIT(src, data, size);
+ if (!conf_deserialise(conf, src)) {
+ fprintf(stderr, "%s: malformed Duplicate Session data\n", appname);
+ exit(1);
}
+ if (use_pty_argv) {
+ int pty_argc = 0;
+ size_t argv_startpos = src->pos;
- sfree(data);
+ while (get_asciz(src), !get_err(src))
+ pty_argc++;
+ src->err = BSE_NO_ERROR;
+
+ if (pty_argc > 0) {
+ src->pos = argv_startpos;
+
+ pty_argv = snewn(pty_argc + 1, char *);
+ pty_argv[pty_argc] = NULL;
+ for (i = 0; i < pty_argc; i++)
+ pty_argv[i] = dupstr(get_asciz(src));
+ }
+ }
+
+ if (get_err(src) || get_avail(src) > 0) {
+ fprintf(stderr, "%s: malformed Duplicate Session data\n", appname);
+ exit(1);
+ }
+
+ sfree(data);
return 0;
}
diff --git a/unix/uxmisc.c b/unix/uxmisc.c
index e96829c4..c478856b 100644
--- a/unix/uxmisc.c
+++ b/unix/uxmisc.c
@@ -78,16 +78,9 @@ void filename_serialise(BinarySink *bs, const Filename *f)
{
put_asciz(bs, f->path);
}
-Filename *filename_deserialise(void *vdata, int maxsize, int *used)
+Filename *filename_deserialise(BinarySource *src)
{
- char *data = (char *)vdata;
- char *end;
- end = memchr(data, '\0', maxsize);
- if (!end)
- return NULL;
- end++;
- *used = end - data;
- return filename_from_str(data);
+ return filename_from_str(get_asciz(src));
}
char filename_char_sanitise(char c)
@@ -274,14 +267,9 @@ void fontspec_serialise(BinarySink *bs, FontSpec *f)
{
put_asciz(bs, f->name);
}
-FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used)
+FontSpec *fontspec_deserialise(BinarySource *src)
{
- char *data = (char *)vdata;
- char *end = memchr(data, '\0', maxsize);
- if (!end)
- return NULL;
- *used = end - data + 1;
- return fontspec_new(data);
+ return fontspec_new(get_asciz(src));
}
char *make_dir_and_check_ours(const char *dirname)
diff --git a/windows/window.c b/windows/window.c
index d6a67d42..88a251fe 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -480,7 +480,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
if (sscanf(p + 1, "%p:%u", &filemap, &cpsize) == 2 &&
(cp = MapViewOfFile(filemap, FILE_MAP_READ,
0, 0, cpsize)) != NULL) {
- conf_deserialise(conf, cp, cpsize);
+ BinarySource src[1];
+ BinarySource_BARE_INIT(src, cp, cpsize);
+ if (!conf_deserialise(conf, src))
+ modalfatalbox("Serialised configuration data was invalid");
UnmapViewOfFile(cp);
CloseHandle(filemap);
} else if (!do_config()) {
diff --git a/windows/winmisc.c b/windows/winmisc.c
index c3ec8306..e65ba1c9 100644
--- a/windows/winmisc.c
+++ b/windows/winmisc.c
@@ -54,16 +54,9 @@ void filename_serialise(BinarySink *bs, const Filename *f)
{
put_asciz(bs, f->path);
}
-Filename *filename_deserialise(void *vdata, int maxsize, int *used)
+Filename *filename_deserialise(BinarySource *src)
{
- char *data = (char *)vdata;
- char *end;
- end = memchr(data, '\0', maxsize);
- if (!end)
- return NULL;
- end++;
- *used = end - data;
- return filename_from_str(data);
+ return filename_from_str(get_asciz(src));
}
char filename_char_sanitise(char c)
@@ -561,21 +554,13 @@ void fontspec_serialise(BinarySink *bs, FontSpec *f)
put_uint32(bs, f->height);
put_uint32(bs, f->charset);
}
-FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used)
-{
- char *data = (char *)vdata;
- char *end;
- if (maxsize < 13)
- return NULL;
- end = memchr(data, '\0', maxsize-12);
- if (!end)
- return NULL;
- end++;
- *used = end - data + 12;
- return fontspec_new(data,
- GET_32BIT_MSB_FIRST(end),
- GET_32BIT_MSB_FIRST(end + 4),
- GET_32BIT_MSB_FIRST(end + 8));
+FontSpec *fontspec_deserialise(BinarySource *src)
+{
+ const char *name = get_asciz(src);
+ unsigned isbold = get_uint32(src);
+ unsigned height = get_uint32(src);
+ unsigned charset = get_uint32(src);
+ return fontspec_new(name, isbold, height, charset);
}
int open_for_write_would_lose_data(const Filename *fn)
From 59e83a8c758c651ac26c434d1a3949dac035f9ee Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 28 May 2018 17:42:03 +0100
Subject: [PATCH 331/607] Rewrite key import functions using BinarySource.
The OpenSSH PEM reader is the most interesting conversion out of
these: it was using a standalone function called get_ber_id_len(),
which only skipped over the header of an ASN.1 BER data item and left
the current position at the start of the payload. That's been replaced
by a get_ber() function more in the spirit of the new API, which
consumes the entire BER element, returning its header details and also
a ptrlen pointing at its payload.
(That function could easily be promoted out of import.c to somewhere
more central, if we ever had a need to handle ASN.1 on a larger scale
- e.g. X.509 certificates would find the same function useful. For the
moment, though, it can stay where it is.)
Other than that, this is a fairly mechanical API translation.
---
import.c | 812 +++++++++++++++++++++++--------------------------------
1 file changed, 340 insertions(+), 472 deletions(-)
diff --git a/import.c b/import.c
index 7b9d516b..bc54108a 100644
--- a/import.c
+++ b/import.c
@@ -168,51 +168,6 @@ void strip_crlf(char *str)
/* Primitive versus constructed bit. */
#define ASN1_CONSTRUCTED (1 << 5)
-static int ber_read_id_len(void *source, int sourcelen,
- int *id, int *length, int *flags)
-{
- unsigned char *p = (unsigned char *) source;
-
- if (sourcelen == 0)
- return -1;
-
- *flags = (*p & 0xE0);
- if ((*p & 0x1F) == 0x1F) {
- *id = 0;
- while (*p & 0x80) {
- p++, sourcelen--;
- if (sourcelen == 0)
- return -1;
- *id = (*id << 7) | (*p & 0x7F);
- }
- p++, sourcelen--;
- } else {
- *id = *p & 0x1F;
- p++, sourcelen--;
- }
-
- if (sourcelen == 0)
- return -1;
-
- if (*p & 0x80) {
- unsigned len;
- int n = *p & 0x7F;
- p++, sourcelen--;
- if (sourcelen < n)
- return -1;
- len = 0;
- while (n--)
- len = (len << 8) | (*p++);
- sourcelen -= n;
- *length = toint(len);
- } else {
- *length = *p;
- p++, sourcelen--;
- }
-
- return p - (unsigned char *) source;
-}
-
/*
* Write an ASN.1/BER identifier and length pair. Returns the
* number of bytes consumed. Assumes dest contains enough space.
@@ -265,30 +220,48 @@ static void BinarySink_put_ber_id_len(BinarySink *bs,
#define put_ber_id_len(bs, id, len, flags) \
BinarySink_put_ber_id_len(BinarySink_UPCAST(bs), id, len, flags)
-/* Simple structure to point to an mp-int within a blob. */
-struct mpint_pos { void *start; int bytes; };
+typedef struct ber_item {
+ int id;
+ int flags;
+ ptrlen data;
+} ber_item;
-static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret)
+static ber_item BinarySource_get_ber(BinarySource *src)
{
- int bytes;
- unsigned char *d = (unsigned char *) data;
+ ber_item toret;
+ unsigned char leadbyte, lenbyte;
+ size_t length;
- if (len < 4)
- goto error;
- bytes = toint(GET_32BIT(d));
- if (bytes < 0 || len-4 < bytes)
- goto error;
+ leadbyte = get_byte(src);
+ toret.flags = (leadbyte & 0xE0);
+ if ((leadbyte & 0x1F) == 0x1F) {
+ unsigned char idbyte;
- ret->start = d + 4;
- ret->bytes = bytes;
- return bytes+4;
+ toret.id = 0;
+ do {
+ idbyte = get_byte(src);
+ toret.id = (toret.id << 7) | (idbyte & 0x7F);
+ } while (idbyte & 0x80);
+ } else {
+ toret.id = leadbyte & 0x1F;
+ }
- error:
- ret->start = NULL;
- ret->bytes = -1;
- return len; /* ensure further calls fail as well */
+ lenbyte = get_byte(src);
+ if (lenbyte & 0x80) {
+ int nbytes = lenbyte & 0x7F;
+ length = 0;
+ while (nbytes-- > 0)
+ length = (length << 8) | get_byte(src);
+ } else {
+ length = lenbyte;
+ }
+
+ toret.data = get_data(src, length);
+ return toret;
}
+#define get_ber(bs) BinarySource_get_ber(BinarySource_UPCAST(bs))
+
/* ----------------------------------------------------------------------
* Code to read and write OpenSSH private keys, in the old-style PEM
* format.
@@ -306,8 +279,7 @@ struct openssh_pem_key {
int encrypted;
openssh_pem_enc encryption;
char iv[32];
- unsigned char *keyblob;
- int keyblob_len, keyblob_size;
+ strbuf *keyblob;
};
void BinarySink_put_mp_ssh2_from_string(
@@ -342,8 +314,7 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
int base64_chars = 0;
ret = snew(struct openssh_pem_key);
- ret->keyblob = NULL;
- ret->keyblob_len = ret->keyblob_size = 0;
+ ret->keyblob = strbuf_new();
fp = f_open(filename, "r", FALSE);
if (!fp) {
@@ -462,14 +433,7 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
goto error;
}
- if (ret->keyblob_len + len > ret->keyblob_size) {
- ret->keyblob_size = ret->keyblob_len + len + 256;
- ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,
- unsigned char);
- }
-
- memcpy(ret->keyblob + ret->keyblob_len, out, len);
- ret->keyblob_len += len;
+ put_data(ret->keyblob, out, len);
smemclr(out, sizeof(out));
}
@@ -485,12 +449,12 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
fclose(fp);
fp = NULL;
- if (ret->keyblob_len == 0 || !ret->keyblob) {
+ if (!ret->keyblob || ret->keyblob->len == 0) {
errmsg = "key body not present";
goto error;
}
- if (ret->encrypted && ret->keyblob_len % 8 != 0) {
+ if (ret->encrypted && ret->keyblob->len % 8 != 0) {
errmsg = "encrypted key blob is not a multiple of "
"cipher block size";
goto error;
@@ -508,10 +472,8 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
}
smemclr(base64_bit, sizeof(base64_bit));
if (ret) {
- if (ret->keyblob) {
- smemclr(ret->keyblob, ret->keyblob_size);
- sfree(ret->keyblob);
- }
+ if (ret->keyblob)
+ strbuf_free(ret->keyblob);
smemclr(ret, sizeof(*ret));
sfree(ret);
}
@@ -528,8 +490,7 @@ int openssh_pem_encrypted(const Filename *filename)
if (!key)
return 0;
ret = key->encrypted;
- smemclr(key->keyblob, key->keyblob_size);
- sfree(key->keyblob);
+ strbuf_free(key->keyblob);
smemclr(key, sizeof(*key));
sfree(key);
return ret;
@@ -541,14 +502,13 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
{
struct openssh_pem_key *key = load_openssh_pem_key(filename, errmsg_p);
struct ssh2_userkey *retkey;
- unsigned char *p;
- int ret, id, len, flags;
+ BinarySource src[1];
int i, num_integers;
struct ssh2_userkey *retval = NULL;
const char *errmsg;
strbuf *blob = strbuf_new();
int privptr = 0, publen;
- char *modptr = NULL;
+ const char *modptr = NULL;
int modlen = 0;
if (!key)
@@ -586,14 +546,14 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
*/
if (key->encryption == OP_E_3DES)
des3_decrypt_pubkey_ossh(keybuf, key->iv,
- key->keyblob, key->keyblob_len);
+ key->keyblob->u, key->keyblob->len);
else {
void *ctx;
assert(key->encryption == OP_E_AES);
ctx = aes_make_context();
aes128_key(ctx, keybuf);
aes_iv(ctx, key->iv);
- aes_ssh2_decrypt_blk(ctx, key->keyblob, key->keyblob_len);
+ aes_ssh2_decrypt_blk(ctx, key->keyblob->u, key->keyblob->len);
aes_free_context(ctx);
}
@@ -623,17 +583,21 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
* EXPLICIT [0] OID curve, EXPLICIT [1] BIT STRING pubPoint
*/
- p = key->keyblob;
-
- /* Expect the SEQUENCE header. Take its absence as a failure to
- * decrypt, if the key was encrypted. */
- ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);
- p += ret;
- if (ret < 0 || id != 16 || len < 0 ||
- key->keyblob+key->keyblob_len-p < len) {
- errmsg = "ASN.1 decoding failure";
- retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
- goto error;
+ BinarySource_BARE_INIT(src, key->keyblob->u, key->keyblob->len);
+
+ {
+ /* Expect the SEQUENCE header. Take its absence as a failure to
+ * decrypt, if the key was encrypted. */
+ ber_item seq = get_ber(src);
+ if (get_err(src) || seq.id != 16) {
+ errmsg = "ASN.1 decoding failure";
+ retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+ goto error;
+ }
+
+ /* Reinitialise our BinarySource to parse just the inside of that
+ * SEQUENCE. */
+ BinarySource_BARE_INIT(src, seq.data.ptr, seq.data.len);
}
/* Expect a load of INTEGERs. */
@@ -647,81 +611,53 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
if (key->keytype == OP_ECDSA) {
/* And now for something completely different */
- unsigned char *priv;
- int privlen;
+ ber_item integer, privkey, sub0, sub1, oid, pubkey;
const ssh_keyalg *alg;
const struct ec_curve *curve;
- /* Read INTEGER 1 */
- ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
- &id, &len, &flags);
- p += ret;
- if (ret < 0 || id != 2 || len != 1 ||
- key->keyblob+key->keyblob_len-p < len || p[0] != 1) {
- errmsg = "ASN.1 decoding failure";
- retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
- goto error;
- }
- p += 1;
- /* Read private key OCTET STRING */
- ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
- &id, &len, &flags);
- p += ret;
- if (ret < 0 || id != 4 || len < 0 ||
- key->keyblob+key->keyblob_len-p < len) {
- errmsg = "ASN.1 decoding failure";
- retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
- goto error;
- }
- priv = p;
- privlen = len;
- p += len;
- /* Read curve OID */
- ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
- &id, &len, &flags);
- p += ret;
- if (ret < 0 || id != 0 || len < 0 ||
- key->keyblob+key->keyblob_len-p < len) {
- errmsg = "ASN.1 decoding failure";
- retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
- goto error;
- }
- ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
- &id, &len, &flags);
- p += ret;
- if (ret < 0 || id != 6 || len < 0 ||
- key->keyblob+key->keyblob_len-p < len) {
+
+ /* Parse the outer layer of things inside the containing SEQUENCE */
+ integer = get_ber(src);
+ privkey = get_ber(src);
+ sub0 = get_ber(src);
+ sub1 = get_ber(src);
+
+ /* Now look inside sub0 for the curve OID */
+ BinarySource_BARE_INIT(src, sub0.data.ptr, sub0.data.len);
+ oid = get_ber(src);
+
+ /* And inside sub1 for the public-key BIT STRING */
+ BinarySource_BARE_INIT(src, sub1.data.ptr, sub1.data.len);
+ pubkey = get_ber(src);
+
+ if (get_err(src) ||
+ integer.id != 2 ||
+ integer.data.len != 1 ||
+ ((const unsigned char *)integer.data.ptr)[0] != 1 ||
+ privkey.id != 4 ||
+ sub0.id != 0 ||
+ sub1.id != 1 ||
+ oid.id != 6 ||
+ pubkey.id != 3) {
+
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
- alg = ec_alg_by_oid(len, p, &curve);
+
+ alg = ec_alg_by_oid(oid.data.len, oid.data.ptr, &curve);
if (!alg) {
errmsg = "Unsupported ECDSA curve.";
retval = NULL;
goto error;
}
- p += len;
- /* Read BIT STRING point */
- ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
- &id, &len, &flags);
- p += ret;
- if (ret < 0 || id != 1 || len < 0 ||
- key->keyblob+key->keyblob_len-p < len) {
- errmsg = "ASN.1 decoding failure";
- retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
- goto error;
- }
- ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
- &id, &len, &flags);
- p += ret;
- if (ret < 0 || id != 3 || len < 0 ||
- key->keyblob+key->keyblob_len-p < len ||
- len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) {
+ if (pubkey.data.len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
- p += 1; len -= 1; /* Skip 0x00 before point */
+ /* Skip 0x00 before point */
+ pubkey.data.ptr = (const char *)pubkey.data.ptr + 1;
+ pubkey.data.len -= 1;
/* Construct the key */
retkey = snew(struct ssh2_userkey);
@@ -729,9 +665,9 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
put_stringz(blob, alg->name);
put_stringz(blob, curve->name);
- put_string(blob, p, len);
+ put_stringpl(blob, pubkey.data);
publen = blob->len;
- put_mp_ssh2_from_string(blob, priv, privlen);
+ put_mp_ssh2_from_string(blob, privkey.data.ptr, privkey.data.len);
retkey->data = retkey->alg->createkey(
retkey->alg, blob->u, publen,
@@ -748,11 +684,9 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
put_stringz(blob, key->keytype == OP_DSA ? "ssh-dss" : "ssh-rsa");
for (i = 0; i < num_integers; i++) {
- ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
- &id, &len, &flags);
- p += ret;
- if (ret < 0 || id != 2 || len < 0 ||
- key->keyblob+key->keyblob_len-p < len) {
+ ber_item integer = get_ber(src);
+
+ if (get_err(src) || integer.id != 2) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
@@ -763,7 +697,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
* The first integer should be zero always (I think
* this is some sort of version indication).
*/
- if (len != 1 || p[0] != 0) {
+ if (integer.data.len != 1 ||
+ ((const unsigned char *)integer.data.ptr)[0] != 0) {
errmsg = "version number mismatch";
goto error;
}
@@ -775,10 +710,11 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
*/
if (i == 1) {
/* Save the details for after we deal with number 2. */
- modptr = (char *)p;
- modlen = len;
+ modptr = integer.data.ptr;
+ modlen = integer.data.len;
} else if (i != 6 && i != 7) {
- put_mp_ssh2_from_string(blob, p, len);
+ put_mp_ssh2_from_string(blob, integer.data.ptr,
+ integer.data.len);
if (i == 2) {
put_mp_ssh2_from_string(blob, modptr, modlen);
privptr = blob->len;
@@ -789,13 +725,11 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
* Integers 1-4 go into the public blob; integer 5 goes
* into the private blob.
*/
- put_mp_ssh2_from_string(blob, p, len);
+ put_mp_ssh2_from_string(blob, integer.data.ptr,
+ integer.data.len);
if (i == 4)
privptr = blob->len;
}
-
- /* Skip past the number. */
- p += len;
}
/*
@@ -833,8 +767,7 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
error:
strbuf_free(blob);
- smemclr(key->keyblob, key->keyblob_size);
- sfree(key->keyblob);
+ strbuf_free(key->keyblob);
smemclr(key, sizeof(*key));
sfree(key);
if (errmsg_p) *errmsg_p = errmsg;
@@ -847,13 +780,14 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
strbuf *pubblob, *privblob, *outblob;
unsigned char *spareblob;
int sparelen = 0;
- struct mpint_pos numbers[9];
+ ptrlen numbers[9];
int nnumbers, i;
const char *header, *footer;
char zero[1];
unsigned char iv[8];
int ret = 0;
FILE *fp;
+ BinarySource src[1];
/*
* Fetch the key blobs.
@@ -881,29 +815,29 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
* common code after that.
*/
if (key->alg == &ssh_rsa) {
- int pos;
- struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1;
+ ptrlen n, e, d, p, q, iqmp, dmp1, dmq1;
Bignum bd, bp, bq, bdmp1, bdmq1;
/*
* These blobs were generated from inside PuTTY, so we needn't
* treat them as untrusted.
*/
- pos = 4 + GET_32BIT(pubblob->u);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &e);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &n);
- pos = 0;
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &d);
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &p);
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &q);
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &iqmp);
-
- assert(e.start && iqmp.start); /* can't go wrong */
+ BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
+ get_string(src); /* skip algorithm name */
+ e = get_string(src);
+ n = get_string(src);
+ BinarySource_BARE_INIT(src, privblob->u, privblob->len);
+ d = get_string(src);
+ p = get_string(src);
+ q = get_string(src);
+ iqmp = get_string(src);
+
+ assert(!get_err(src)); /* can't go wrong */
/* We also need d mod (p-1) and d mod (q-1). */
- bd = bignum_from_bytes(d.start, d.bytes);
- bp = bignum_from_bytes(p.start, p.bytes);
- bq = bignum_from_bytes(q.start, q.bytes);
+ bd = bignum_from_bytes(d.ptr, d.len);
+ bp = bignum_from_bytes(p.ptr, p.len);
+ bq = bignum_from_bytes(q.ptr, q.len);
decbn(bp);
decbn(bq);
bdmp1 = bigmod(bd, bp);
@@ -912,20 +846,20 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
freebn(bp);
freebn(bq);
- dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8;
- dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8;
- sparelen = dmp1.bytes + dmq1.bytes;
+ dmp1.len = (bignum_bitcount(bdmp1)+8)/8;
+ dmq1.len = (bignum_bitcount(bdmq1)+8)/8;
+ sparelen = dmp1.len + dmq1.len;
spareblob = snewn(sparelen, unsigned char);
- dmp1.start = spareblob;
- dmq1.start = spareblob + dmp1.bytes;
- for (i = 0; i < dmp1.bytes; i++)
- spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i);
- for (i = 0; i < dmq1.bytes; i++)
- spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i);
+ dmp1.ptr = spareblob;
+ dmq1.ptr = spareblob + dmp1.len;
+ for (i = 0; i < dmp1.len; i++)
+ spareblob[i] = bignum_byte(bdmp1, dmp1.len-1 - i);
+ for (i = 0; i < dmq1.len; i++)
+ spareblob[i+dmp1.len] = bignum_byte(bdmq1, dmq1.len-1 - i);
freebn(bdmp1);
freebn(bdmq1);
- numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
+ numbers[0] = make_ptrlen(zero, 1); zero[0] = '\0';
numbers[1] = n;
numbers[2] = e;
numbers[3] = d;
@@ -939,24 +873,24 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
header = "-----BEGIN RSA PRIVATE KEY-----\n";
footer = "-----END RSA PRIVATE KEY-----\n";
} else { /* ssh-dss */
- int pos;
- struct mpint_pos p, q, g, y, x;
+ ptrlen p, q, g, y, x;
/*
* These blobs were generated from inside PuTTY, so we needn't
* treat them as untrusted.
*/
- pos = 4 + GET_32BIT(pubblob->u);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &p);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &q);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &g);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &y);
- pos = 0;
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &x);
-
- assert(y.start && x.start); /* can't go wrong */
-
- numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
+ BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
+ get_string(src); /* skip algorithm name */
+ p = get_string(src);
+ q = get_string(src);
+ g = get_string(src);
+ y = get_string(src);
+ BinarySource_BARE_INIT(src, privblob->u, privblob->len);
+ x = get_string(src);
+
+ assert(!get_err(src)); /* can't go wrong */
+
+ numbers[0].ptr = zero; numbers[0].len = 1; zero[0] = '\0';
numbers[1] = p;
numbers[2] = q;
numbers[3] = g;
@@ -970,8 +904,8 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
seq = strbuf_new();
for (i = 0; i < nnumbers; i++) {
- put_ber_id_len(seq, 2, numbers[i].bytes, 0);
- put_data(seq, numbers[i].start, numbers[i].bytes);
+ put_ber_id_len(seq, 2, numbers[i].len, 0);
+ put_data(seq, numbers[i].ptr, numbers[i].len);
}
put_ber_id_len(outblob, 16, seq->len, ASN1_CONSTRUCTED);
put_data(outblob, seq->s, seq->len);
@@ -1161,14 +1095,12 @@ struct openssh_new_key {
int rounds;
/* This points to a position within keyblob, not a
* separately allocated thing */
- const unsigned char *salt;
- int saltlen;
+ ptrlen salt;
} bcrypt;
} kdfopts;
int nkeys, key_wanted;
/* This too points to a position within keyblob */
- unsigned char *privatestr;
- int privatelen;
+ ptrlen private;
unsigned char *keyblob;
int keyblob_len, keyblob_size;
@@ -1184,11 +1116,9 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
char *p;
char base64_bit[4];
int base64_chars = 0;
- const void *filedata;
- int filelen;
- const void *string, *kdfopts, *bcryptsalt, *pubkey;
- int stringlen, kdfoptlen, bcryptsaltlen, pubkeylen;
- unsigned bcryptrounds, nkeys, key_index;
+ BinarySource src[1];
+ ptrlen str;
+ unsigned key_index;
ret = snew(struct openssh_new_key);
ret->keyblob = NULL;
@@ -1268,68 +1198,61 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
goto error;
}
- filedata = ret->keyblob;
- filelen = ret->keyblob_len;
+ BinarySource_BARE_INIT(src, ret->keyblob, ret->keyblob_len);
- if (filelen < 15 || 0 != memcmp(filedata, "openssh-key-v1\0", 15)) {
+ if (strcmp(get_asciz(src), "openssh-key-v1") != 0) {
errmsg = "new-style OpenSSH magic number missing\n";
goto error;
}
- filedata = (const char *)filedata + 15;
- filelen -= 15;
- if (!(string = get_ssh_string(&filelen, &filedata, &stringlen))) {
- errmsg = "encountered EOF before cipher name\n";
- goto error;
- }
- if (match_ssh_id(stringlen, string, "none")) {
+ /* Cipher name */
+ str = get_string(src);
+ if (ptrlen_eq_string(str, "none")) {
ret->cipher = ON_E_NONE;
- } else if (match_ssh_id(stringlen, string, "aes256-cbc")) {
+ } else if (ptrlen_eq_string(str, "aes256-cbc")) {
ret->cipher = ON_E_AES256CBC;
- } else if (match_ssh_id(stringlen, string, "aes256-ctr")) {
+ } else if (ptrlen_eq_string(str, "aes256-ctr")) {
ret->cipher = ON_E_AES256CTR;
} else {
- errmsg = "unrecognised cipher name\n";
+ errmsg = get_err(src) ? "no cipher name found" :
+ "unrecognised cipher name\n";
goto error;
}
- if (!(string = get_ssh_string(&filelen, &filedata, &stringlen))) {
- errmsg = "encountered EOF before kdf name\n";
- goto error;
- }
- if (match_ssh_id(stringlen, string, "none")) {
+ /* Key derivation function name */
+ str = get_string(src);
+ if (ptrlen_eq_string(str, "none")) {
ret->kdf = ON_K_NONE;
- } else if (match_ssh_id(stringlen, string, "bcrypt")) {
+ } else if (ptrlen_eq_string(str, "bcrypt")) {
ret->kdf = ON_K_BCRYPT;
} else {
- errmsg = "unrecognised kdf name\n";
+ errmsg = get_err(src) ? "no kdf name found" :
+ "unrecognised kdf name\n";
goto error;
}
- if (!(kdfopts = get_ssh_string(&filelen, &filedata, &kdfoptlen))) {
- errmsg = "encountered EOF before kdf options\n";
- goto error;
- }
+ /* KDF extra options */
+ str = get_string(src);
switch (ret->kdf) {
case ON_K_NONE:
- if (kdfoptlen != 0) {
+ if (str.len != 0) {
errmsg = "expected empty options string for 'none' kdf";
goto error;
}
break;
case ON_K_BCRYPT:
- if (!(bcryptsalt = get_ssh_string(&kdfoptlen, &kdfopts,
- &bcryptsaltlen))) {
- errmsg = "bcrypt options string did not contain salt\n";
- goto error;
- }
- if (!get_ssh_uint32(&kdfoptlen, &kdfopts, &bcryptrounds)) {
- errmsg = "bcrypt options string did not contain round count\n";
- goto error;
+ {
+ BinarySource opts[1];
+
+ BinarySource_BARE_INIT(opts, str.ptr, str.len);
+ ret->kdfopts.bcrypt.salt = get_string(opts);
+ ret->kdfopts.bcrypt.rounds = get_uint32(opts);
+
+ if (get_err(opts)) {
+ errmsg = "failed to parse bcrypt options string";
+ goto error;
+ }
}
- ret->kdfopts.bcrypt.salt = bcryptsalt;
- ret->kdfopts.bcrypt.saltlen = bcryptsaltlen;
- ret->kdfopts.bcrypt.rounds = bcryptrounds;
break;
}
@@ -1345,35 +1268,27 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
* 'key_wanted' field is set to a value in the range [0,
* nkeys) by some mechanism.
*/
- if (!get_ssh_uint32(&filelen, &filedata, &nkeys)) {
- errmsg = "encountered EOF before key count\n";
+ ret->nkeys = toint(get_uint32(src));
+ if (ret->nkeys != 1) {
+ errmsg = get_err(src) ? "no key count found" :
+ "multiple keys in new-style OpenSSH key file not supported\n";
goto error;
}
- if (nkeys != 1) {
- errmsg = "multiple keys in new-style OpenSSH key file "
- "not supported\n";
- goto error;
- }
- ret->nkeys = nkeys;
ret->key_wanted = 0;
- for (key_index = 0; key_index < nkeys; key_index++) {
- if (!(pubkey = get_ssh_string(&filelen, &filedata, &pubkeylen))) {
- errmsg = "encountered EOF before kdf options\n";
- goto error;
- }
- }
+ /* Read and ignore a string per public key. */
+ for (key_index = 0; key_index < ret->nkeys; key_index++)
+ str = get_string(src);
/*
* Now we expect a string containing the encrypted part of the
* key file.
*/
- if (!(string = get_ssh_string(&filelen, &filedata, &stringlen))) {
- errmsg = "encountered EOF before private key container\n";
+ ret->private = get_string(src);
+ if (get_err(src)) {
+ errmsg = "no private key container string found\n";
goto error;
}
- ret->privatestr = (unsigned char *)string;
- ret->privatelen = stringlen;
/*
* And now we're done, until asked to actually decrypt.
@@ -1427,9 +1342,9 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
int i;
struct ssh2_userkey *retval = NULL;
const char *errmsg;
- unsigned checkint0, checkint1;
- const void *priv, *string;
- int privlen, stringlen, key_index;
+ unsigned checkint;
+ BinarySource src[1];
+ int key_index;
const ssh_keyalg *alg = NULL;
if (!key)
@@ -1460,8 +1375,8 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
break;
case ON_K_BCRYPT:
openssh_bcrypt(passphrase,
- key->kdfopts.bcrypt.salt,
- key->kdfopts.bcrypt.saltlen,
+ key->kdfopts.bcrypt.salt.ptr,
+ key->kdfopts.bcrypt.salt.len,
key->kdfopts.bcrypt.rounds,
keybuf, keysize);
break;
@@ -1473,7 +1388,7 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
break;
case ON_E_AES256CBC:
case ON_E_AES256CTR:
- if (key->privatelen % 16 != 0) {
+ if (key->private.len % 16 != 0) {
errmsg = "private key container length is not a"
" multiple of AES block size\n";
goto error;
@@ -1482,13 +1397,15 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
void *ctx = aes_make_context();
aes256_key(ctx, keybuf);
aes_iv(ctx, keybuf + 32);
+ /* Decrypt the private section in place, casting away
+ * the const from key->private being a ptrlen */
if (key->cipher == ON_E_AES256CBC) {
- aes_ssh2_decrypt_blk(ctx, key->privatestr,
- key->privatelen);
+ aes_ssh2_decrypt_blk(ctx, (char *)key->private.ptr,
+ key->private.len);
}
else {
- aes_ssh2_sdctr(ctx, key->privatestr,
- key->privatelen);
+ aes_ssh2_sdctr(ctx, (char *)key->private.ptr,
+ key->private.len);
}
aes_free_context(ctx);
}
@@ -1502,29 +1419,23 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
* Now parse the entire encrypted section, and extract the key
* identified by key_wanted.
*/
- priv = key->privatestr;
- privlen = key->privatelen;
+ BinarySource_BARE_INIT(src, key->private.ptr, key->private.len);
- if (!get_ssh_uint32(&privlen, &priv, &checkint0) ||
- !get_ssh_uint32(&privlen, &priv, &checkint1) ||
- checkint0 != checkint1) {
+ checkint = get_uint32(src);
+ if (get_uint32(src) != checkint || get_err(src)) {
errmsg = "decryption check failed";
goto error;
}
retkey = NULL;
for (key_index = 0; key_index < key->nkeys; key_index++) {
- const unsigned char *thiskey;
- int thiskeylen;
+ ptrlen keytype, thiskey, comment;
/*
* Read the key type, which will tell us how to scan over
* the key to get to the next one.
*/
- if (!(string = get_ssh_string(&privlen, &priv, &stringlen))) {
- errmsg = "expected key type in private string";
- goto error;
- }
+ keytype = get_string(src);
/*
* Preliminary key type identification, and decide how
@@ -1533,38 +1444,35 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
* of strings, so we just need to know how many of them to
* skip over. (The numbers below exclude the key comment.)
*/
- {
- /* find_pubkey_alg needs a zero-terminated copy of the
- * algorithm name */
- char *name_zt = dupprintf("%.*s", stringlen, (char *)string);
- alg = find_pubkey_alg(name_zt);
- sfree(name_zt);
- }
-
+ alg = find_pubkey_alg_len(keytype);
if (!alg) {
errmsg = "private key type not recognised\n";
goto error;
}
- thiskey = priv;
+ thiskey.ptr = get_ptr(src);
/*
* Skip over the pieces of key.
*/
- for (i = 0; i < alg->openssh_private_npieces; i++) {
- if (!(string = get_ssh_string(&privlen, &priv, &stringlen))) {
- errmsg = "ran out of data in mid-private-key";
- goto error;
- }
+ for (i = 0; i < alg->openssh_private_npieces; i++)
+ get_string(src);
+
+ if (get_err(src)) {
+ errmsg = "unable to read entire private key";
+ goto error;
}
- thiskeylen = (int)((const unsigned char *)priv -
- (const unsigned char *)thiskey);
+ thiskey.len = (const char *)get_ptr(src) - (const char *)thiskey.ptr;
+
if (key_index == key->key_wanted) {
+ const unsigned char *blobptr = thiskey.ptr;
+ int bloblen = thiskey.len;
+
retkey = snew(struct ssh2_userkey);
retkey->comment = NULL;
retkey->alg = alg;
- retkey->data = alg->openssh_createkey(alg, &thiskey, &thiskeylen);
+ retkey->data = alg->openssh_createkey(alg, &blobptr, &bloblen);
if (!retkey->data) {
errmsg = "unable to create key data structure";
goto error;
@@ -1574,14 +1482,14 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
/*
* Read the key comment.
*/
- if (!(string = get_ssh_string(&privlen, &priv, &stringlen))) {
- errmsg = "ran out of data at key comment";
+ comment = get_string(src);
+ if (get_err(src)) {
+ errmsg = "unable to read key comment";
goto error;
}
if (key_index == key->key_wanted) {
assert(retkey);
- retkey->comment = dupprintf("%.*s", stringlen,
- (const char *)string);
+ retkey->comment = mkstr(comment);
}
}
@@ -1593,11 +1501,13 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
/*
* Now we expect nothing left but padding.
*/
- for (i = 0; i < privlen; i++) {
- if (((const unsigned char *)priv)[i] != (unsigned char)(i+1)) {
- errmsg = "padding at end of private string did not match";
- goto error;
- }
+ {
+ unsigned char expected_pad_byte = 1;
+ while (get_avail(src) > 0)
+ if (get_byte(src) != expected_pad_byte++) {
+ errmsg = "padding at end of private string did not match";
+ goto error;
+ }
}
errmsg = NULL; /* no error */
@@ -2014,36 +1924,25 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename,
int sshcom_encrypted(const Filename *filename, char **comment)
{
struct sshcom_key *key = load_sshcom_key(filename, NULL);
- int pos, len, answer;
-
- answer = 0;
+ BinarySource src[1];
+ ptrlen str;
+ int answer = FALSE;
*comment = NULL;
if (!key)
goto done;
- /*
- * Check magic number.
- */
- if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) {
- goto done; /* key is invalid */
- }
+ BinarySource_BARE_INIT(src, key->keyblob, key->keyblob_len);
- /*
- * Find the cipher-type string.
- */
- pos = 8;
- if (key->keyblob_len < pos+4)
- goto done; /* key is far too short */
- len = toint(GET_32BIT(key->keyblob + pos));
- if (len < 0 || len > key->keyblob_len - pos - 4)
- goto done; /* key is far too short */
- pos += 4 + len; /* skip key type */
- len = toint(GET_32BIT(key->keyblob + pos)); /* find cipher-type length */
- if (len < 0 || len > key->keyblob_len - pos - 4)
- goto done; /* cipher type string is incomplete */
- if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4))
- answer = 1;
+ if (get_uint32(src) != SSHCOM_MAGIC_NUMBER)
+ goto done; /* key is invalid */
+ get_uint32(src); /* skip length field */
+ get_string(src); /* skip key type */
+ str = get_string(src); /* cipher type */
+ if (get_err(src))
+ goto done; /* key is invalid */
+ if (!ptrlen_eq_string(str, "none"))
+ answer = TRUE;
done:
if (key) {
@@ -2058,29 +1957,6 @@ int sshcom_encrypted(const Filename *filename, char **comment)
return answer;
}
-static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret)
-{
- unsigned bits, bytes;
- unsigned char *d = (unsigned char *) data;
-
- if (len < 4)
- goto error;
- bits = GET_32BIT(d);
-
- bytes = (bits + 7) / 8;
- if (len < 4+bytes)
- goto error;
-
- ret->start = d + 4;
- ret->bytes = bytes;
- return bytes+4;
-
- error:
- ret->start = NULL;
- ret->bytes = -1;
- return len; /* ensure further calls fail as well */
-}
-
void BinarySink_put_mp_sshcom_from_string(
BinarySink *bs, const void *bytesv, int nbytes)
{
@@ -2101,18 +1977,27 @@ void BinarySink_put_mp_sshcom_from_string(
#define put_mp_sshcom_from_string(bs, val, len) \
BinarySink_put_mp_sshcom_from_string(BinarySink_UPCAST(bs), val, len)
+static ptrlen BinarySource_get_mp_sshcom_as_string(BinarySource *src)
+{
+ unsigned bits = get_uint32(src);
+ return get_data(src, (bits + 7) / 8);
+}
+
+#define get_mp_sshcom_as_string(bs) \
+ BinarySource_get_mp_sshcom_as_string(BinarySource_UPCAST(bs))
+
struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
const char **errmsg_p)
{
struct sshcom_key *key = load_sshcom_key(filename, errmsg_p);
const char *errmsg;
- int pos, len, publen;
+ BinarySource src[1];
+ ptrlen str, ciphertext;
+ int publen;
const char prefix_rsa[] = "if-modn{sign{rsa";
const char prefix_dsa[] = "dl-modp{sign{dsa";
enum { RSA, DSA } type;
int encrypted;
- char *ciphertext;
- int cipherlen;
struct ssh2_userkey *ret = NULL, *retkey;
const ssh_keyalg *alg;
strbuf *blob = NULL;
@@ -2120,68 +2005,48 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
if (!key)
return NULL;
- /*
- * Check magic number.
- */
- if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) {
+ BinarySource_BARE_INIT(src, key->keyblob, key->keyblob_len);
+
+ if (get_uint32(src) != SSHCOM_MAGIC_NUMBER) {
errmsg = "key does not begin with magic number";
goto error;
}
+ get_uint32(src); /* skip length field */
/*
* Determine the key type.
*/
- pos = 8;
- if (key->keyblob_len < pos+4 ||
- (len = toint(GET_32BIT(key->keyblob + pos))) < 0 ||
- len > key->keyblob_len - pos - 4) {
- errmsg = "key blob does not contain a key type string";
- goto error;
- }
- if (len > sizeof(prefix_rsa) - 1 &&
- !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) {
+ str = get_string(src);
+ if (str.len > sizeof(prefix_rsa) - 1 &&
+ !memcmp(str.ptr, prefix_rsa, sizeof(prefix_rsa) - 1)) {
type = RSA;
- } else if (len > sizeof(prefix_dsa) - 1 &&
- !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) {
+ } else if (str.len > sizeof(prefix_dsa) - 1 &&
+ !memcmp(str.ptr, prefix_dsa, sizeof(prefix_dsa) - 1)) {
type = DSA;
} else {
errmsg = "key is of unknown type";
goto error;
}
- pos += 4+len;
/*
* Determine the cipher type.
*/
- if (key->keyblob_len < pos+4 ||
- (len = toint(GET_32BIT(key->keyblob + pos))) < 0 ||
- len > key->keyblob_len - pos - 4) {
- errmsg = "key blob does not contain a cipher type string";
- goto error;
- }
- if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4))
+ str = get_string(src);
+ if (ptrlen_eq_string(str, "none"))
encrypted = 0;
- else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8))
+ else if (ptrlen_eq_string(str, "3des-cbc"))
encrypted = 1;
else {
errmsg = "key encryption is of unknown type";
goto error;
}
- pos += 4+len;
/*
* Get hold of the encrypted part of the key.
*/
- if (key->keyblob_len < pos+4 ||
- (len = toint(GET_32BIT(key->keyblob + pos))) < 0 ||
- len > key->keyblob_len - pos - 4) {
- errmsg = "key blob does not contain actual key data";
- goto error;
- }
- ciphertext = (char *)key->keyblob + pos + 4;
- cipherlen = len;
- if (cipherlen == 0) {
- errmsg = "length of key data is zero";
+ ciphertext = get_string(src);
+ if (ciphertext.len == 0) {
+ errmsg = "no key data found";
goto error;
}
@@ -2200,7 +2065,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
struct MD5Context md5c;
unsigned char keybuf[32], iv[8];
- if (cipherlen % 8 != 0) {
+ if (ciphertext.len % 8 != 0) {
errmsg = "encrypted part of key is not a multiple of cipher block"
" size";
goto error;
@@ -2216,10 +2081,12 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
MD5Final(keybuf+16, &md5c);
/*
- * Now decrypt the key blob.
+ * Now decrypt the key blob in place (casting away const from
+ * ciphertext being a ptrlen).
*/
memset(iv, 0, sizeof(iv));
- des3_decrypt_pubkey_ossh(keybuf, iv, ciphertext, cipherlen);
+ des3_decrypt_pubkey_ossh(keybuf, iv,
+ (char *)ciphertext.ptr, ciphertext.len);
smemclr(&md5c, sizeof(md5c));
smemclr(keybuf, sizeof(keybuf));
@@ -2235,15 +2102,16 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
}
/*
- * Strip away the containing string to get to the real meat.
+ * Expect the ciphertext to be formatted as a containing string,
+ * and reinitialise src to start parsing the inside of that string.
*/
- len = toint(GET_32BIT(ciphertext));
- if (len < 0 || len > cipherlen-4) {
+ BinarySource_BARE_INIT(src, ciphertext.ptr, ciphertext.len);
+ str = get_string(src);
+ if (get_err(src)) {
errmsg = "containing string was ill-formed";
goto error;
}
- ciphertext += 4;
- cipherlen = len;
+ BinarySource_BARE_INIT(src, str.ptr, str.len);
/*
* Now we break down into RSA versus DSA. In either case we'll
@@ -2252,56 +2120,55 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
*/
blob = strbuf_new();
if (type == RSA) {
- struct mpint_pos n, e, d, u, p, q;
- int pos = 0;
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e);
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d);
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n);
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u);
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);
- if (!q.start) {
+ ptrlen n, e, d, u, p, q;
+
+ e = get_mp_sshcom_as_string(src);
+ d = get_mp_sshcom_as_string(src);
+ n = get_mp_sshcom_as_string(src);
+ u = get_mp_sshcom_as_string(src);
+ p = get_mp_sshcom_as_string(src);
+ q = get_mp_sshcom_as_string(src);
+ if (get_err(src)) {
errmsg = "key data did not contain six integers";
goto error;
}
alg = &ssh_rsa;
put_stringz(blob, "ssh-rsa");
- put_mp_ssh2_from_string(blob, e.start, e.bytes);
- put_mp_ssh2_from_string(blob, n.start, n.bytes);
+ put_mp_ssh2_from_string(blob, e.ptr, e.len);
+ put_mp_ssh2_from_string(blob, n.ptr, n.len);
publen = blob->len;
- put_string(blob, d.start, d.bytes);
- put_mp_ssh2_from_string(blob, q.start, q.bytes);
- put_mp_ssh2_from_string(blob, p.start, p.bytes);
- put_mp_ssh2_from_string(blob, u.start, u.bytes);
+ put_mp_ssh2_from_string(blob, d.ptr, d.len);
+ put_mp_ssh2_from_string(blob, q.ptr, q.len);
+ put_mp_ssh2_from_string(blob, p.ptr, p.len);
+ put_mp_ssh2_from_string(blob, u.ptr, u.len);
} else {
- struct mpint_pos p, q, g, x, y;
- int pos = 4;
+ ptrlen p, q, g, x, y;
assert(type == DSA); /* the only other option from the if above */
- if (GET_32BIT(ciphertext) != 0) {
+ if (get_uint32(src) != 0) {
errmsg = "predefined DSA parameters not supported";
goto error;
}
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g);
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y);
- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x);
- if (!x.start) {
+ p = get_mp_sshcom_as_string(src);
+ g = get_mp_sshcom_as_string(src);
+ q = get_mp_sshcom_as_string(src);
+ y = get_mp_sshcom_as_string(src);
+ x = get_mp_sshcom_as_string(src);
+ if (get_err(src)) {
errmsg = "key data did not contain five integers";
goto error;
}
alg = &ssh_dss;
put_stringz(blob, "ssh-dss");
- put_mp_ssh2_from_string(blob, p.start, p.bytes);
- put_mp_ssh2_from_string(blob, q.start, q.bytes);
- put_mp_ssh2_from_string(blob, g.start, g.bytes);
- put_mp_ssh2_from_string(blob, y.start, y.bytes);
+ put_mp_ssh2_from_string(blob, p.ptr, p.len);
+ put_mp_ssh2_from_string(blob, q.ptr, q.len);
+ put_mp_ssh2_from_string(blob, g.ptr, g.len);
+ put_mp_ssh2_from_string(blob, y.ptr, y.len);
publen = blob->len;
- put_mp_ssh2_from_string(blob, x.start, x.bytes);
+ put_mp_ssh2_from_string(blob, x.ptr, x.len);
}
retkey = snew(struct ssh2_userkey);
@@ -2334,8 +2201,9 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
char *passphrase)
{
strbuf *pubblob, *privblob, *outblob;
- struct mpint_pos numbers[6];
+ ptrlen numbers[6];
int nnumbers, initial_zero, lenpos, i;
+ BinarySource src[1];
const char *type;
char *ciphertext;
int cipherlen;
@@ -2356,23 +2224,23 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
* key blob, and also decide on the header line.
*/
if (key->alg == &ssh_rsa) {
- int pos;
- struct mpint_pos n, e, d, p, q, iqmp;
+ ptrlen n, e, d, p, q, iqmp;
/*
* These blobs were generated from inside PuTTY, so we needn't
* treat them as untrusted.
*/
- pos = 4 + GET_32BIT(pubblob->u);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &e);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &n);
- pos = 0;
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &d);
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &p);
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &q);
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &iqmp);
-
- assert(e.start && iqmp.start); /* can't go wrong */
+ BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
+ get_string(src); /* skip algorithm name */
+ e = get_string(src);
+ n = get_string(src);
+ BinarySource_BARE_INIT(src, privblob->u, privblob->len);
+ d = get_string(src);
+ p = get_string(src);
+ q = get_string(src);
+ iqmp = get_string(src);
+
+ assert(!get_err(src)); /* can't go wrong */
numbers[0] = e;
numbers[1] = d;
@@ -2385,22 +2253,22 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
initial_zero = 0;
type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}";
} else if (key->alg == &ssh_dss) {
- int pos;
- struct mpint_pos p, q, g, y, x;
+ ptrlen p, q, g, y, x;
/*
* These blobs were generated from inside PuTTY, so we needn't
* treat them as untrusted.
*/
- pos = 4 + GET_32BIT(pubblob->u);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &p);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &q);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &g);
- pos += ssh2_read_mpint(pubblob->u+pos, pubblob->len-pos, &y);
- pos = 0;
- pos += ssh2_read_mpint(privblob->u+pos, privblob->len-pos, &x);
+ BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
+ get_string(src); /* skip algorithm name */
+ p = get_string(src);
+ q = get_string(src);
+ g = get_string(src);
+ y = get_string(src);
+ BinarySource_BARE_INIT(src, privblob->u, privblob->len);
+ x = get_string(src);
- assert(y.start && x.start); /* can't go wrong */
+ assert(!get_err(src)); /* can't go wrong */
numbers[0] = p;
numbers[1] = g;
@@ -2431,7 +2299,7 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
if (initial_zero)
put_uint32(outblob, 0);
for (i = 0; i < nnumbers; i++)
- put_mp_sshcom_from_string(outblob, numbers[i].start, numbers[i].bytes);
+ put_mp_sshcom_from_string(outblob, numbers[i].ptr, numbers[i].len);
/* Now wrap up the encrypted payload. */
PUT_32BIT(outblob->s + lenpos + 4,
outblob->len - (lenpos + 8));
From 28c086ca9ad79f6ffc4d9af3f69e9eb204e3b5eb Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 29 May 2018 19:29:54 +0100
Subject: [PATCH 332/607] Rewrite key loading functions using BinarySource.
This does for sshpubk.c's handling of PuTTY's native key formats what
the previous commit did for the foreign formats handled by import.c.
---
sshpubk.c | 157 ++++++++++++++++++++++--------------------------------
1 file changed, 65 insertions(+), 92 deletions(-)
diff --git a/sshpubk.c b/sshpubk.c
index 68cf6945..01cacb58 100644
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -27,70 +27,52 @@ static int rsa_ssh1_load_main(FILE * fp, struct RSAKey *key, int pub_only,
char **commentptr, const char *passphrase,
const char **error)
{
- unsigned char buf[16384];
- unsigned char keybuf[16];
- int len;
- int i, j, ciphertype;
+ strbuf *buf;
+ int ciphertype;
int ret = 0;
struct MD5Context md5c;
- char *comment;
+ ptrlen comment;
+ BinarySource src[1];
*error = NULL;
/* Slurp the whole file (minus the header) into a buffer. */
- len = fread(buf, 1, sizeof(buf), fp);
- fclose(fp);
- if (len < 0 || len == sizeof(buf)) {
- *error = "error reading file";
- goto end; /* file too big or not read */
+ buf = strbuf_new();
+ {
+ int ch;
+ while ((ch = fgetc(fp)) != EOF)
+ put_byte(buf, ch);
}
+ fclose(fp);
+
+ BinarySource_BARE_INIT(src, buf->u, buf->len);
- i = 0;
*error = "file format error";
/*
- * A zero byte. (The signature includes a terminating NUL.)
+ * A zero byte. (The signature includes a terminating NUL, which
+ * we haven't gone past yet because we read it using fgets which
+ * stopped after the \n.)
*/
- if (len - i < 1 || buf[i] != 0)
+ if (get_byte(src) != 0)
goto end;
- i++;
/* One byte giving encryption type, and one reserved uint32. */
- if (len - i < 1)
- goto end;
- ciphertype = buf[i];
+ ciphertype = get_byte(src);
if (ciphertype != 0 && ciphertype != SSH_CIPHER_3DES)
goto end;
- i++;
- if (len - i < 4)
- goto end; /* reserved field not present */
- if (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0
- || buf[i + 3] != 0) goto end; /* reserved field nonzero, panic! */
- i += 4;
+ if (get_uint32(src) != 0)
+ goto end; /* reserved field nonzero, panic! */
/* Now the serious stuff. An ordinary SSH-1 public key. */
- j = rsa_ssh1_readpub(buf + i, len - i, key, NULL, RSA_SSH1_MODULUS_FIRST);
- if (j < 0)
- goto end; /* overran */
- i += j;
+ get_rsa_ssh1_pub(src, key, NULL, RSA_SSH1_MODULUS_FIRST);
/* Next, the comment field. */
- j = toint(GET_32BIT(buf + i));
- i += 4;
- if (j < 0 || len - i < j)
- goto end;
- comment = snewn(j + 1, char);
- if (comment) {
- memcpy(comment, buf + i, j);
- comment[j] = '\0';
- }
- i += j;
+ comment = get_string(src);
if (commentptr)
- *commentptr = dupstr(comment);
+ *commentptr = mkstr(comment);
if (key)
- key->comment = comment;
- else
- sfree(comment);
+ key->comment = mkstr(comment);
if (pub_only) {
ret = 1;
@@ -107,10 +89,16 @@ static int rsa_ssh1_load_main(FILE * fp, struct RSAKey *key, int pub_only,
* Decrypt remainder of buffer.
*/
if (ciphertype) {
+ unsigned char keybuf[16];
+ size_t enclen = buf->len - src->pos;
+
+ if (enclen & 7)
+ goto end;
+
MD5Init(&md5c);
put_data(&md5c, passphrase, strlen(passphrase));
MD5Final(keybuf, &md5c);
- des3_decrypt_pubkey(keybuf, buf + i, (len - i + 7) & ~7);
+ des3_decrypt_pubkey(keybuf, buf->u + src->pos, enclen);
smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */
}
@@ -118,32 +106,27 @@ static int rsa_ssh1_load_main(FILE * fp, struct RSAKey *key, int pub_only,
* We are now in the secret part of the key. The first four
* bytes should be of the form a, b, a, b.
*/
- if (len - i < 4)
- goto end;
- if (buf[i] != buf[i + 2] || buf[i + 1] != buf[i + 3]) {
- *error = "wrong passphrase";
- ret = -1;
- goto end;
+ {
+ int b0a = get_byte(src);
+ int b1a = get_byte(src);
+ int b0b = get_byte(src);
+ int b1b = get_byte(src);
+ if (b0a != b0b || b1a != b1b) {
+ *error = "wrong passphrase";
+ ret = -1;
+ goto end;
+ }
}
- i += 4;
/*
* After that, we have one further bignum which is our
* decryption exponent, and then the three auxiliary values
* (iqmp, q, p).
*/
- j = rsa_ssh1_readpriv(buf + i, len - i, key);
- if (j < 0) goto end;
- i += j;
- j = ssh1_read_bignum(buf + i, len - i, &key->iqmp);
- if (j < 0) goto end;
- i += j;
- j = ssh1_read_bignum(buf + i, len - i, &key->q);
- if (j < 0) goto end;
- i += j;
- j = ssh1_read_bignum(buf + i, len - i, &key->p);
- if (j < 0) goto end;
- i += j;
+ get_rsa_ssh1_priv(src, key);
+ key->iqmp = get_mp_ssh1(src);
+ key->q = get_mp_ssh1(src);
+ key->p = get_mp_ssh1(src);
if (!rsa_verify(key)) {
*error = "rsa_verify failed";
@@ -153,7 +136,7 @@ static int rsa_ssh1_load_main(FILE * fp, struct RSAKey *key, int pub_only,
ret = 1;
end:
- smemclr(buf, sizeof(buf)); /* burn the evidence */
+ strbuf_free(buf);
return ret;
}
@@ -1400,31 +1383,25 @@ static char *ssh2_pubkey_openssh_str_internal(const char *comment,
int pub_len)
{
const unsigned char *ssh2blob = (const unsigned char *)v_pub_blob;
- const char *alg;
- int alglen;
+ ptrlen alg;
char *buffer, *p;
int i;
- if (pub_len < 4) {
- alg = NULL;
- } else {
- alglen = GET_32BIT(ssh2blob);
- if (alglen > 0 && alglen < pub_len - 4) {
- alg = (const char *)ssh2blob + 4;
- } else {
- alg = NULL;
+ {
+ BinarySource src[1];
+ BinarySource_BARE_INIT(src, ssh2blob, pub_len);
+ alg = get_string(src);
+ if (get_err(src)) {
+ const char *replacement_str = "INVALID-ALGORITHM";
+ alg.ptr = replacement_str;
+ alg.len = strlen(replacement_str);
}
}
- if (!alg) {
- alg = "INVALID-ALGORITHM";
- alglen = strlen(alg);
- }
-
- buffer = snewn(alglen +
+ buffer = snewn(alg.len +
4 * ((pub_len+2) / 3) +
(comment ? strlen(comment) : 0) + 3, char);
- p = buffer + sprintf(buffer, "%.*s ", alglen, alg);
+ p = buffer + sprintf(buffer, "%.*s ", PTRLEN_PRINTF(alg));
i = 0;
while (i < pub_len) {
int n = (pub_len - i < 3 ? pub_len - i : 3);
@@ -1512,10 +1489,10 @@ char *ssh2_fingerprint_blob(const void *blob, int bloblen)
{
unsigned char digest[16];
char fingerprint_str[16*3];
- const char *algstr;
- int alglen;
+ ptrlen algname;
const ssh_keyalg *alg;
int i;
+ BinarySource src[1];
/*
* The fingerprint hash itself is always just the MD5 of the blob.
@@ -1527,21 +1504,17 @@ char *ssh2_fingerprint_blob(const void *blob, int bloblen)
/*
* Identify the key algorithm, if possible.
*/
- alglen = toint(GET_32BIT((const unsigned char *)blob));
- if (alglen > 0 && alglen < bloblen-4) {
- algstr = (const char *)blob + 4;
-
- /*
- * If we can actually identify the algorithm as one we know
- * about, get hold of the key's bit count too.
- */
- alg = find_pubkey_alg_len(make_ptrlen(algstr, alglen));
+ BinarySource_BARE_INIT(src, blob, bloblen);
+ algname = get_string(src);
+ if (!get_err(src)) {
+ alg = find_pubkey_alg_len(algname);
if (alg) {
int bits = alg->pubkey_bits(alg, blob, bloblen);
- return dupprintf("%.*s %d %s", alglen, algstr,
+ return dupprintf("%.*s %d %s", PTRLEN_PRINTF(algname),
bits, fingerprint_str);
} else {
- return dupprintf("%.*s %s", alglen, algstr, fingerprint_str);
+ return dupprintf("%.*s %s", PTRLEN_PRINTF(algname),
+ fingerprint_str);
}
} else {
/*
From 5be57af17365fc6ff3bf5c6a8bfd3334385b9428 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 29 May 2018 19:11:22 +0100
Subject: [PATCH 333/607] Rewrite packet parsing in sshshare.c using
BinarySource.
Another set of localised decoding routines get thrown away here. Also,
I've changed the APIs of a couple of helper functions in x11fwd.c to
take ptrlens in place of zero-terminated C strings, because that's the
format in which they come back from the decode, and it saves mallocing
a zero-terminated version of each one just to pass to those helpers.
---
ssh.h | 4 +-
sshshare.c | 259 +++++++++++++++++++----------------------------------
x11fwd.c | 12 +--
3 files changed, 100 insertions(+), 175 deletions(-)
diff --git a/ssh.h b/ssh.h
index 21ab1593..855bee89 100644
--- a/ssh.h
+++ b/ssh.h
@@ -657,8 +657,8 @@ char *platform_get_x_display(void);
*/
void x11_get_auth_from_authfile(struct X11Display *display,
const char *authfilename);
-int x11_identify_auth_proto(const char *proto);
-void *x11_dehexify(const char *hex, int *outlen);
+int x11_identify_auth_proto(ptrlen protoname);
+void *x11_dehexify(ptrlen hex, int *outlen);
Bignum copybn(Bignum b);
Bignum bn_power_2(int n);
diff --git a/sshshare.c b/sshshare.c
index 862d871a..5f0c01a1 100644
--- a/sshshare.c
+++ b/sshshare.c
@@ -728,30 +728,30 @@ static void send_packet_to_downstream(struct ssh_sharing_connstate *cs,
* If that happens, we just chop up the packet into pieces and
* send them as separate CHANNEL_DATA packets.
*/
- const char *upkt = (const char *)pkt;
+ BinarySource src[1];
+ unsigned channel;
+ ptrlen data;
- int len = toint(GET_32BIT(upkt + 4));
- upkt += 8; /* skip channel id + length field */
-
- if (len < 0 || len > pktlen - 8)
- len = pktlen - 8;
+ BinarySource_BARE_INIT(src, pkt, pktlen);
+ channel = get_uint32(src);
+ data = get_string(src);
do {
- int this_len = (len > chan->downstream_maxpkt ?
- chan->downstream_maxpkt : len);
+ int this_len = (data.len > chan->downstream_maxpkt ?
+ chan->downstream_maxpkt : data.len);
packet = strbuf_new();
put_uint32(packet, 0); /* placeholder for length field */
put_byte(packet, type);
- put_uint32(packet, chan->downstream_id);
+ put_uint32(packet, channel);
put_uint32(packet, this_len);
- put_data(packet, upkt, this_len);
- len -= this_len;
- upkt += this_len;
+ put_data(packet, data.ptr, this_len);
+ data.ptr = (const char *)data.ptr + this_len;
+ data.len -= this_len;
PUT_32BIT(packet->s, packet->len-4);
sk_write(cs->sock, packet->s, packet->len);
strbuf_free(packet);
- } while (len > 0);
+ } while (data.len > 0);
} else {
/*
* Just do the obvious thing.
@@ -924,44 +924,6 @@ static void share_closing(Plug plug, const char *error_msg, int error_code,
share_begin_cleanup(cs);
}
-static int getstring_inner(const void *vdata, int datalen,
- char **out, int *outlen)
-{
- const unsigned char *data = (const unsigned char *)vdata;
- int len;
-
- if (datalen < 4)
- return FALSE;
-
- len = toint(GET_32BIT(data));
- if (len < 0 || len > datalen - 4)
- return FALSE;
-
- if (outlen)
- *outlen = len + 4; /* total size including length field */
- if (out)
- *out = dupprintf("%.*s", len, (char *)data + 4);
- return TRUE;
-}
-
-static char *getstring(const void *data, int datalen)
-{
- char *ret;
- if (getstring_inner(data, datalen, &ret, NULL))
- return ret;
- else
- return NULL;
-}
-
-static int getstring_size(const void *data, int datalen)
-{
- int ret;
- if (getstring_inner(data, datalen, NULL, &ret))
- return ret;
- else
- return -1;
-}
-
/*
* Append a message to the end of an xchannel's queue.
*/
@@ -1011,9 +973,11 @@ void share_dead_xchannel_respond(struct ssh_sharing_connstate *cs,
* A CHANNEL_REQUEST is responded to by sending
* CHANNEL_FAILURE, if it has want_reply set.
*/
- int wantreplypos = getstring_size(msg->data, msg->datalen);
- if (wantreplypos > 0 && wantreplypos < msg->datalen &&
- msg->data[wantreplypos] != 0) {
+ BinarySource src[1];
+ BinarySource_BARE_INIT(src, msg->data, msg->datalen);
+ get_uint32(src); /* skip channel id */
+ get_string(src); /* skip request type */
+ if (get_bool(src)) {
strbuf *packet = strbuf_new();
put_uint32(packet, xc->server_id);
ssh_send_packet_from_downstream
@@ -1170,10 +1134,13 @@ void share_got_pkt_from_server(void *csv, int type,
{
struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)csv;
struct share_globreq *globreq;
- int id_pos;
+ size_t id_pos;
unsigned upstream_id, server_id;
struct share_channel *chan;
struct share_xchannel *xc;
+ BinarySource src[1];
+
+ BinarySource_BARE_INIT(src, pkt, pktlen);
switch (type) {
case SSH2_MSG_REQUEST_SUCCESS:
@@ -1207,9 +1174,9 @@ void share_got_pkt_from_server(void *csv, int type,
break;
case SSH2_MSG_CHANNEL_OPEN:
- id_pos = getstring_size(pkt, pktlen);
- assert(id_pos >= 0);
- server_id = GET_32BIT(pkt + id_pos);
+ get_string(src);
+ server_id = get_uint32(src);
+ assert(!get_err(src));
share_add_halfchannel(cs, server_id);
send_packet_to_downstream(cs, type, pkt, pktlen, NULL);
@@ -1230,13 +1197,13 @@ void share_got_pkt_from_server(void *csv, int type,
* first uint32 field in the packet. Substitute the downstream
* channel id for our one and pass the packet downstream.
*/
- assert(pktlen >= 4);
- upstream_id = GET_32BIT(pkt);
+ id_pos = src->pos;
+ upstream_id = get_uint32(src);
if ((chan = share_find_channel_by_upstream(cs, upstream_id)) != NULL) {
/*
* The normal case: this id refers to an open channel.
*/
- PUT_32BIT(pkt, chan->downstream_id);
+ PUT_32BIT(pkt + id_pos, chan->downstream_id);
send_packet_to_downstream(cs, type, pkt, pktlen, chan);
/*
@@ -1296,9 +1263,10 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
int type,
unsigned char *pkt, int pktlen)
{
- char *request_name;
+ ptrlen request_name;
struct share_forwarding *fwd;
- int id_pos;
+ size_t id_pos;
+ unsigned maxpkt;
unsigned old_id, new_id, server_id;
struct share_globreq *globreq;
struct share_channel *chan;
@@ -1306,6 +1274,11 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
struct share_xchannel *xc;
strbuf *packet;
char *err = NULL;
+ BinarySource src[1];
+ size_t wantreplypos;
+ int orig_wantreply;
+
+ BinarySource_BARE_INIT(src, pkt, pktlen);
switch (type) {
case SSH2_MSG_DISCONNECT:
@@ -1326,39 +1299,26 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* will probably require that too, and so we don't forward on
* any request we don't understand.
*/
- request_name = getstring(pkt, pktlen);
- if (request_name == NULL) {
- err = dupprintf("Truncated GLOBAL_REQUEST packet");
- goto confused;
- }
+ request_name = get_string(src);
+ wantreplypos = src->pos;
+ orig_wantreply = get_bool(src);
- if (!strcmp(request_name, "tcpip-forward")) {
- int wantreplypos, orig_wantreply, port, ret;
+ if (ptrlen_eq_string(request_name, "tcpip-forward")) {
+ ptrlen hostpl;
char *host;
-
- sfree(request_name);
+ int port, ret;
/*
* Pick the packet apart to find the want_reply field and
* the host/port we're going to ask to listen on.
*/
- wantreplypos = getstring_size(pkt, pktlen);
- if (wantreplypos < 0 || wantreplypos >= pktlen) {
+ hostpl = get_string(src);
+ port = toint(get_uint32(src));
+ if (get_err(src)) {
err = dupprintf("Truncated GLOBAL_REQUEST packet");
goto confused;
}
- orig_wantreply = pkt[wantreplypos];
- port = getstring_size(pkt + (wantreplypos + 1),
- pktlen - (wantreplypos + 1));
- port += (wantreplypos + 1);
- if (port < 0 || port > pktlen - 4) {
- err = dupprintf("Truncated GLOBAL_REQUEST packet");
- goto confused;
- }
- host = getstring(pkt + (wantreplypos + 1),
- pktlen - (wantreplypos + 1));
- assert(host != NULL);
- port = GET_32BIT(pkt + port);
+ host = mkstr(hostpl);
/*
* See if we can allocate space in ssh.c's tree of remote
@@ -1382,11 +1342,10 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* that we know whether this forwarding needs to be
* cleaned up if downstream goes away.
*/
- int old_wantreply = pkt[wantreplypos];
pkt[wantreplypos] = 1;
ssh_send_packet_from_downstream
(cs->parent->ssh, cs->id, type, pkt, pktlen,
- old_wantreply ? NULL : "upstream added want_reply flag");
+ orig_wantreply ? NULL : "upstream added want_reply flag");
fwd = share_add_forwarding(cs, host, port);
ssh_sharing_queue_global_request(cs->parent->ssh, cs);
@@ -1404,34 +1363,23 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
}
sfree(host);
- } else if (!strcmp(request_name, "cancel-tcpip-forward")) {
- int wantreplypos, orig_wantreply, port;
+ } else if (ptrlen_eq_string(request_name, "cancel-tcpip-forward")) {
+ ptrlen hostpl;
char *host;
+ int port;
struct share_forwarding *fwd;
- sfree(request_name);
-
/*
* Pick the packet apart to find the want_reply field and
* the host/port we're going to ask to listen on.
*/
- wantreplypos = getstring_size(pkt, pktlen);
- if (wantreplypos < 0 || wantreplypos >= pktlen) {
- err = dupprintf("Truncated GLOBAL_REQUEST packet");
- goto confused;
- }
- orig_wantreply = pkt[wantreplypos];
- port = getstring_size(pkt + (wantreplypos + 1),
- pktlen - (wantreplypos + 1));
- port += (wantreplypos + 1);
- if (port < 0 || port > pktlen - 4) {
+ hostpl = get_string(src);
+ port = toint(get_uint32(src));
+ if (get_err(src)) {
err = dupprintf("Truncated GLOBAL_REQUEST packet");
goto confused;
}
- host = getstring(pkt + (wantreplypos + 1),
- pktlen - (wantreplypos + 1));
- assert(host != NULL);
- port = GET_32BIT(pkt + port);
+ host = mkstr(hostpl);
/*
* Look up the existing forwarding with these details.
@@ -1449,11 +1397,10 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* that _we_ know whether the forwarding has been
* deleted even if downstream doesn't want to know.
*/
- int old_wantreply = pkt[wantreplypos];
pkt[wantreplypos] = 1;
ssh_send_packet_from_downstream
(cs->parent->ssh, cs->id, type, pkt, pktlen,
- old_wantreply ? NULL : "upstream added want_reply flag");
+ orig_wantreply ? NULL : "upstream added want_reply flag");
ssh_sharing_queue_global_request(cs->parent->ssh, cs);
}
@@ -1463,16 +1410,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* Request we don't understand. Manufacture a failure
* message if an answer was required.
*/
- int wantreplypos;
-
- sfree(request_name);
-
- wantreplypos = getstring_size(pkt, pktlen);
- if (wantreplypos < 0 || wantreplypos >= pktlen) {
- err = dupprintf("Truncated GLOBAL_REQUEST packet");
- goto confused;
- }
- if (pkt[wantreplypos])
+ if (orig_wantreply)
send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
"", 0, NULL);
}
@@ -1480,16 +1418,17 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
case SSH2_MSG_CHANNEL_OPEN:
/* Sender channel id comes after the channel type string */
- id_pos = getstring_size(pkt, pktlen);
- if (id_pos < 0 || id_pos > pktlen - 12) {
+ get_string(src);
+ id_pos = src->pos;
+ old_id = get_uint32(src);
+ new_id = ssh_alloc_sharing_channel(cs->parent->ssh, cs);
+ get_uint32(src); /* skip initial window size */
+ maxpkt = get_uint32(src);
+ if (get_err(src)) {
err = dupprintf("Truncated CHANNEL_OPEN packet");
goto confused;
}
-
- old_id = GET_32BIT(pkt + id_pos);
- new_id = ssh_alloc_sharing_channel(cs->parent->ssh, cs);
- share_add_channel(cs, old_id, new_id, 0, UNACKNOWLEDGED,
- GET_32BIT(pkt + id_pos + 8));
+ share_add_channel(cs, old_id, new_id, 0, UNACKNOWLEDGED, maxpkt);
PUT_32BIT(pkt + id_pos, new_id);
ssh_send_packet_from_downstream(cs->parent->ssh, cs->id,
type, pkt, pktlen, NULL);
@@ -1501,10 +1440,16 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
goto confused;
}
- id_pos = 4; /* sender channel id is 2nd uint32 field in packet */
- old_id = GET_32BIT(pkt + id_pos);
+ server_id = get_uint32(src);
+ id_pos = src->pos;
+ old_id = get_uint32(src);
+ get_uint32(src); /* skip initial window size */
+ maxpkt = get_uint32(src);
+ if (get_err(src)) {
+ err = dupprintf("Truncated CHANNEL_OPEN_CONFIRMATION packet");
+ goto confused;
+ }
- server_id = GET_32BIT(pkt);
/* This server id may refer to either a halfchannel or an xchannel. */
hc = NULL, xc = NULL; /* placate optimiser */
if ((hc = share_find_halfchannel(cs, server_id)) != NULL) {
@@ -1519,8 +1464,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
PUT_32BIT(pkt + id_pos, new_id);
- chan = share_add_channel(cs, old_id, new_id, server_id, OPEN,
- GET_32BIT(pkt + 12));
+ chan = share_add_channel(cs, old_id, new_id, server_id, OPEN, maxpkt);
if (hc) {
ssh_send_packet_from_downstream(cs->parent->ssh, cs->id,
@@ -1539,12 +1483,12 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
break;
case SSH2_MSG_CHANNEL_OPEN_FAILURE:
- if (pktlen < 4) {
+ server_id = get_uint32(src);
+ if (get_err(src)) {
err = dupprintf("Truncated CHANNEL_OPEN_FAILURE packet");
goto confused;
}
- server_id = GET_32BIT(pkt);
/* This server id may refer to either a halfchannel or an xchannel. */
if ((hc = share_find_halfchannel(cs, server_id)) != NULL) {
ssh_send_packet_from_downstream(cs->parent->ssh, cs->id,
@@ -1570,8 +1514,11 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
case SSH2_MSG_CHANNEL_FAILURE:
case SSH2_MSG_IGNORE:
case SSH2_MSG_DEBUG:
- if (type == SSH2_MSG_CHANNEL_REQUEST &&
- (request_name = getstring(pkt + 4, pktlen - 4)) != NULL) {
+ server_id = get_uint32(src);
+
+ if (type == SSH2_MSG_CHANNEL_REQUEST) {
+ request_name = get_string(src);
+
/*
* Agent forwarding requests from downstream are treated
* specially. Because OpenSSHD doesn't let us enable agent
@@ -1597,11 +1544,8 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* subsequent CHANNEL_OPENs still can't be associated with
* a parent session channel.)
*/
- if (!strcmp(request_name, "auth-agent-req@openssh.com") &&
+ if (ptrlen_eq_string(request_name, "auth-agent-req@openssh.com") &&
!ssh_agent_forwarding_permitted(cs->parent->ssh)) {
- unsigned server_id = GET_32BIT(pkt);
-
- sfree(request_name);
chan = share_find_channel_by_server(cs, server_id);
if (chan) {
@@ -1630,14 +1574,10 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* whether it's one to handle locally or one to pass on to
* a downstream, and if the latter, which one.
*/
- if (!strcmp(request_name, "x11-req")) {
- unsigned server_id = GET_32BIT(pkt);
+ if (ptrlen_eq_string(request_name, "x11-req")) {
int want_reply, single_connection, screen;
- char *auth_proto_str, *auth_data;
+ ptrlen auth_data;
int auth_proto;
- int pos;
-
- sfree(request_name);
chan = share_find_channel_by_server(cs, server_id);
if (!chan) {
@@ -1652,26 +1592,16 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* Pick apart the whole message to find the downstream
* auth details.
*/
- /* we have already seen: 4 bytes channel id, 4+7 request name */
- if (pktlen < 17) {
- err = dupprintf("Truncated CHANNEL_REQUEST(\"x11\") packet");
+ want_reply = get_bool(src);
+ single_connection = get_bool(src);
+ auth_proto = x11_identify_auth_proto(get_string(src));
+ auth_data = get_string(src);
+ screen = toint(get_uint32(src));
+ if (get_err(src)) {
+ err = dupprintf("Truncated CHANNEL_REQUEST(\"x11-req\")"
+ " packet");
goto confused;
}
- want_reply = pkt[15] != 0;
- single_connection = pkt[16] != 0;
- auth_proto_str = getstring(pkt+17, pktlen-17);
- auth_proto = x11_identify_auth_proto(auth_proto_str);
- sfree(auth_proto_str);
- pos = 17 + getstring_size(pkt+17, pktlen-17);
- auth_data = getstring(pkt+pos, pktlen-pos);
- pos += getstring_size(pkt+pos, pktlen-pos);
-
- if (pktlen < pos+4) {
- err = dupprintf("Truncated CHANNEL_REQUEST(\"x11\") packet");
- sfree(auth_data);
- goto confused;
- }
- screen = GET_32BIT(pkt+pos);
if (auth_proto < 0) {
/* Reject due to not understanding downstream's
@@ -1682,14 +1612,12 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
cs, SSH2_MSG_CHANNEL_FAILURE,
packet->s, packet->len, NULL);
strbuf_free(packet);
- sfree(auth_data);
break;
}
chan->x11_auth_proto = auth_proto;
chan->x11_auth_data = x11_dehexify(auth_data,
&chan->x11_auth_datalen);
- sfree(auth_data);
chan->x11_auth_upstream =
ssh_sharing_add_x11_display(cs->parent->ssh, auth_proto,
cs, chan);
@@ -1715,14 +1643,11 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
break;
}
-
- sfree(request_name);
}
ssh_send_packet_from_downstream(cs->parent->ssh, cs->id,
type, pkt, pktlen, NULL);
if (type == SSH2_MSG_CHANNEL_CLOSE && pktlen >= 4) {
- server_id = GET_32BIT(pkt);
chan = share_find_channel_by_server(cs, server_id);
if (chan) {
if (chan->state == RCVD_CLOSE) {
diff --git a/x11fwd.c b/x11fwd.c
index eee73095..f59f9e2e 100644
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -974,29 +974,29 @@ void x11_send_eof(struct X11Connection *xconn)
* representations of an X11 auth protocol name + hex cookie into our
* usual integer protocol id and binary auth data.
*/
-int x11_identify_auth_proto(const char *protoname)
+int x11_identify_auth_proto(ptrlen protoname)
{
int protocol;
for (protocol = 1; protocol < lenof(x11_authnames); protocol++)
- if (!strcmp(protoname, x11_authnames[protocol]))
+ if (ptrlen_eq_string(protoname, x11_authnames[protocol]))
return protocol;
return -1;
}
-void *x11_dehexify(const char *hex, int *outlen)
+void *x11_dehexify(ptrlen hexpl, int *outlen)
{
int len, i;
unsigned char *ret;
- len = strlen(hex) / 2;
+ len = hexpl.len / 2;
ret = snewn(len, unsigned char);
for (i = 0; i < len; i++) {
char bytestr[3];
unsigned val = 0;
- bytestr[0] = hex[2*i];
- bytestr[1] = hex[2*i+1];
+ bytestr[0] = ((const char *)hexpl.ptr)[2*i];
+ bytestr[1] = ((const char *)hexpl.ptr)[2*i+1];
bytestr[2] = '\0';
sscanf(bytestr, "%x", &val);
ret[i] = val;
From ae3edcdfc0b6e71377aee57070f43faacd6f6456 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 31 May 2018 18:40:51 +0100
Subject: [PATCH 334/607] Clean up ssh_keyalg APIs and implementations.
Quite a few of the function pointers in the ssh_keyalg vtable now take
ptrlen arguments in place of separate pointer and length pairs.
Meanwhile, the various key types' implementations of those functions
now work by initialising a BinarySource with the input ptrlen and
using the new decode functions to walk along it.
One exception is the openssh_createkey method which reads a private
key in the wire format used by OpenSSH's SSH-2 agent protocol, which
has to consume a prefix of a larger data stream, and tell the caller
how much of that data was the private key. That function now takes an
actual BinarySource, and passes that directly to the decode functions,
so that on return the caller finds that the BinarySource's read
pointer has been advanced exactly past the private key.
This let me throw away _several_ reimplementations of mpint-reading
functions, one in each of sshrsa, sshdss.c and sshecc.c. Worse still,
they didn't all have exactly the SSH-2 semantics, because the thing in
sshrsa.c whose name suggested it was an mpint-reading function
actually tolerated the wrong number of leading zero bytes, which it
had to be able to do to cope with the "ssh-rsa" signature format which
contains a thing that isn't quite an SSH-2 mpint. Now that deviation
is clearly commented!
---
cmdgen.c | 5 +-
import.c | 18 +++---
pageant.c | 9 +--
ssh.c | 18 +++---
ssh.h | 16 ++---
sshdss.c | 171 ++++++++++++++------------------------------------
sshecc.c | 183 ++++++++++++++++++------------------------------------
sshpubk.c | 7 ++-
sshrsa.c | 137 +++++++++++++++-------------------------
9 files changed, 189 insertions(+), 375 deletions(-)
diff --git a/cmdgen.c b/cmdgen.c
index 160169a7..b4fcd567 100644
--- a/cmdgen.c
+++ b/cmdgen.c
@@ -857,9 +857,8 @@ int main(int argc, char **argv)
&origcomment, &error)) {
ssh2algf = find_pubkey_alg(ssh2alg);
if (ssh2algf)
- bits = ssh2algf->pubkey_bits(ssh2algf,
- ssh2blob->s,
- ssh2blob->len);
+ bits = ssh2algf->pubkey_bits(
+ ssh2algf, make_ptrlen(ssh2blob->s, ssh2blob->len));
else
bits = -1;
} else {
diff --git a/import.c b/import.c
index bc54108a..0b2d271c 100644
--- a/import.c
+++ b/import.c
@@ -670,8 +670,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
put_mp_ssh2_from_string(blob, privkey.data.ptr, privkey.data.len);
retkey->data = retkey->alg->createkey(
- retkey->alg, blob->u, publen,
- blob->u + publen, blob->len - publen);
+ retkey->alg, make_ptrlen(blob->u, publen),
+ make_ptrlen(blob->u + publen, blob->len - publen));
if (!retkey->data) {
sfree(retkey);
@@ -742,7 +742,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
retkey = snew(struct ssh2_userkey);
retkey->alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss);
retkey->data = retkey->alg->createkey(
- retkey->alg, blob->u, privptr, blob->u+privptr, blob->len-privptr);
+ retkey->alg, make_ptrlen(blob->u, privptr),
+ make_ptrlen(blob->u+privptr, blob->len-privptr));
if (!retkey->data) {
sfree(retkey);
@@ -1466,13 +1467,13 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
thiskey.len = (const char *)get_ptr(src) - (const char *)thiskey.ptr;
if (key_index == key->key_wanted) {
- const unsigned char *blobptr = thiskey.ptr;
- int bloblen = thiskey.len;
+ BinarySource src[1];
+ BinarySource_BARE_INIT(src, thiskey.ptr, thiskey.len);
retkey = snew(struct ssh2_userkey);
retkey->comment = NULL;
retkey->alg = alg;
- retkey->data = alg->openssh_createkey(alg, &blobptr, &bloblen);
+ retkey->data = alg->openssh_createkey(alg, src);
if (!retkey->data) {
errmsg = "unable to create key data structure";
goto error;
@@ -2173,8 +2174,9 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
retkey = snew(struct ssh2_userkey);
retkey->alg = alg;
- retkey->data = alg->createkey(alg, blob->u, publen,
- blob->u + publen, blob->len - publen);
+ retkey->data = alg->createkey(
+ alg, make_ptrlen(blob->u, publen),
+ make_ptrlen(blob->u + publen, blob->len - publen));
if (!retkey->data) {
sfree(retkey);
errmsg = "unable to create key data structure";
diff --git a/pageant.c b/pageant.c
index 25d0b9ab..71256ff3 100644
--- a/pageant.c
+++ b/pageant.c
@@ -430,14 +430,7 @@ void pageant_handle_msg(BinarySink *bs,
goto add2_cleanup;
}
- {
- const unsigned char *p = get_ptr(msg);
- int len = get_avail(msg);
- key->data = key->alg->openssh_createkey(key->alg, &p, &len);
- assert(len >= 0);
- assert(len < get_avail(msg));
- msg->pos += get_avail(msg) - len;
- }
+ key->data = key->alg->openssh_createkey(key->alg, msg);
if (!key->data) {
pageant_failure_msg(bs, "key setup failed", logctx, logfn);
diff --git a/ssh.c b/ssh.c
index 518372ca..012d7062 100644
--- a/ssh.c
+++ b/ssh.c
@@ -7192,8 +7192,7 @@ static void do_ssh2_transport(void *vctx)
}
set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
s->hostkeydata = get_string(pktin);
- s->hkey = ssh->hostkey->newkey(ssh->hostkey,
- s->hostkeydata.ptr, s->hostkeydata.len);
+ s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata);
s->f = get_mp_ssh2(pktin);
s->sigdata = get_string(pktin);
if (get_err(pktin)) {
@@ -7264,8 +7263,7 @@ static void do_ssh2_transport(void *vctx)
s->hostkeydata = get_string(pktin);
put_stringpl(ssh->exhash_bs, s->hostkeydata);
- s->hkey = ssh->hostkey->newkey(ssh->hostkey,
- s->hostkeydata.ptr, s->hostkeydata.len);
+ s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata);
{
strbuf *pubpoint = strbuf_new();
@@ -7469,8 +7467,7 @@ static void do_ssh2_transport(void *vctx)
s->hostkeydata = get_string(pktin);
if (ssh->hostkey) {
s->hkey = ssh->hostkey->newkey(ssh->hostkey,
- s->hostkeydata.ptr,
- s->hostkeydata.len);
+ s->hostkeydata);
put_string(ssh->exhash_bs,
s->hostkeydata.ptr, s->hostkeydata.len);
}
@@ -7564,8 +7561,7 @@ static void do_ssh2_transport(void *vctx)
s->hostkeydata = get_string(pktin);
put_stringpl(ssh->exhash_bs, s->hostkeydata);
- s->hkey = ssh->hostkey->newkey(ssh->hostkey,
- s->hostkeydata.ptr, s->hostkeydata.len);
+ s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata);
rsakeydata = get_string(pktin);
@@ -7704,9 +7700,9 @@ static void do_ssh2_transport(void *vctx)
crStopV;
}
- if (!ssh->hostkey->verifysig(s->hkey, s->sigdata.ptr, s->sigdata.len,
- s->exchange_hash,
- ssh->kex->hash->hlen)) {
+ if (!ssh->hostkey->verifysig(
+ s->hkey, s->sigdata,
+ make_ptrlen(s->exchange_hash, ssh->kex->hash->hlen))) {
#ifndef FUZZING
bombout(("Server's host key did not match the signature "
"supplied"));
diff --git a/ssh.h b/ssh.h
index 855bee89..655d72d2 100644
--- a/ssh.h
+++ b/ssh.h
@@ -394,17 +394,13 @@ struct ssh_kexes {
};
struct ssh_keyalg {
- ssh_key *(*newkey) (const ssh_keyalg *self,
- const void *data, int len);
+ ssh_key *(*newkey) (const ssh_keyalg *self, ptrlen data);
void (*freekey) (ssh_key *key);
char *(*fmtkey) (ssh_key *key);
void (*public_blob)(ssh_key *key, BinarySink *);
void (*private_blob)(ssh_key *key, BinarySink *);
- ssh_key *(*createkey) (const ssh_keyalg *self,
- const void *pub_blob, int pub_len,
- const void *priv_blob, int priv_len);
- ssh_key *(*openssh_createkey) (const ssh_keyalg *self,
- const unsigned char **blob, int *len);
+ ssh_key *(*createkey) (const ssh_keyalg *self, ptrlen pub, ptrlen priv);
+ ssh_key *(*openssh_createkey) (const ssh_keyalg *self, BinarySource *);
void (*openssh_fmtkey) (ssh_key *key, BinarySink *);
/* OpenSSH private key blobs, as created by openssh_fmtkey and
* consumed by openssh_createkey, always (at least so far...) take
@@ -415,10 +411,8 @@ struct ssh_keyalg {
* skip over the right number to find the next key in the file.
* openssh_private_npieces gives that information. */
int openssh_private_npieces;
- int (*pubkey_bits) (const ssh_keyalg *self,
- const void *blob, int len);
- int (*verifysig) (ssh_key *key, const void *sig, int siglen,
- const void *data, int datalen);
+ int (*pubkey_bits) (const ssh_keyalg *self, ptrlen blob);
+ int (*verifysig) (ssh_key *key, ptrlen sig, ptrlen data);
void (*sign) (ssh_key *key, const void *data, int datalen, BinarySink *);
const char *name;
const char *keytype; /* for host key cache */
diff --git a/sshdss.c b/sshdss.c
index 262d3e39..3c9589fb 100644
--- a/sshdss.c
+++ b/sshdss.c
@@ -9,86 +9,25 @@
#include "ssh.h"
#include "misc.h"
-static void getstring(const char **data, int *datalen,
- const char **p, int *length)
-{
- *p = NULL;
- if (*datalen < 4)
- return;
- *length = toint(GET_32BIT(*data));
- if (*length < 0)
- return;
- *datalen -= 4;
- *data += 4;
- if (*datalen < *length)
- return;
- *p = *data;
- *data += *length;
- *datalen -= *length;
-}
-static Bignum getmp(const char **data, int *datalen)
-{
- const char *p;
- int length;
- Bignum b;
-
- getstring(data, datalen, &p, &length);
- if (!p)
- return NULL;
- if (p[0] & 0x80)
- return NULL; /* negative mp */
- b = bignum_from_bytes(p, length);
- return b;
-}
-
-static Bignum get160(const char **data, int *datalen)
-{
- Bignum b;
-
- if (*datalen < 20)
- return NULL;
-
- b = bignum_from_bytes(*data, 20);
- *data += 20;
- *datalen -= 20;
-
- return b;
-}
-
static void dss_freekey(ssh_key *key); /* forward reference */
-static ssh_key *dss_newkey(const ssh_keyalg *self,
- const void *vdata, int len)
+static ssh_key *dss_newkey(const ssh_keyalg *self, ptrlen data)
{
- const char *data = (const char *)vdata;
- const char *p;
- int slen;
+ BinarySource src[1];
struct dss_key *dss;
- dss = snew(struct dss_key);
- getstring(&data, &len, &p, &slen);
-
-#ifdef DEBUG_DSS
- {
- int i;
- printf("key:");
- for (i = 0; i < len; i++)
- printf(" %02x", (unsigned char) (data[i]));
- printf("\n");
- }
-#endif
-
- if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) {
- sfree(dss);
+ BinarySource_BARE_INIT(src, data.ptr, data.len);
+ if (!ptrlen_eq_string(get_string(src), "ssh-dss"))
return NULL;
- }
- dss->p = getmp(&data, &len);
- dss->q = getmp(&data, &len);
- dss->g = getmp(&data, &len);
- dss->y = getmp(&data, &len);
+
+ dss = snew(struct dss_key);
+ dss->p = get_mp_ssh2(src);
+ dss->q = get_mp_ssh2(src);
+ dss->g = get_mp_ssh2(src);
+ dss->y = get_mp_ssh2(src);
dss->x = NULL;
- if (!dss->p || !dss->q || !dss->g || !dss->y ||
+ if (get_err(src) ||
!bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) {
/* Invalid key. */
dss_freekey(&dss->sshk);
@@ -164,29 +103,19 @@ static char *dss_fmtkey(ssh_key *key)
return p;
}
-static int dss_verifysig(ssh_key *key, const void *vsig, int siglen,
- const void *data, int datalen)
+static int dss_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
{
struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
- const char *sig = (const char *)vsig;
- const char *p;
- int slen;
- char hash[20];
+ BinarySource src[1];
+ unsigned char hash[20];
Bignum r, s, w, gu1p, yu2p, gu1yu2p, u1, u2, sha, v;
int ret;
if (!dss->p)
return 0;
-#ifdef DEBUG_DSS
- {
- int i;
- printf("sig:");
- for (i = 0; i < siglen; i++)
- printf(" %02x", (unsigned char) (sig[i]));
- printf("\n");
- }
-#endif
+ BinarySource_BARE_INIT(src, sig.ptr, sig.len);
+
/*
* Commercial SSH (2.0.13) and OpenSSH disagree over the format
* of a DSA signature. OpenSSH is in line with RFC 4253:
@@ -198,15 +127,18 @@ static int dss_verifysig(ssh_key *key, const void *vsig, int siglen,
* the length: length 40 means the commercial-SSH bug, anything
* else is assumed to be RFC-compliant.
*/
- if (siglen != 40) { /* bug not present; read admin fields */
- getstring(&sig, &siglen, &p, &slen);
- if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) {
- return 0;
- }
- sig += 4, siglen -= 4; /* skip yet another length field */
+ if (sig.len != 40) { /* bug not present; read admin fields */
+ ptrlen type = get_string(src);
+ sig = get_string(src);
+
+ if (get_err(src) || !ptrlen_eq_string(type, "ssh-dss") ||
+ sig.len != 40)
+ return 0;
}
- r = get160(&sig, &siglen);
- s = get160(&sig, &siglen);
+
+ /* Now we're sitting on a 40-byte string for sure. */
+ r = bignum_from_bytes(sig.ptr, 20);
+ s = bignum_from_bytes((const char *)sig.ptr + 20, 20);
if (!r || !s) {
if (r)
freebn(r);
@@ -234,10 +166,8 @@ static int dss_verifysig(ssh_key *key, const void *vsig, int siglen,
/*
* Step 2. u1 <- SHA(message) * w mod q.
*/
- SHA_Simple(data, datalen, (unsigned char *)hash);
- p = hash;
- slen = 20;
- sha = get160(&p, &slen);
+ SHA_Simple(data.ptr, data.len, hash);
+ sha = bignum_from_bytes(hash, 20);
u1 = modmul(sha, w, dss->q);
/*
@@ -291,26 +221,24 @@ static void dss_private_blob(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, dss->x);
}
-static ssh_key *dss_createkey(const ssh_keyalg *self,
- const void *pub_blob, int pub_len,
- const void *priv_blob, int priv_len)
+static ssh_key *dss_createkey(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
{
+ BinarySource src[1];
ssh_key *sshk;
struct dss_key *dss;
- const char *pb = (const char *) priv_blob;
- const char *hash;
- int hashlen;
+ ptrlen hash;
SHA_State s;
unsigned char digest[20];
Bignum ytest;
- sshk = dss_newkey(self, pub_blob, pub_len);
+ sshk = dss_newkey(self, pub);
if (!sshk)
return NULL;
dss = FROMFIELD(sshk, struct dss_key, sshk);
- dss->x = getmp(&pb, &priv_len);
- if (!dss->x) {
+ BinarySource_BARE_INIT(src, priv.ptr, priv.len);
+ dss->x = get_mp_ssh2(src);
+ if (get_err(src)) {
dss_freekey(&dss->sshk);
return NULL;
}
@@ -318,15 +246,14 @@ static ssh_key *dss_createkey(const ssh_keyalg *self,
/*
* Check the obsolete hash in the old DSS key format.
*/
- hashlen = -1;
- getstring(&pb, &priv_len, &hash, &hashlen);
- if (hashlen == 20) {
+ hash = get_string(src);
+ if (hash.len == 20) {
SHA_Init(&s);
put_mp_ssh2(&s, dss->p);
put_mp_ssh2(&s, dss->q);
put_mp_ssh2(&s, dss->g);
SHA_Final(&s, digest);
- if (0 != memcmp(hash, digest, 20)) {
+ if (0 != memcmp(hash.ptr, digest, 20)) {
dss_freekey(&dss->sshk);
return NULL;
}
@@ -347,20 +274,19 @@ static ssh_key *dss_createkey(const ssh_keyalg *self,
}
static ssh_key *dss_openssh_createkey(const ssh_keyalg *self,
- const unsigned char **blob, int *len)
+ BinarySource *src)
{
- const char **b = (const char **) blob;
struct dss_key *dss;
dss = snew(struct dss_key);
- dss->p = getmp(b, len);
- dss->q = getmp(b, len);
- dss->g = getmp(b, len);
- dss->y = getmp(b, len);
- dss->x = getmp(b, len);
+ dss->p = get_mp_ssh2(src);
+ dss->q = get_mp_ssh2(src);
+ dss->g = get_mp_ssh2(src);
+ dss->y = get_mp_ssh2(src);
+ dss->x = get_mp_ssh2(src);
- if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x ||
+ if (get_err(src) ||
!bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) {
/* Invalid key. */
dss_freekey(&dss->sshk);
@@ -381,14 +307,13 @@ static void dss_openssh_fmtkey(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, dss->x);
}
-static int dss_pubkey_bits(const ssh_keyalg *self,
- const void *blob, int len)
+static int dss_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
{
ssh_key *sshk;
struct dss_key *dss;
int ret;
- sshk = dss_newkey(self, blob, len);
+ sshk = dss_newkey(self, pub);
if (!sshk)
return -1;
diff --git a/sshecc.c b/sshecc.c
index 57bd3595..0d97395a 100644
--- a/sshecc.c
+++ b/sshecc.c
@@ -1590,47 +1590,12 @@ static void _ecdsa_sign(const Bignum privateKey, const struct ec_curve *curve,
* Misc functions
*/
-static void getstring(const char **data, int *datalen,
- const char **p, int *length)
+static Bignum BinarySource_get_mp_le(BinarySource *src)
{
- *p = NULL;
- if (*datalen < 4)
- return;
- *length = toint(GET_32BIT(*data));
- if (*length < 0)
- return;
- *datalen -= 4;
- *data += 4;
- if (*datalen < *length)
- return;
- *p = *data;
- *data += *length;
- *datalen -= *length;
-}
-
-static Bignum getmp(const char **data, int *datalen)
-{
- const char *p;
- int length;
-
- getstring(data, datalen, &p, &length);
- if (!p)
- return NULL;
- if (p[0] & 0x80)
- return NULL; /* negative mp */
- return bignum_from_bytes(p, length);
-}
-
-static Bignum getmp_le(const char **data, int *datalen)
-{
- const char *p;
- int length;
-
- getstring(data, datalen, &p, &length);
- if (!p)
- return NULL;
- return bignum_from_bytes_le(p, length);
+ ptrlen mp_str = get_string(src);
+ return bignum_from_bytes_le(mp_str.ptr, mp_str.len);
}
+#define get_mp_le(src) BinarySource_get_mp_le(BinarySource_UPCAST(src))
static int decodepoint_ed(const char *p, int length, struct ec_point *point)
{
@@ -1709,15 +1674,13 @@ static int decodepoint(const char *p, int length, struct ec_point *point)
return 1;
}
-static int getmppoint(const char **data, int *datalen, struct ec_point *point)
+static int BinarySource_get_point(BinarySource *src, struct ec_point *point)
{
- const char *p;
- int length;
-
- getstring(data, datalen, &p, &length);
- if (!p) return 0;
- return decodepoint(p, length, point);
+ ptrlen str = get_string(src);
+ if (get_err(src)) return 0;
+ return decodepoint(str.ptr, str.len, point);
}
+#define get_point(src, pt) BinarySource_get_point(BinarySource_UPCAST(src), pt)
/* ----------------------------------------------------------------------
* Exposed ECDSA interface
@@ -1750,30 +1713,24 @@ static void ecdsa_freekey(ssh_key *key)
sfree(ec);
}
-static ssh_key *ecdsa_newkey(const ssh_keyalg *self,
- const void *vdata, int len)
+static ssh_key *ecdsa_newkey(const ssh_keyalg *self, ptrlen data)
{
const struct ecsign_extra *extra =
(const struct ecsign_extra *)self->extra;
- const char *data = (const char *)vdata;
- const char *p;
- int slen;
+ BinarySource src[1];
struct ec_key *ec;
struct ec_curve *curve;
- getstring(&data, &len, &p, &slen);
+ BinarySource_BARE_INIT(src, data.ptr, data.len);
+ get_string(src);
- if (!p) {
- return NULL;
- }
curve = extra->curve();
assert(curve->type == EC_WEIERSTRASS || curve->type == EC_EDWARDS);
/* Curve name is duplicated for Weierstrass form */
if (curve->type == EC_WEIERSTRASS) {
- getstring(&data, &len, &p, &slen);
- if (!p) return NULL;
- if (!match_ssh_id(slen, p, curve->name)) return NULL;
+ if (!ptrlen_eq_string(get_string(src), curve->name))
+ return NULL;
}
ec = snew(struct ec_key);
@@ -1785,7 +1742,7 @@ static ssh_key *ecdsa_newkey(const ssh_keyalg *self,
ec->publicKey.y = NULL;
ec->publicKey.z = NULL;
ec->privateKey = NULL;
- if (!getmppoint(&data, &len, &ec->publicKey)) {
+ if (!get_point(src, &ec->publicKey)) {
ecdsa_freekey(&ec->sshk);
return NULL;
}
@@ -1908,19 +1865,19 @@ static void ecdsa_private_blob(ssh_key *key, BinarySink *bs)
}
static ssh_key *ecdsa_createkey(const ssh_keyalg *self,
- const void *pub_blob, int pub_len,
- const void *priv_blob, int priv_len)
+ ptrlen pub, ptrlen priv)
{
+ BinarySource src[1];
ssh_key *sshk;
struct ec_key *ec;
struct ec_point *publicKey;
- const char *pb = (const char *) priv_blob;
- sshk = ecdsa_newkey(self, pub_blob, pub_len);
+ sshk = ecdsa_newkey(self, pub);
if (!sshk)
return NULL;
ec = FROMFIELD(sshk, struct ec_key, sshk);
+ BinarySource_BARE_INIT(src, priv.ptr, priv.len);
if (ec->publicKey.curve->type != EC_WEIERSTRASS
&& ec->publicKey.curve->type != EC_EDWARDS) {
@@ -1929,9 +1886,9 @@ static ssh_key *ecdsa_createkey(const ssh_keyalg *self,
}
if (ec->publicKey.curve->type == EC_EDWARDS) {
- ec->privateKey = getmp_le(&pb, &priv_len);
+ ec->privateKey = get_mp_le(src);
} else {
- ec->privateKey = getmp(&pb, &priv_len);
+ ec->privateKey = get_mp_ssh2(src);
}
if (!ec->privateKey) {
ecdsa_freekey(&ec->sshk);
@@ -1954,18 +1911,16 @@ static ssh_key *ecdsa_createkey(const ssh_keyalg *self,
}
static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self,
- const unsigned char **blob, int *len)
+ BinarySource *src)
{
struct ec_key *ec;
struct ec_point *publicKey;
- const char *p, *q;
- int plen, qlen;
+ ptrlen p, q;
- getstring((const char**)blob, len, &p, &plen);
- if (!p)
- {
+ p = get_string(src);
+ q = get_string(src);
+ if (get_err(src) || p.len != 32 || q.len != 64)
return NULL;
- }
ec = snew(struct ec_key);
@@ -1977,19 +1932,13 @@ static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self,
ec->publicKey.z = NULL;
ec->publicKey.y = NULL;
- if (!decodepoint_ed(p, plen, &ec->publicKey))
+ if (!decodepoint_ed(p.ptr, p.len, &ec->publicKey))
{
ecdsa_freekey(&ec->sshk);
return NULL;
}
- getstring((const char**)blob, len, &q, &qlen);
- if (!q || qlen != 64) {
- ecdsa_freekey(&ec->sshk);
- return NULL;
- }
-
- ec->privateKey = bignum_from_bytes_le(q, 32);
+ ec->privateKey = bignum_from_bytes_le(q.ptr, 32);
/* Check that private key generates public key */
publicKey = ec_public(ec->privateKey, ec->publicKey.curve);
@@ -2009,7 +1958,7 @@ static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self,
* correct as well, otherwise the key we think we've imported
* won't behave identically to the way OpenSSH would have treated
* it. */
- if (plen != 32 || 0 != memcmp(q + 32, p, 32)) {
+ if (0 != memcmp((const char *)q.ptr + 32, p.ptr, 32)) {
ecdsa_freekey(&ec->sshk);
return NULL;
}
@@ -2054,22 +2003,16 @@ static void ed25519_openssh_fmtkey(ssh_key *key, BinarySink *bs)
}
static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self,
- const unsigned char **blob, int *len)
+ BinarySource *src)
{
const struct ecsign_extra *extra =
(const struct ecsign_extra *)self->extra;
- const char **b = (const char **) blob;
- const char *p;
- int slen;
struct ec_key *ec;
struct ec_curve *curve;
struct ec_point *publicKey;
- getstring(b, len, &p, &slen);
+ get_string(src);
- if (!p) {
- return NULL;
- }
curve = extra->curve();
assert(curve->type == EC_WEIERSTRASS);
@@ -2081,7 +2024,7 @@ static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self,
ec->publicKey.x = NULL;
ec->publicKey.y = NULL;
ec->publicKey.z = NULL;
- if (!getmppoint(b, len, &ec->publicKey)) {
+ if (!get_point(src, &ec->publicKey)) {
ecdsa_freekey(&ec->sshk);
return NULL;
}
@@ -2095,7 +2038,7 @@ static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self,
return NULL;
}
- ec->privateKey = getmp(b, len);
+ ec->privateKey = get_mp_ssh2(src);
if (ec->privateKey == NULL)
{
ecdsa_freekey(&ec->sshk);
@@ -2147,14 +2090,13 @@ static void ecdsa_openssh_fmtkey(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, ec->privateKey);
}
-static int ecdsa_pubkey_bits(const ssh_keyalg *self,
- const void *blob, int len)
+static int ecdsa_pubkey_bits(const ssh_keyalg *self, ptrlen blob)
{
ssh_key *sshk;
struct ec_key *ec;
int ret;
- sshk = ecdsa_newkey(self, blob, len);
+ sshk = ecdsa_newkey(self, blob);
if (!sshk)
return -1;
@@ -2165,39 +2107,35 @@ static int ecdsa_pubkey_bits(const ssh_keyalg *self,
return ret;
}
-static int ecdsa_verifysig(ssh_key *key, const void *vsig, int siglen,
- const void *vdata, int datalen)
+static int ecdsa_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
{
struct ec_key *ec = FROMFIELD(key, struct ec_key, sshk);
- const char *sig = (const char *)vsig;
- const char *data = (const char *)vdata;
const struct ecsign_extra *extra =
(const struct ecsign_extra *)ec->signalg->extra;
- const char *p;
- int slen;
- int digestLen;
+ BinarySource src[1];
+ ptrlen sigstr;
int ret;
if (!ec->publicKey.x || !ec->publicKey.y || !ec->publicKey.curve)
return 0;
+ BinarySource_BARE_INIT(src, sig.ptr, sig.len);
+
/* Check the signature starts with the algorithm name */
- getstring(&sig, &siglen, &p, &slen);
- if (!p) {
+ if (!ptrlen_eq_string(get_string(src), ec->signalg->name))
return 0;
- }
- if (!match_ssh_id(slen, p, ec->signalg->name)) {
+
+ sigstr = get_string(src);
+ if (get_err(src))
return 0;
- }
- getstring(&sig, &siglen, &p, &slen);
- if (!p) return 0;
if (ec->publicKey.curve->type == EC_EDWARDS) {
struct ec_point *r;
+ int pointlen = ec->publicKey.curve->fieldBits / 8;
Bignum s, h;
/* Check that the signature is two times the length of a point */
- if (slen != (ec->publicKey.curve->fieldBits / 8) * 2) {
+ if (sigstr.len != pointlen * 2) {
return 0;
}
@@ -2211,25 +2149,23 @@ static int ecdsa_verifysig(ssh_key *key, const void *vsig, int siglen,
if (!r) {
return 0;
}
- if (!decodepoint(p, ec->publicKey.curve->fieldBits / 8, r)) {
+ if (!decodepoint(sigstr.ptr, pointlen, r)) {
ec_point_free(r);
return 0;
}
- s = bignum_from_bytes_le((unsigned char*)p + (ec->publicKey.curve->fieldBits / 8),
- ec->publicKey.curve->fieldBits / 8);
+ s = bignum_from_bytes_le(
+ (const char *)sigstr.ptr + pointlen, pointlen);
/* Get the hash of the encoded value of R + encoded value of pk + message */
{
- int i, pointlen;
+ int i;
unsigned char digest[512 / 8];
SHA512_State hs;
SHA512_Init(&hs);
- pointlen = ec->publicKey.curve->fieldBits / 8;
-
/* Add encoded r (no need to encode it again, it was in
* the signature) */
- put_data(&hs, p, pointlen);
+ put_data(&hs, sigstr.ptr, pointlen);
/* Encode pk and add it */
for (i = 0; i < pointlen - 1; ++i)
@@ -2239,7 +2175,7 @@ static int ecdsa_verifysig(ssh_key *key, const void *vsig, int siglen,
(bignum_bit(ec->publicKey.x, 0) << 7)));
/* Add the message itself */
- put_data(&hs, data, datalen);
+ put_data(&hs, data.ptr, data.len);
/* Get the hash */
SHA512_Final(&hs, digest);
@@ -2291,20 +2227,23 @@ static int ecdsa_verifysig(ssh_key *key, const void *vsig, int siglen,
} else {
Bignum r, s;
unsigned char digest[512 / 8];
+ int digestLen;
void *hashctx;
- r = getmp(&p, &slen);
- if (!r) return 0;
- s = getmp(&p, &slen);
- if (!s) {
+ BinarySource_BARE_INIT(src, sigstr.ptr, sigstr.len);
+
+ r = get_mp_ssh2(src);
+ s = get_mp_ssh2(src);
+ if (get_err(src)) {
freebn(r);
+ freebn(s);
return 0;
}
digestLen = extra->hash->hlen;
assert(digestLen <= sizeof(digest));
hashctx = extra->hash->init();
- put_data(extra->hash->sink(hashctx), data, datalen);
+ put_data(extra->hash->sink(hashctx), data.ptr, data.len);
extra->hash->final(hashctx, digest);
/* Verify the signature */
diff --git a/sshpubk.c b/sshpubk.c
index 01cacb58..e3a80b0c 100644
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -799,8 +799,9 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
ret = snew(struct ssh2_userkey);
ret->alg = alg;
ret->comment = comment;
- ret->data = alg->createkey(alg, public_blob->u, public_blob->len,
- private_blob->u, private_blob->len);
+ ret->data = alg->createkey(
+ alg, make_ptrlen(public_blob->u, public_blob->len),
+ make_ptrlen(private_blob->u, private_blob->len));
if (!ret->data) {
sfree(ret);
ret = NULL;
@@ -1509,7 +1510,7 @@ char *ssh2_fingerprint_blob(const void *blob, int bloblen)
if (!get_err(src)) {
alg = find_pubkey_alg_len(algname);
if (alg) {
- int bits = alg->pubkey_bits(alg, blob, bloblen);
+ int bits = alg->pubkey_bits(alg, make_ptrlen(blob, bloblen));
return dupprintf("%.*s %d %s", PTRLEN_PRINTF(algname),
bits, fingerprint_str);
} else {
diff --git a/sshrsa.c b/sshrsa.c
index c0008dc1..751d1c3e 100644
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -520,62 +520,27 @@ void freersakey(struct RSAKey *key)
* Implementation of the ssh-rsa signing key type.
*/
-static void getstring(const char **data, int *datalen,
- const char **p, int *length)
-{
- *p = NULL;
- if (*datalen < 4)
- return;
- *length = toint(GET_32BIT(*data));
- if (*length < 0)
- return;
- *datalen -= 4;
- *data += 4;
- if (*datalen < *length)
- return;
- *p = *data;
- *data += *length;
- *datalen -= *length;
-}
-static Bignum getmp(const char **data, int *datalen)
-{
- const char *p;
- int length;
- Bignum b;
-
- getstring(data, datalen, &p, &length);
- if (!p)
- return NULL;
- b = bignum_from_bytes(p, length);
- return b;
-}
-
static void rsa2_freekey(ssh_key *key); /* forward reference */
-static ssh_key *rsa2_newkey(const ssh_keyalg *self,
- const void *vdata, int len)
+static ssh_key *rsa2_newkey(const ssh_keyalg *self, ptrlen data)
{
- const char *p;
- const char *data = (const char *)vdata;
- int slen;
+ BinarySource src[1];
struct RSAKey *rsa;
- rsa = snew(struct RSAKey);
- getstring(&data, &len, &p, &slen);
-
- if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) {
- sfree(rsa);
+ BinarySource_BARE_INIT(src, data.ptr, data.len);
+ if (!ptrlen_eq_string(get_string(src), "ssh-rsa"))
return NULL;
- }
- rsa->exponent = getmp(&data, &len);
- rsa->modulus = getmp(&data, &len);
+
+ rsa = snew(struct RSAKey);
+ rsa->exponent = get_mp_ssh2(src);
+ rsa->modulus = get_mp_ssh2(src);
rsa->private_exponent = NULL;
rsa->p = rsa->q = rsa->iqmp = NULL;
rsa->comment = NULL;
- if (!rsa->exponent || !rsa->modulus) {
- rsa2_freekey(&rsa->sshk);
- return NULL;
+ if (get_err(src)) {
+ rsa2_freekey(&rsa->sshk);
+ return NULL;
}
return &rsa->sshk;
@@ -620,24 +585,24 @@ static void rsa2_private_blob(ssh_key *key, BinarySink *bs)
}
static ssh_key *rsa2_createkey(const ssh_keyalg *self,
- const void *pub_blob, int pub_len,
- const void *priv_blob, int priv_len)
+ ptrlen pub, ptrlen priv)
{
+ BinarySource src[1];
ssh_key *sshk;
struct RSAKey *rsa;
- const char *pb = (const char *) priv_blob;
- sshk = rsa2_newkey(self, pub_blob, pub_len);
+ sshk = rsa2_newkey(self, pub);
if (!sshk)
return NULL;
rsa = FROMFIELD(sshk, struct RSAKey, sshk);
- rsa->private_exponent = getmp(&pb, &priv_len);
- rsa->p = getmp(&pb, &priv_len);
- rsa->q = getmp(&pb, &priv_len);
- rsa->iqmp = getmp(&pb, &priv_len);
+ BinarySource_BARE_INIT(src, priv.ptr, priv.len);
+ rsa->private_exponent = get_mp_ssh2(src);
+ rsa->p = get_mp_ssh2(src);
+ rsa->q = get_mp_ssh2(src);
+ rsa->iqmp = get_mp_ssh2(src);
- if (!rsa_verify(rsa)) {
+ if (get_err(src) || !rsa_verify(rsa)) {
rsa2_freekey(&rsa->sshk);
return NULL;
}
@@ -646,28 +611,21 @@ static ssh_key *rsa2_createkey(const ssh_keyalg *self,
}
static ssh_key *rsa2_openssh_createkey(const ssh_keyalg *self,
- const unsigned char **blob, int *len)
+ BinarySource *src)
{
- const char **b = (const char **) blob;
struct RSAKey *rsa;
rsa = snew(struct RSAKey);
rsa->comment = NULL;
- rsa->modulus = getmp(b, len);
- rsa->exponent = getmp(b, len);
- rsa->private_exponent = getmp(b, len);
- rsa->iqmp = getmp(b, len);
- rsa->p = getmp(b, len);
- rsa->q = getmp(b, len);
-
- if (!rsa->modulus || !rsa->exponent || !rsa->private_exponent ||
- !rsa->iqmp || !rsa->p || !rsa->q) {
- rsa2_freekey(&rsa->sshk);
- return NULL;
- }
+ rsa->modulus = get_mp_ssh2(src);
+ rsa->exponent = get_mp_ssh2(src);
+ rsa->private_exponent = get_mp_ssh2(src);
+ rsa->iqmp = get_mp_ssh2(src);
+ rsa->p = get_mp_ssh2(src);
+ rsa->q = get_mp_ssh2(src);
- if (!rsa_verify(rsa)) {
+ if (get_err(src) || !rsa_verify(rsa)) {
rsa2_freekey(&rsa->sshk);
return NULL;
}
@@ -687,14 +645,13 @@ static void rsa2_openssh_fmtkey(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, rsa->q);
}
-static int rsa2_pubkey_bits(const ssh_keyalg *self,
- const void *blob, int len)
+static int rsa2_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
{
ssh_key *sshk;
struct RSAKey *rsa;
int ret;
- sshk = rsa2_newkey(self, blob, len);
+ sshk = rsa2_newkey(self, pub);
if (!sshk)
return -1;
@@ -736,24 +693,32 @@ static const unsigned char asn1_weird_stuff[] = {
#define ASN1_LEN ( (int) sizeof(asn1_weird_stuff) )
-static int rsa2_verifysig(ssh_key *key, const void *vsig, int siglen,
- const void *data, int datalen)
+static int rsa2_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
{
struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
- const char *sig = (const char *)vsig;
+ BinarySource src[1];
+ ptrlen type, in_pl;
Bignum in, out;
- const char *p;
- int slen;
int bytes, i, j, ret;
unsigned char hash[20];
- getstring(&sig, &siglen, &p, &slen);
- if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) {
+ BinarySource_BARE_INIT(src, sig.ptr, sig.len);
+ type = get_string(src);
+ /*
+ * RFC 4253 section 6.6: the signature integer in an ssh-rsa
+ * signature is 'without lengths or padding'. That is, we _don't_
+ * expect the usual leading zero byte if the topmost bit of the
+ * first byte is set. (However, because of the possibility of
+ * BUG_SSH2_RSA_PADDING at the other end, we tolerate it if it's
+ * there.) So we can't use get_mp_ssh2, which enforces that
+ * leading-byte scheme; instead we use get_string and
+ * bignum_from_bytes, which will tolerate anything.
+ */
+ in_pl = get_string(src);
+ if (get_err(src) || !ptrlen_eq_string(type, "ssh-rsa"))
return 0;
- }
- in = getmp(&sig, &siglen);
- if (!in)
- return 0;
+
+ in = bignum_from_bytes(in_pl.ptr, in_pl.len);
out = modpow(in, rsa->exponent, rsa->modulus);
freebn(in);
@@ -777,7 +742,7 @@ static int rsa2_verifysig(ssh_key *key, const void *vsig, int siglen,
ret = 0;
}
/* Finally, we expect to see the SHA-1 hash of the signed data. */
- SHA_Simple(data, datalen, hash);
+ SHA_Simple(data.ptr, data.len, hash);
for (i = 19, j = 0; i >= 0; i--, j++) {
if (bignum_byte(out, i) != hash[j])
ret = 0;
@@ -846,7 +811,7 @@ const ssh_keyalg ssh_rsa = {
struct RSAKey *ssh_rsakex_newkey(const void *data, int len)
{
- ssh_key *sshk = rsa2_newkey(&ssh_rsa, data, len);
+ ssh_key *sshk = rsa2_newkey(&ssh_rsa, make_ptrlen(data, len));
if (!sshk)
return NULL;
return FROMFIELD(sshk, struct RSAKey, sshk);
From 5acd523ae6c5b6a4be83b5131d53826606778335 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 29 May 2018 22:41:37 +0100
Subject: [PATCH 335/607] Rewrite .Xauthority parsing using BinarySource.
This rewrite replaces a particularly hairy macro-based system.
---
x11fwd.c | 103 ++++++++++++++++++++++++++++++++++---------------------
1 file changed, 63 insertions(+), 40 deletions(-)
diff --git a/x11fwd.c b/x11fwd.c
index f59f9e2e..bd4c9289 100644
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -429,15 +429,28 @@ static const char *x11_verify(unsigned long peer_ip, int peer_port,
return NULL;
}
+ptrlen BinarySource_get_string_xauth(BinarySource *src)
+{
+ size_t len = get_uint16(src);
+ return get_data(src, len);
+}
+#define get_string_xauth(src) \
+ BinarySource_get_string_xauth(BinarySource_UPCAST(src))
+
void x11_get_auth_from_authfile(struct X11Display *disp,
const char *authfilename)
{
FILE *authfp;
- char *buf, *ptr, *str[4];
- int len[4];
+ char *buf;
+ int size;
+ BinarySource src[1];
int family, protocol;
+ ptrlen addr, protoname, data;
+ char *displaynum_string;
+ int displaynum;
int ideal_match = FALSE;
char *ourhostname;
+ const size_t MAX_RECORD_SIZE = 0x80, BUF_SIZE = 2 * MAX_RECORD_SIZE;
/*
* Normally we should look for precisely the details specified in
@@ -468,29 +481,41 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
ourhostname = get_hostname();
- /* Records in .Xauthority contain four strings of up to 64K each */
- buf = snewn(65537 * 4, char);
+ /*
+ * Allocate enough space to hold two maximally sized records, so
+ * that a full record can start anywhere in the first half. That
+ * way we avoid the accidentally-quadratic algorithm that would
+ * arise if we moved everything to the front of the buffer after
+ * consuming each record; instead, we only move everything to the
+ * front after our current position gets past the half-way mark.
+ * Before then, there's no need to move anyway; so this guarantees
+ * linear time, in that every byte written into this buffer moves
+ * at most once (because every move is from the second half of the
+ * buffer to the first half).
+ */
+ buf = snewn(BUF_SIZE, char);
+ size = fread(buf, 1, BUF_SIZE, authfp);
+ BinarySource_BARE_INIT(src, buf, size);
while (!ideal_match) {
- int c, i, j, match = FALSE;
-
-#define GET do { c = fgetc(authfp); if (c == EOF) goto done; c = (unsigned char)c; } while (0)
- /* Expect a big-endian 2-byte number giving address family */
- GET; family = c;
- GET; family = (family << 8) | c;
- /* Then expect four strings, each composed of a big-endian 2-byte
- * length field followed by that many bytes of data */
- ptr = buf;
- for (i = 0; i < 4; i++) {
- GET; len[i] = c;
- GET; len[i] = (len[i] << 8) | c;
- str[i] = ptr;
- for (j = 0; j < len[i]; j++) {
- GET; *ptr++ = c;
- }
- *ptr++ = '\0';
- }
-#undef GET
+ int match = FALSE;
+
+ if (src->pos >= MAX_RECORD_SIZE) {
+ size -= src->pos;
+ memcpy(buf, buf + src->pos, size);
+ size += fread(buf + size, 1, BUF_SIZE - size, authfp);
+ BinarySource_BARE_INIT(src, buf, size);
+ }
+
+ family = get_uint16(src);
+ addr = get_string_xauth(src);
+ displaynum_string = mkstr(get_string_xauth(src));
+ displaynum = atoi(displaynum_string);
+ sfree(displaynum_string);
+ protoname = get_string_xauth(src);
+ data = get_string_xauth(src);
+ if (get_err(src))
+ break;
/*
* Now we have a full X authority record in memory. See
@@ -504,7 +529,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
* connect to the display. 0 means IPv4; 6 means IPv6;
* 256 means Unix-domain sockets.
*
- * - str[0] is the network address itself. For IPv4 and
+ * - 'addr' is the network address itself. For IPv4 and
* IPv6, this is a string of binary data of the
* appropriate length (respectively 4 and 16 bytes)
* representing the address in big-endian format, e.g.
@@ -515,24 +540,23 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
* authority entries for Unix-domain displays on
* several machines without them clashing).
*
- * - str[1] is the display number. I've no idea why
+ * - 'displaynum' is the display number. I've no idea why
* .Xauthority stores this as a string when it has a
* perfectly good integer format, but there we go.
*
- * - str[2] is the authorisation method, encoded as its
- * canonical string name (i.e. "MIT-MAGIC-COOKIE-1",
- * "XDM-AUTHORIZATION-1" or something we don't
- * recognise).
+ * - 'protoname' is the authorisation protocol, encoded as
+ * its canonical string name (i.e. "MIT-MAGIC-COOKIE-1",
+ * "XDM-AUTHORIZATION-1" or something we don't recognise).
*
- * - str[3] is the actual authorisation data, stored in
+ * - 'data' is the actual authorisation data, stored in
* binary form.
*/
- if (disp->displaynum < 0 || disp->displaynum != atoi(str[1]))
+ if (disp->displaynum < 0 || disp->displaynum != displaynum)
continue; /* not the one */
for (protocol = 1; protocol < lenof(x11_authnames); protocol++)
- if (!strcmp(str[2], x11_authnames[protocol]))
+ if (ptrlen_eq_string(protoname, x11_authnames[protocol]))
break;
if (protocol == lenof(x11_authnames))
continue; /* don't recognise this protocol, look for another */
@@ -543,7 +567,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
sk_addrtype(disp->addr) == ADDRTYPE_IPV4) {
char buf[4];
sk_addrcopy(disp->addr, buf);
- if (len[0] == 4 && !memcmp(str[0], buf, 4)) {
+ if (addr.len == 4 && !memcmp(addr.ptr, buf, 4)) {
match = TRUE;
/* If this is a "localhost" entry, note it down
* but carry on looking for a Unix-domain entry. */
@@ -556,7 +580,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
sk_addrtype(disp->addr) == ADDRTYPE_IPV6) {
char buf[16];
sk_addrcopy(disp->addr, buf);
- if (len[0] == 16 && !memcmp(str[0], buf, 16)) {
+ if (addr.len == 16 && !memcmp(addr.ptr, buf, 16)) {
match = TRUE;
ideal_match = !localhost;
}
@@ -564,7 +588,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
break;
case 256: /* Unix-domain / localhost */
if ((disp->unixdomain || localhost)
- && ourhostname && !strcmp(ourhostname, str[0]))
+ && ourhostname && ptrlen_eq_string(addr, ourhostname))
/* A matching Unix-domain socket is always the best
* match. */
match = ideal_match = TRUE;
@@ -575,15 +599,14 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
/* Current best guess -- may be overridden if !ideal_match */
disp->localauthproto = protocol;
sfree(disp->localauthdata); /* free previous guess, if any */
- disp->localauthdata = snewn(len[3], unsigned char);
- memcpy(disp->localauthdata, str[3], len[3]);
- disp->localauthdatalen = len[3];
+ disp->localauthdata = snewn(data.len, unsigned char);
+ memcpy(disp->localauthdata, data.ptr, data.len);
+ disp->localauthdatalen = data.len;
}
}
- done:
fclose(authfp);
- smemclr(buf, 65537 * 4);
+ smemclr(buf, 2 * MAX_RECORD_SIZE);
sfree(buf);
sfree(ourhostname);
}
From 4d8c03359614356244be9591abac4c5f6dc1e34b Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 30 May 2018 22:36:20 +0100
Subject: [PATCH 336/607] Rewrite SOCKS client code using BinarySource.
I've also replaced the entire SOCKS state machine whose states were
barely-documented literal integers with one that uses an actual enum.
I think the result is a great deal clearer.
In the course of this rewrite I noticed that PuTTY's dynamic port
forwarding had never got round to supporting the SOCKS5 IPv6 address
format - though there was a FIXME comment saying it ought to. So now
it does: if a SOCKS5 client provides a binary IPv6 address (which
PuTTY's _own_ SOCKS5 client, in proxy.c, is quite capable of doing!),
then that will be translated into the usual IPv6 hex literal
representation to put in the "direct-tcpip" channel open request.
---
portfwd.c | 500 ++++++++++++++++++++++++++++--------------------------
1 file changed, 259 insertions(+), 241 deletions(-)
diff --git a/portfwd.c b/portfwd.c
index 1e4d2d1f..069bc3bf 100644
--- a/portfwd.c
+++ b/portfwd.c
@@ -2,12 +2,25 @@
* SSH port forwarding.
*/
+#include
#include
#include
#include "putty.h"
#include "ssh.h"
+/*
+ * Enumeration of values that live in the 'socks_state' field of
+ * struct PortForwarding.
+ */
+typedef enum {
+ SOCKS_NONE, /* direct connection (no SOCKS, or SOCKS already done) */
+ SOCKS_INITIAL, /* don't know if we're SOCKS 4 or 5 yet */
+ SOCKS_4, /* expect a SOCKS 4 (or 4A) connection message */
+ SOCKS_5_INITIAL, /* expect a SOCKS 5 preliminary message */
+ SOCKS_5_CONNECT /* expect a SOCKS 5 connection message */
+} SocksState;
+
struct PortForwarding {
struct ssh_channel *c; /* channel structure held by ssh.c */
void *backhandle; /* instance of SSH backend itself */
@@ -15,13 +28,7 @@ struct PortForwarding {
Socket s;
int throttled, throttle_override;
int ready;
- /*
- * `dynamic' does double duty. It's set to 0 for an ordinary
- * forwarded port, and nonzero for SOCKS-style dynamic port
- * forwarding; but the nonzero values are also a state machine
- * tracking where the SOCKS exchange has got to.
- */
- int dynamic;
+ SocksState socks_state;
/*
* `hostname' and `port' are the real hostname and port, once
* we know what we're connecting to.
@@ -29,18 +36,12 @@ struct PortForwarding {
char *hostname;
int port;
/*
- * `socksbuf' is the buffer we use to accumulate a SOCKS request.
+ * `socksbuf' is the buffer we use to accumulate the initial SOCKS
+ * segment of the incoming data, plus anything after that that we
+ * receive before we're ready to send data to the SSH server.
*/
- char *socksbuf;
- int sockslen, sockssize;
- /*
- * When doing dynamic port forwarding, we can receive
- * connection data before we are actually able to send it; so
- * we may have to temporarily hold some in a dynamically
- * allocated buffer here.
- */
- void *buffer;
- int buflen;
+ strbuf *socksbuf;
+ size_t socksbuf_consumed;
const Plug_vtable *plugvt;
};
@@ -48,11 +49,7 @@ struct PortForwarding {
struct PortListener {
void *backhandle; /* instance of SSH backend itself */
Socket s;
- /*
- * `dynamic' is set to 0 for an ordinary forwarded port, and
- * nonzero for SOCKS-style dynamic port forwarding.
- */
- int dynamic;
+ int is_dynamic;
/*
* `hostname' and `port' are the real hostname and port, for
* ordinary forwardings.
@@ -68,8 +65,6 @@ static struct PortForwarding *new_portfwd_state(void)
struct PortForwarding *pf = snew(struct PortForwarding);
pf->hostname = NULL;
pf->socksbuf = NULL;
- pf->sockslen = pf->sockssize = 0;
- pf->buffer = NULL;
return pf;
}
@@ -78,8 +73,8 @@ static void free_portfwd_state(struct PortForwarding *pf)
if (!pf)
return;
sfree(pf->hostname);
- sfree(pf->socksbuf);
- sfree(pf->buffer);
+ if (pf->socksbuf)
+ strbuf_free(pf->socksbuf);
sfree(pf);
}
@@ -162,207 +157,238 @@ static void wrap_send_port_open(void *channel, const char *hostname, int port,
sfree(description);
}
+static char *ipv4_to_string(unsigned ipv4)
+{
+ return dupprintf("%u.%u.%u.%u",
+ (ipv4 >> 24) & 0xFF, (ipv4 >> 16) & 0xFF,
+ (ipv4 >> 8) & 0xFF, (ipv4 ) & 0xFF);
+}
+
+static char *ipv6_to_string(ptrlen ipv6)
+{
+ const unsigned char *addr = ipv6.ptr;
+ assert(ipv6.len == 16);
+ return dupprintf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+ (unsigned)GET_16BIT_MSB_FIRST(addr + 0),
+ (unsigned)GET_16BIT_MSB_FIRST(addr + 2),
+ (unsigned)GET_16BIT_MSB_FIRST(addr + 4),
+ (unsigned)GET_16BIT_MSB_FIRST(addr + 6),
+ (unsigned)GET_16BIT_MSB_FIRST(addr + 8),
+ (unsigned)GET_16BIT_MSB_FIRST(addr + 10),
+ (unsigned)GET_16BIT_MSB_FIRST(addr + 12),
+ (unsigned)GET_16BIT_MSB_FIRST(addr + 14));
+}
+
static void pfd_receive(Plug plug, int urgent, char *data, int len)
{
struct PortForwarding *pf = FROMFIELD(plug, struct PortForwarding, plugvt);
- if (pf->dynamic) {
- while (len--) {
- if (pf->sockslen >= pf->sockssize) {
- pf->sockssize = pf->sockslen * 5 / 4 + 256;
- pf->socksbuf = sresize(pf->socksbuf, pf->sockssize, char);
- }
- pf->socksbuf[pf->sockslen++] = *data++;
-
- /*
- * Now check what's in the buffer to see if it's a
- * valid and complete message in the SOCKS exchange.
- */
- if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 4) &&
- pf->socksbuf[0] == 4) {
- /*
- * SOCKS 4.
- */
- if (pf->dynamic == 1)
- pf->dynamic = 0x4000;
- if (pf->sockslen < 2)
- continue; /* don't have command code yet */
- if (pf->socksbuf[1] != 1) {
- /* Not CONNECT. */
- /* Send back a SOCKS 4 error before closing. */
- char data[8];
- memset(data, 0, sizeof(data));
- data[1] = 91; /* generic `request rejected' */
- sk_write(pf->s, data, 8);
- pfd_close(pf);
- return;
- }
- if (pf->sockslen <= 8)
- continue; /* haven't started user/hostname */
- if (pf->socksbuf[pf->sockslen-1] != 0)
- continue; /* haven't _finished_ user/hostname */
- /*
- * Now we have a full SOCKS 4 request. Check it to
- * see if it's a SOCKS 4A request.
- */
- if (pf->socksbuf[4] == 0 && pf->socksbuf[5] == 0 &&
- pf->socksbuf[6] == 0 && pf->socksbuf[7] != 0) {
- /*
- * It's SOCKS 4A. So if we haven't yet
- * collected the host name, we should continue
- * waiting for data in order to do so; if we
- * have, we can go ahead.
- */
- int len;
- if (pf->dynamic == 0x4000) {
- pf->dynamic = 0x4001;
- pf->sockslen = 8; /* reset buffer to overwrite name */
- continue;
- }
- pf->socksbuf[0] = 0; /* reply version code */
- pf->socksbuf[1] = 90; /* request granted */
- sk_write(pf->s, pf->socksbuf, 8);
- len = pf->sockslen - 8;
- pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2);
- pf->hostname = snewn(len+1, char);
- pf->hostname[len] = '\0';
- memcpy(pf->hostname, pf->socksbuf + 8, len);
- goto connect;
- } else {
- /*
- * It's SOCKS 4, which means we should format
- * the IP address into the hostname string and
- * then just go.
- */
- pf->socksbuf[0] = 0; /* reply version code */
- pf->socksbuf[1] = 90; /* request granted */
- sk_write(pf->s, pf->socksbuf, 8);
- pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2);
- pf->hostname = dupprintf("%d.%d.%d.%d",
- (unsigned char)pf->socksbuf[4],
- (unsigned char)pf->socksbuf[5],
- (unsigned char)pf->socksbuf[6],
- (unsigned char)pf->socksbuf[7]);
- goto connect;
- }
- }
-
- if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 5) &&
- pf->socksbuf[0] == 5) {
- /*
- * SOCKS 5.
- */
- if (pf->dynamic == 1)
- pf->dynamic = 0x5000;
-
- if (pf->dynamic == 0x5000) {
- int i, method;
- char data[2];
- /*
- * We're receiving a set of method identifiers.
- */
- if (pf->sockslen < 2)
- continue; /* no method count yet */
- if (pf->sockslen < 2 + (unsigned char)pf->socksbuf[1])
- continue; /* no methods yet */
- method = 0xFF; /* invalid */
- for (i = 0; i < (unsigned char)pf->socksbuf[1]; i++)
- if (pf->socksbuf[2+i] == 0) {
- method = 0;/* no auth */
- break;
- }
- data[0] = 5;
- data[1] = method;
- sk_write(pf->s, data, 2);
- pf->dynamic = 0x5001;
- pf->sockslen = 0; /* re-empty the buffer */
- continue;
- }
-
- if (pf->dynamic == 0x5001) {
- /*
- * We're receiving a SOCKS request.
- */
- unsigned char reply[10]; /* SOCKS5 atyp=1 reply */
- int atype, alen = 0;
-
- /*
- * Pre-fill reply packet.
- * In all cases, we set BND.{HOST,ADDR} to 0.0.0.0:0
- * (atyp=1) in the reply; if we succeed, we don't know
- * the right answers, and if we fail, they should be
- * ignored.
- */
- memset(reply, 0, lenof(reply));
- reply[0] = 5; /* VER */
- reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */
-
- if (pf->sockslen < 6) continue;
- atype = (unsigned char)pf->socksbuf[3];
- if (atype == 1) /* IPv4 address */
- alen = 4;
- if (atype == 4) /* IPv6 address */
- alen = 16;
- if (atype == 3) /* domain name has leading length */
- alen = 1 + (unsigned char)pf->socksbuf[4];
- if (pf->sockslen < 6 + alen) continue;
- if (pf->socksbuf[1] != 1 || pf->socksbuf[2] != 0) {
- /* Not CONNECT or reserved field nonzero - error */
- reply[1] = 1; /* generic failure */
- sk_write(pf->s, reply, lenof(reply));
- pfd_close(pf);
- return;
- }
- /*
- * Now we have a viable connect request. Switch
- * on atype.
- */
- pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+4+alen);
- if (atype == 1) {
- /* REP=0 (success) already */
- sk_write(pf->s, reply, lenof(reply));
- pf->hostname = dupprintf("%d.%d.%d.%d",
- (unsigned char)pf->socksbuf[4],
- (unsigned char)pf->socksbuf[5],
- (unsigned char)pf->socksbuf[6],
- (unsigned char)pf->socksbuf[7]);
- goto connect;
- } else if (atype == 3) {
- /* REP=0 (success) already */
- sk_write(pf->s, reply, lenof(reply));
- pf->hostname = snewn(alen, char);
- pf->hostname[alen-1] = '\0';
- memcpy(pf->hostname, pf->socksbuf + 5, alen-1);
- goto connect;
- } else {
- /*
- * Unknown address type. (FIXME: support IPv6!)
- */
- reply[1] = 8; /* atype not supported */
- sk_write(pf->s, reply, lenof(reply));
- pfd_close(pf);
- return;
- }
- }
- }
-
- /*
- * If we get here without either having done `continue'
- * or `goto connect', it must be because there is no
- * sensible interpretation of what's in our buffer. So
- * close the connection rudely.
- */
- pfd_close(pf);
- break;
- }
- return;
+
+ if (len == 0)
+ return;
+
+ if (pf->socks_state != SOCKS_NONE) {
+ BinarySource src[1];
+
+ /*
+ * Store all the data we've got in socksbuf.
+ */
+ put_data(pf->socksbuf, data, len);
+
+ /*
+ * Check the start of socksbuf to see if it's a valid and
+ * complete message in the SOCKS exchange.
+ */
+
+ if (pf->socks_state == SOCKS_INITIAL) {
+ /* Preliminary: check the first byte of the data (which we
+ * _must_ have by now) to find out which SOCKS major
+ * version we're speaking. */
+ switch (pf->socksbuf->u[0]) {
+ case 4:
+ pf->socks_state = SOCKS_4;
+ break;
+ case 5:
+ pf->socks_state = SOCKS_5_INITIAL;
+ break;
+ default:
+ pfd_close(pf); /* unrecognised version */
+ return;
+ }
+ }
+
+ BinarySource_BARE_INIT(src, pf->socksbuf->u, pf->socksbuf->len);
+ get_data(src, pf->socksbuf_consumed);
+
+ while (pf->socks_state != SOCKS_NONE) {
+ unsigned socks_version, message_type, reserved_byte;
+ unsigned reply_code, port, ipv4, method;
+ ptrlen methods;
+ const char *socks4_hostname;
+ strbuf *output;
+
+ switch (pf->socks_state) {
+ case SOCKS_INITIAL:
+ case SOCKS_NONE:
+ assert(0 && "These case values cannot appear");
+
+ case SOCKS_4:
+ /* SOCKS 4/4A connect message */
+ socks_version = get_byte(src);
+ message_type = get_byte(src);
+
+ if (get_err(src) == BSE_OUT_OF_DATA)
+ return;
+ if (socks_version == 4 && message_type == 1) {
+ /* CONNECT message */
+ int name_based = FALSE;
+
+ port = get_uint16(src);
+ ipv4 = get_uint32(src);
+ if (ipv4 > 0x00000000 && ipv4 < 0x00000100) {
+ /*
+ * Addresses in this range indicate the SOCKS 4A
+ * extension to specify a hostname, which comes
+ * after the username.
+ */
+ name_based = TRUE;
+ }
+ get_asciz(src); /* skip username */
+ socks4_hostname = name_based ? get_asciz(src) : NULL;
+
+ if (get_err(src) == BSE_OUT_OF_DATA)
+ return;
+ if (get_err(src))
+ goto socks4_reject;
+
+ pf->port = port;
+ if (name_based) {
+ pf->hostname = dupstr(socks4_hostname);
+ } else {
+ pf->hostname = ipv4_to_string(ipv4);
+ }
+
+ output = strbuf_new();
+ put_byte(output, 0); /* reply version */
+ put_byte(output, 90); /* SOCKS 4 'request granted' */
+ put_uint16(output, 0); /* null port field */
+ put_uint32(output, 0); /* null address field */
+ sk_write(pf->s, output->u, output->len);
+ strbuf_free(output);
+
+ pf->socks_state = SOCKS_NONE;
+ pf->socksbuf_consumed = src->pos;
+ break;
+ }
+
+ socks4_reject:
+ output = strbuf_new();
+ put_byte(output, 0); /* reply version */
+ put_byte(output, 91); /* SOCKS 4 'request rejected' */
+ put_uint16(output, 0); /* null port field */
+ put_uint32(output, 0); /* null address field */
+ sk_write(pf->s, output->u, output->len);
+ strbuf_free(output);
+ pfd_close(pf);
+ return;
+
+ case SOCKS_5_INITIAL:
+ /* SOCKS 5 initial method list */
+ socks_version = get_byte(src);
+ methods = get_pstring(src);
+
+ method = 0xFF; /* means 'no usable method found' */
+ {
+ int i;
+ for (i = 0; i < methods.len; i++) {
+ if (((const unsigned char *)methods.ptr)[i] == 0 ) {
+ method = 0; /* no auth */
+ break;
+ }
+ }
+ }
+
+ if (get_err(src) == BSE_OUT_OF_DATA)
+ return;
+ if (get_err(src))
+ method = 0xFF;
+
+ output = strbuf_new();
+ put_byte(output, 5); /* SOCKS version */
+ put_byte(output, method); /* selected auth method */
+ sk_write(pf->s, output->u, output->len);
+ strbuf_free(output);
+
+ if (method == 0xFF) {
+ pfd_close(pf);
+ return;
+ }
+
+ pf->socks_state = SOCKS_5_CONNECT;
+ pf->socksbuf_consumed = src->pos;
+ break;
+
+ case SOCKS_5_CONNECT:
+ /* SOCKS 5 connect message */
+ socks_version = get_byte(src);
+ message_type = get_byte(src);
+ reserved_byte = get_byte(src);
+
+ if (socks_version == 5 && message_type == 1 &&
+ reserved_byte == 0) {
+
+ reply_code = 0; /* success */
+
+ switch (get_byte(src)) {
+ case 1: /* IPv4 */
+ pf->hostname = ipv4_to_string(get_uint32(src));
+ break;
+ case 4: /* IPv6 */
+ pf->hostname = ipv6_to_string(get_data(src, 16));
+ break;
+ case 3: /* unresolved domain name */
+ pf->hostname = mkstr(get_pstring(src));
+ break;
+ default:
+ pf->hostname = NULL;
+ reply_code = 8; /* address type not supported */
+ break;
+ }
+
+ pf->port = get_uint16(src);
+ } else {
+ reply_code = 7; /* command not supported */
+ }
+
+ if (get_err(src) == BSE_OUT_OF_DATA)
+ return;
+ if (get_err(src))
+ reply_code = 1; /* general server failure */
+
+ output = strbuf_new();
+ put_byte(output, 5); /* SOCKS version */
+ put_byte(output, reply_code);
+ put_byte(output, 0); /* reserved */
+ put_byte(output, 1); /* IPv4 address follows */
+ put_uint32(output, 0); /* bound IPv4 address (unused) */
+ put_uint16(output, 0); /* bound port number (unused) */
+ sk_write(pf->s, output->u, output->len);
+ strbuf_free(output);
+
+ if (reply_code != 0) {
+ pfd_close(pf);
+ return;
+ }
+
+ pf->socks_state = SOCKS_NONE;
+ pf->socksbuf_consumed = src->pos;
+ break;
+ }
+ }
/*
* We come here when we're ready to make an actual
* connection.
*/
- connect:
- sfree(pf->socksbuf);
- pf->socksbuf = NULL;
/*
* Freeze the socket until the SSH server confirms the
@@ -378,17 +404,6 @@ static void pfd_receive(Plug plug, int urgent, char *data, int len)
/* asks to forward to the specified host/port for this */
wrap_send_port_open(pf->c, pf->hostname, pf->port, pf->s);
}
- pf->dynamic = 0;
-
- /*
- * If there's any data remaining in our current buffer,
- * save it to be sent on pfd_confirm().
- */
- if (len > 0) {
- pf->buffer = snewn(len, char);
- memcpy(pf->buffer, data, len);
- pf->buflen = len;
- }
}
if (pf->ready) {
if (sshfwd_write(pf->c, data, len) > 0) {
@@ -450,7 +465,7 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
pf->ready = 1;
pf->c = c;
pf->backhandle = NULL; /* we shouldn't need this */
- pf->dynamic = 0;
+ pf->socks_state = SOCKS_NONE;
pf->s = new_connection(addr, dummy_realhost, port,
0, 1, 0, 0, &pf->plugvt, conf);
@@ -493,12 +508,14 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
pf->throttled = pf->throttle_override = 0;
pf->ready = 0;
- if (pl->dynamic) {
- pf->dynamic = 1;
+ if (pl->is_dynamic) {
+ pf->socks_state = SOCKS_INITIAL;
+ pf->socksbuf = strbuf_new();
+ pf->socksbuf_consumed = 0;
pf->port = 0; /* "hostname" buffer is so far empty */
sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */
} else {
- pf->dynamic = 0;
+ pf->socks_state = SOCKS_NONE;
pf->hostname = dupstr(pl->hostname);
pf->port = pl->port;
pf->c = new_sock_channel(pl->backhandle, pf);
@@ -544,9 +561,9 @@ char *pfl_listen(char *desthost, int destport, char *srcaddr,
if (desthost) {
pl->hostname = dupstr(desthost);
pl->port = destport;
- pl->dynamic = 0;
+ pl->is_dynamic = FALSE;
} else
- pl->dynamic = 1;
+ pl->is_dynamic = TRUE;
pl->backhandle = backhandle;
pl->s = new_listener(srcaddr, port, &pl->plugvt,
@@ -625,9 +642,10 @@ void pfd_confirm(struct PortForwarding *pf)
pf->ready = 1;
sk_set_frozen(pf->s, 0);
sk_write(pf->s, NULL, 0);
- if (pf->buffer) {
- sshfwd_write(pf->c, pf->buffer, pf->buflen);
- sfree(pf->buffer);
- pf->buffer = NULL;
+ if (pf->socksbuf) {
+ sshfwd_write(pf->c, pf->socksbuf->u + pf->socksbuf_consumed,
+ pf->socksbuf->len - pf->socksbuf_consumed);
+ strbuf_free(pf->socksbuf);
+ pf->socksbuf = NULL;
}
}
From 6dc63925965f34e0e898c2c5e465476c536a215f Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 29 May 2018 20:36:21 +0100
Subject: [PATCH 337/607] Remove obsolete functions.
There are several old functions that the previous commits have removed
all, or nearly all, of the references to. match_ssh_id is superseded
by ptrlen_eq_string; get_ssh_{string,uint32} is yet another replicated
set of decode functions (this time _partly_ centralised into misc.c);
the old APIs for the SSH-1 RSA decode functions are gone (together
with their last couple of holdout clients), as are
ssh{1,2}_{read,write}_bignum and ssh{1,2}_bignum_length.
Particularly odd was the use of ssh1_{read,write}_bignum in the SSH-2
Diffie-Hellman implementation. I'd completely forgotten I did that!
Now replaced with a raw bignum_from_bytes, which is simpler anyway.
---
cmdgen.c | 23 +++---------------
misc.c | 33 --------------------------
misc.h | 17 --------------
ssh.h | 8 -------
sshbn.c | 65 +--------------------------------------------------
sshdh.c | 9 ++++---
sshrsa.c | 32 -------------------------
unix/uxpgnt.c | 5 +++-
8 files changed, 12 insertions(+), 180 deletions(-)
diff --git a/cmdgen.c b/cmdgen.c
index b4fcd567..e2d1e75f 100644
--- a/cmdgen.c
+++ b/cmdgen.c
@@ -807,30 +807,13 @@ int main(int argc, char **argv)
ssh1key = snew(struct RSAKey);
if (!load_encrypted) {
strbuf *blob;
- int n, l;
+ BinarySource src[1];
blob = strbuf_new();
ret = rsa_ssh1_loadpub(infilename, BinarySink_UPCAST(blob),
&origcomment, &error);
-
- n = 4; /* skip modulus bits */
-
- l = ssh1_read_bignum(blob->u + n,
- blob->len - n,
- &ssh1key->exponent);
- if (l < 0) {
- error = "SSH-1 public key blob was too short";
- } else {
- n += l;
- l = ssh1_read_bignum(
- blob->u + n,
- blob->len - n, &ssh1key->modulus);
- if (l < 0) {
- error = "SSH-1 public key blob was too short";
- } else
- n += l;
- }
-
+ BinarySource_BARE_INIT(src, blob->u, blob->len);
+ get_rsa_ssh1_pub(src, ssh1key, NULL, RSA_SSH1_EXPONENT_FIRST);
strbuf_free(blob);
ssh1key->comment = dupstr(origcomment);
diff --git a/misc.c b/misc.c
index 30bfec90..36f27171 100644
--- a/misc.c
+++ b/misc.c
@@ -1181,12 +1181,6 @@ int smemeq(const void *av, const void *bv, size_t len)
return (0x100 - val) >> 8;
}
-int match_ssh_id(int stringlen, const void *string, const char *id)
-{
- int idlen = strlen(id);
- return (idlen == stringlen && !memcmp(string, id, idlen));
-}
-
ptrlen make_ptrlen(const void *ptr, size_t len)
{
ptrlen pl;
@@ -1209,33 +1203,6 @@ char *mkstr(ptrlen pl)
return p;
}
-void *get_ssh_string(int *datalen, const void **data, int *stringlen)
-{
- void *ret;
- unsigned int len;
-
- if (*datalen < 4)
- return NULL;
- len = GET_32BIT_MSB_FIRST((const unsigned char *)*data);
- if (*datalen - 4 < len)
- return NULL;
- ret = (void *)((const char *)*data + 4);
- *datalen -= len + 4;
- *data = (const char *)*data + len + 4;
- *stringlen = len;
- return ret;
-}
-
-int get_ssh_uint32(int *datalen, const void **data, unsigned *ret)
-{
- if (*datalen < 4)
- return FALSE;
- *ret = GET_32BIT_MSB_FIRST((const unsigned char *)*data);
- *datalen -= 4;
- *data = (const char *)*data + 4;
- return TRUE;
-}
-
int strstartswith(const char *s, const char *t)
{
return !memcmp(s, t, strlen(t));
diff --git a/misc.h b/misc.h
index de662a87..4b830d99 100644
--- a/misc.h
+++ b/misc.h
@@ -109,23 +109,6 @@ void smemclr(void *b, size_t len);
* by the 'eq' in the name. */
int smemeq(const void *av, const void *bv, size_t len);
-/* Extracts an SSH-marshalled string from the start of *data. If
- * successful (*datalen is not too small), advances data/datalen past
- * the string and returns a pointer to the string itself and its
- * length in *stringlen. Otherwise does nothing and returns NULL.
- *
- * Like strchr, this function can discard const from its parameter.
- * Treat it as if it was a family of two functions, one returning a
- * non-const string given a non-const pointer, and one taking and
- * returning const. */
-void *get_ssh_string(int *datalen, const void **data, int *stringlen);
-/* Extracts an SSH uint32, similarly. Returns TRUE on success, and
- * leaves the extracted value in *ret. */
-int get_ssh_uint32(int *datalen, const void **data, unsigned *ret);
-/* Given a not-necessarily-zero-terminated string in (length,data)
- * form, check if it equals an ordinary C zero-terminated string. */
-int match_ssh_id(int stringlen, const void *string, const char *id);
-
char *buildinfo(const char *newline);
/*
diff --git a/ssh.h b/ssh.h
index 655d72d2..228c7072 100644
--- a/ssh.h
+++ b/ssh.h
@@ -180,13 +180,9 @@ struct ec_point *ec_public(const Bignum privateKey, const struct ec_curve *curve
*/
typedef enum { RSA_SSH1_EXPONENT_FIRST, RSA_SSH1_MODULUS_FIRST } RsaSsh1Order;
-int rsa_ssh1_readpub(const unsigned char *data, int len, struct RSAKey *result,
- const unsigned char **keystr, RsaSsh1Order order);
void BinarySource_get_rsa_ssh1_pub(
BinarySource *src, struct RSAKey *result,
ptrlen *keystr, RsaSsh1Order order);
-int rsa_ssh1_readpriv(const unsigned char *data, int len,
- struct RSAKey *result);
void BinarySource_get_rsa_ssh1_priv(
BinarySource *src, struct RSAKey *rsa);
int rsa_ssh1_encrypt(unsigned char *data, int length, struct RSAKey *key);
@@ -667,14 +663,10 @@ extern Bignum Zero, One;
Bignum bignum_from_bytes(const void *data, int nbytes);
Bignum bignum_from_bytes_le(const void *data, int nbytes);
Bignum bignum_random_in_range(const Bignum lower, const Bignum upper);
-int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result);
int bignum_bitcount(Bignum bn);
-int ssh1_bignum_length(Bignum bn);
-int ssh2_bignum_length(Bignum bn);
int bignum_byte(Bignum bn, int i);
int bignum_bit(Bignum bn, int i);
void bignum_set_bit(Bignum bn, int i, int value);
-int ssh1_write_bignum(void *data, Bignum bn);
Bignum biggcd(Bignum a, Bignum b);
unsigned short bignum_mod_short(Bignum number, unsigned short modulus);
Bignum bignum_add_long(Bignum number, unsigned long addend);
diff --git a/sshbn.c b/sshbn.c
index 1f0213c0..9c36f531 100644
--- a/sshbn.c
+++ b/sshbn.c
@@ -1509,36 +1509,7 @@ Bignum bignum_random_in_range(const Bignum lower, const Bignum upper)
}
/*
- * Read an SSH-1-format bignum from a data buffer. Return the number
- * of bytes consumed, or -1 if there wasn't enough data.
- */
-int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result)
-{
- const unsigned char *p = data;
- int i;
- int w, b;
-
- if (len < 2)
- return -1;
-
- w = 0;
- for (i = 0; i < 2; i++)
- w = (w << 8) + *p++;
- b = (w + 7) / 8; /* bits -> bytes */
-
- if (len < b+2)
- return -1;
-
- if (!result) /* just return length */
- return b + 2;
-
- *result = bignum_from_bytes(p, b);
-
- return p + b - data;
-}
-
-/*
- * Return the bit count of a bignum, for SSH-1 encoding.
+ * Return the bit count of a bignum.
*/
int bignum_bitcount(Bignum bn)
{
@@ -1548,22 +1519,6 @@ int bignum_bitcount(Bignum bn)
return bitcount + 1;
}
-/*
- * Return the byte length of a bignum when SSH-1 encoded.
- */
-int ssh1_bignum_length(Bignum bn)
-{
- return 2 + (bignum_bitcount(bn) + 7) / 8;
-}
-
-/*
- * Return the byte length of a bignum when SSH-2 encoded.
- */
-int ssh2_bignum_length(Bignum bn)
-{
- return 4 + (bignum_bitcount(bn) + 8) / 8;
-}
-
/*
* Return a byte from a bignum; 0 is least significant, etc.
*/
@@ -1604,24 +1559,6 @@ void bignum_set_bit(Bignum bn, int bitnum, int value)
}
}
-/*
- * Write a SSH-1-format bignum into a buffer. It is assumed the
- * buffer is big enough. Returns the number of bytes used.
- */
-int ssh1_write_bignum(void *data, Bignum bn)
-{
- unsigned char *p = data;
- int len = ssh1_bignum_length(bn);
- int i;
- int bitc = bignum_bitcount(bn);
-
- *p++ = (bitc >> 8) & 0xFF;
- *p++ = (bitc) & 0xFF;
- for (i = len - 2; i--;)
- *p++ = bignum_byte(bn, i);
- return len;
-}
-
void BinarySink_put_mp_ssh1(BinarySink *bs, Bignum bn)
{
int bits = bignum_bitcount(bn);
diff --git a/sshdh.c b/sshdh.c
index 46f3a3fc..1c27b021 100644
--- a/sshdh.c
+++ b/sshdh.c
@@ -247,7 +247,7 @@ Bignum dh_create_e(void *handle, int nbits)
int nbytes;
unsigned char *buf;
- nbytes = ssh1_bignum_length(ctx->qmask);
+ nbytes = (bignum_bitcount(ctx->qmask) + 7) / 8;
buf = snewn(nbytes, unsigned char);
do {
@@ -258,10 +258,9 @@ Bignum dh_create_e(void *handle, int nbits)
if (ctx->x)
freebn(ctx->x);
if (nbits == 0 || nbits > bignum_bitcount(ctx->qmask)) {
- ssh1_write_bignum(buf, ctx->qmask);
- for (i = 2; i < nbytes; i++)
- buf[i] &= random_byte();
- ssh1_read_bignum(buf, nbytes, &ctx->x); /* can't fail */
+ for (i = 0; i < nbytes; i++)
+ buf[i] = bignum_byte(ctx->qmask, i) & random_byte();
+ ctx->x = bignum_from_bytes(buf, nbytes);
} else {
int b, nb;
ctx->x = bn_power_2(nbits);
diff --git a/sshrsa.c b/sshrsa.c
index 751d1c3e..51396a5c 100644
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -47,44 +47,12 @@ void BinarySource_get_rsa_ssh1_pub(
}
}
-int rsa_ssh1_readpub(const unsigned char *data, int len, struct RSAKey *result,
- const unsigned char **keystr, RsaSsh1Order order)
-{
- BinarySource src;
- ptrlen key_pl;
-
- BinarySource_BARE_INIT(&src, data, len);
- get_rsa_ssh1_pub(&src, result, &key_pl, order);
-
- if (keystr)
- *keystr = key_pl.ptr;
-
- if (get_err(&src))
- return -1;
- else
- return key_pl.len;
-}
-
void BinarySource_get_rsa_ssh1_priv(
BinarySource *src, struct RSAKey *rsa)
{
rsa->private_exponent = get_mp_ssh1(src);
}
-int rsa_ssh1_readpriv(const unsigned char *data, int len,
- struct RSAKey *result)
-{
- BinarySource src;
-
- BinarySource_BARE_INIT(&src, data, len);
- get_rsa_ssh1_priv(&src, result);
-
- if (get_err(&src))
- return -1;
- else
- return src.pos;
-}
-
int rsa_ssh1_encrypt(unsigned char *data, int length, struct RSAKey *key)
{
Bignum b1, b2;
diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
index 4ef4d433..e3dfe318 100644
--- a/unix/uxpgnt.c
+++ b/unix/uxpgnt.c
@@ -701,10 +701,13 @@ void run_client(void)
FILE *fp = stdout; /* FIXME: add a -o option? */
if (key->ssh_version == 1) {
+ BinarySource src[1];
struct RSAKey rkey;
+
+ BinarySource_BARE_INIT(src, key->blob->u, key->blob->len);
memset(&rkey, 0, sizeof(rkey));
rkey.comment = dupstr(key->comment);
- rsa_ssh1_readpub(key->blob->u, key->blob->len, &rkey, NULL,
+ get_rsa_ssh1_pub(src, &rkey, NULL,
RSA_SSH1_EXPONENT_FIRST);
ssh1_write_pubkey(fp, &rkey);
freersakey(&rkey);
From 6cbca87a62683a0342b3bf9265dc833cf7f3e918 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 06:46:28 +0100
Subject: [PATCH 338/607] Try harder not to call connection_fatal twice.
If the server sends an SSH_MSG_DISCONNECT, then we call
connection_fatal(). But if the server closes the network connection,
then we call connection_fatal(). In situations where the former
happens, the latter happens too.
Currently, calling connection_fatal twice is especially bad on GTK
because all dialogs are now non-modal and an assertion fails in the
GTK front end when two fatal message boxes try to exist at the same
time (the register_dialog system finds that slot is already occupied).
But regardless of that, we'd rather not even _try_ to print two fatal
boxes, because even if the front end doesn't fail an assertion,
there's no guarantee that the _more useful_ one of the messages will
end up being displayed. So a better fix is to have ssh.c make a
sensible decision about which message is the helpful one - in this
case, the actual error message out of the SSH_MSG_DISCONNECT, rather
than the predictable fact of the connection having been slammed shut
immediately afterwards - and only pass that one to the front end in
the first place.
---
ssh.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 46 insertions(+), 1 deletion(-)
diff --git a/ssh.c b/ssh.c
index 012d7062..547ffb5b 100644
--- a/ssh.c
+++ b/ssh.c
@@ -930,6 +930,7 @@ struct ssh_tag {
int exitcode;
int close_expected;
int clean_exit;
+ int disconnect_message_seen;
tree234 *rportfwds, *portfwds;
@@ -1593,6 +1594,20 @@ static void ssh1_rdpkt(Ssh ssh)
BinarySource_INIT(st->pktin, st->pktin->body, st->pktin->length);
+ /*
+ * Mild layer violation: if the message is a DISCONNECT, we
+ * should unset the close_expected flag, because now we _do_
+ * expect the server to close the network connection
+ * afterwards. That way, the more informative connection_fatal
+ * message for the disconnect itself won't fight with 'Server
+ * unexpectedly closed network connection'.
+ */
+ if (st->pktin->type == SSH1_MSG_DISCONNECT) {
+ ssh->clean_exit = FALSE;
+ ssh->close_expected = TRUE;
+ ssh->disconnect_message_seen = TRUE;
+ }
+
pq_push(&ssh->pq_full, st->pktin);
queue_idempotent_callback(&ssh->pq_full_consumer);
}
@@ -2015,6 +2030,20 @@ static void ssh2_rdpkt(Ssh ssh)
BinarySource_INIT(st->pktin, st->pktin->body, st->pktin->length);
+ /*
+ * Mild layer violation: if the message is a DISCONNECT, we
+ * should unset the close_expected flag, because now we _do_
+ * expect the server to close the network connection
+ * afterwards. That way, the more informative connection_fatal
+ * message for the disconnect itself won't fight with 'Server
+ * unexpectedly closed network connection'.
+ */
+ if (st->pktin->type == SSH2_MSG_DISCONNECT) {
+ ssh->clean_exit = FALSE;
+ ssh->close_expected = TRUE;
+ ssh->disconnect_message_seen = TRUE;
+ }
+
pq_push(&ssh->pq_full, st->pktin);
queue_idempotent_callback(&ssh->pq_full_consumer);
if (st->pktin->type == SSH2_MSG_NEWKEYS) {
@@ -2079,6 +2108,20 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
BinarySource_INIT(st->pktin, st->pktin->body, st->pktin->length);
+ /*
+ * Mild layer violation: if the message is a DISCONNECT, we
+ * should unset the close_expected flag, because now we _do_
+ * expect the server to close the network connection
+ * afterwards. That way, the more informative connection_fatal
+ * message for the disconnect itself won't fight with 'Server
+ * unexpectedly closed network connection'.
+ */
+ if (st->pktin->type == SSH2_MSG_DISCONNECT) {
+ ssh->clean_exit = FALSE;
+ ssh->close_expected = TRUE;
+ ssh->disconnect_message_seen = TRUE;
+ }
+
pq_push(&ssh->pq_full, st->pktin);
queue_idempotent_callback(&ssh->pq_full_consumer);
}
@@ -3379,7 +3422,8 @@ static void ssh_process_incoming_data(void *ctx)
if (error_msg)
logevent(error_msg);
- if (!ssh->close_expected || !ssh->clean_exit)
+ if ((!ssh->close_expected || !ssh->clean_exit) &&
+ !ssh->disconnect_message_seen)
connection_fatal(ssh->frontend, "%s", error_msg);
}
}
@@ -11959,6 +12003,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->exitcode = -1;
ssh->close_expected = FALSE;
ssh->clean_exit = FALSE;
+ ssh->disconnect_message_seen = FALSE;
ssh->state = SSH_STATE_PREPACKET;
ssh->size_needed = FALSE;
ssh->eof_needed = FALSE;
From 7079cf06c8b814fb28d002247bcb22c478bc11a0 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 07:08:47 +0100
Subject: [PATCH 339/607] Outgoing packet logging: log the right amount of
data.
I must have introduced this bug yesterday when I rewrote the packet
censoring functions using BinarySource. The base pointer passed to
log_packet was pointing at the right place, but the accompanying
length was the gross rather than net one, as it were - it counted the
extra header data we're about to insert at the _start_ of the packet,
so log_packet() was trying to print that many extra bytes at the _end_
and overrunning its buffer.
---
ssh.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/ssh.c b/ssh.c
index 547ffb5b..77d3f582 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1492,8 +1492,7 @@ static void ssh1_log_outgoing_packet(Ssh ssh, const struct Packet *pkt)
log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12],
ssh1_pkt_type(pkt->data[12]),
- pkt->body, pkt->length,
- nblanks, blanks, NULL, 0, NULL);
+ src->data, src->len, nblanks, blanks, NULL, 0, NULL);
}
/*
@@ -1742,7 +1741,7 @@ static void ssh2_log_outgoing_packet(Ssh ssh, const struct Packet *pkt)
log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5],
ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]),
- pkt->body, pkt->length, nblanks, blanks,
+ src->data, src->len, nblanks, blanks,
&ssh->v2_outgoing_sequence,
pkt->downstream_id, pkt->additional_log_text);
}
From 2b54c86e7e043ebae6f056943bdffd039bfd0ba7 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 07:11:10 +0100
Subject: [PATCH 340/607] Stop calling ssh2_set_window in SSH-1!
This must have been a bug introduced during the SSH-2 connection
sharing rework. Apparently nobody's ever re-tested SSH-1 X forwarding
since then - until I did so yesterday in the course of testing my
enormous refactor of the packet unmarshalling code.
---
ssh.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/ssh.c b/ssh.c
index 77d3f582..24c17630 100644
--- a/ssh.c
+++ b/ssh.c
@@ -9456,7 +9456,9 @@ void sshfwd_x11_is_local(struct ssh_channel *c)
* exchange mode.
*/
c->u.x11.initial = FALSE;
- ssh2_set_window(c, ssh_is_simple(c->ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE);
+ if (c->ssh->version == 2)
+ ssh2_set_window(
+ c, ssh_is_simple(c->ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE);
}
/*
From 314c8f5270a8a880faaf30bb11ad3130350e6f55 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 07:37:45 +0100
Subject: [PATCH 341/607] Connection sharing: handle reply to
cancel-tcpip-forward.
This is another bug that must have been around since connection
sharing was introduced, and nobody noticed until I did some unusually
thorough testing yesterday.
When a sharing downstream asks to set up a remote port forwarding, we
pass through the "tcpip-forward" global request, and we also intercept
the reply so that we know that the forwarding has been set up (and
hence that we should be passing "forwarded-tcpip" channel opens for
that port to this downstream). To do that, we set the want-reply flag
in the version of the packet we pass to the server, even if it was
clear in downstream's version; and we also put an item on a queue
local to sshshare.c which reminds us what to do about the reply when
it comes back.
But when the downstream _cancels_ one of those forwardings, I wrote
the code for all parts of that process except adding that queue item.
I even wrote the code to _consume_ the queue item, but somehow I
completely forgot to generate one in the first place! So the enum
value GLOBREQ_CANCEL_TCPIP_FORWARD was declared, tested for, but never
actually assigned to anything.
---
sshshare.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/sshshare.c b/sshshare.c
index 5f0c01a1..75b6940c 100644
--- a/sshshare.c
+++ b/sshshare.c
@@ -1146,6 +1146,7 @@ void share_got_pkt_from_server(void *csv, int type,
case SSH2_MSG_REQUEST_SUCCESS:
case SSH2_MSG_REQUEST_FAILURE:
globreq = cs->globreq_head;
+ assert(globreq); /* should match the queue in ssh.c */
if (globreq->type == GLOBREQ_TCPIP_FORWARD) {
if (type == SSH2_MSG_REQUEST_FAILURE) {
share_remove_forwarding(cs, globreq->fwd);
@@ -1402,6 +1403,20 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
(cs->parent->ssh, cs->id, type, pkt, pktlen,
orig_wantreply ? NULL : "upstream added want_reply flag");
ssh_sharing_queue_global_request(cs->parent->ssh, cs);
+
+ /*
+ * And queue a globreq so that when the reply comes
+ * back we know to cancel it.
+ */
+ globreq = snew(struct share_globreq);
+ globreq->next = NULL;
+ if (cs->globreq_tail)
+ cs->globreq_tail->next = globreq;
+ else
+ cs->globreq_head = globreq;
+ globreq->fwd = fwd;
+ globreq->want_reply = orig_wantreply;
+ globreq->type = GLOBREQ_CANCEL_TCPIP_FORWARD;
}
sfree(host);
From 3f1f7c3ce765f3f60d23f7290910f8bfc5b05b0a Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 07:54:00 +0100
Subject: [PATCH 342/607] Remove downstream remote port forwardings in ssh.c
too.
Another piece of half-finished machinery that I can't have tested
properly when I set up connection sharing: I had the function
ssh_alloc_sharing_rportfwd which is how sshshare.c asks ssh.c to start
sending it channel-open requests for a given remote forwarded port,
but I had no companion function that removes one of those requests
again when a downstream remote port forwarding goes away (either by
mid-session cancel-tcpip-forward or by the whole downstream
disconnecting).
As a result, the _second_ attempt to set up the same remote port
forwarding, after a sharing downstream had done so once and then
stopped, would quietly fail.
---
ssh.c | 15 +++++++++++++++
ssh.h | 2 ++
sshshare.c | 8 ++++++++
3 files changed, 25 insertions(+)
diff --git a/ssh.c b/ssh.c
index 24c17630..e575fc99 100644
--- a/ssh.c
+++ b/ssh.c
@@ -5236,6 +5236,21 @@ int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
return TRUE;
}
+void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
+ void *share_ctx)
+{
+ struct ssh_rportfwd pf, *realpf;
+
+ assert(ssh->rportfwds);
+ pf.shost = dupstr(shost);
+ pf.sport = sport;
+ realpf = del234(ssh->rportfwds, &pf);
+ assert(realpf);
+ assert(realpf->share_ctx == share_ctx);
+ sfree(realpf->shost);
+ sfree(realpf);
+}
+
static void ssh_sharing_global_request_response(Ssh ssh, struct Packet *pktin,
void *ctx)
{
diff --git a/ssh.h b/ssh.h
index 228c7072..2905037a 100644
--- a/ssh.h
+++ b/ssh.h
@@ -38,6 +38,8 @@ unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx);
void ssh_delete_sharing_channel(Ssh ssh, unsigned localid);
int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
void *share_ctx);
+void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
+ void *share_ctx);
void ssh_sharing_queue_global_request(Ssh ssh, void *share_ctx);
struct X11FakeAuth *ssh_sharing_add_x11_display(Ssh ssh, int authtype,
void *share_cs,
diff --git a/sshshare.c b/sshshare.c
index 75b6940c..895c99c8 100644
--- a/sshshare.c
+++ b/sshshare.c
@@ -857,6 +857,8 @@ static void share_try_cleanup(struct ssh_sharing_connstate *cs)
"cleanup after downstream went away");
strbuf_free(packet);
+ ssh_remove_sharing_rportfwd(cs->parent->ssh,
+ fwd->host, fwd->port, cs);
share_remove_forwarding(cs, fwd);
i--; /* don't accidentally skip one as a result */
}
@@ -1392,6 +1394,12 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
"", 0, NULL);
}
} else {
+ /*
+ * Tell ssh.c to stop sending us channel-opens for
+ * this forwarding.
+ */
+ ssh_remove_sharing_rportfwd(cs->parent->ssh, host, port, cs);
+
/*
* Pass the cancel request on to the SSH server, but
* set want_reply even if it wasn't originally set, so
From ae3863679d83910fd69fbdd1627d92a61ff112c5 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 08:08:53 +0100
Subject: [PATCH 343/607] Give rsa_fingerprint() a new name and API.
It's an SSH-1 specific function, so it should have a name reflecting
that, and it didn't. Also it had one of those outdated APIs involving
passing it a client-allocated buffer and size. Now it has a sensible
name, and internally it constructs the output string using a strbuf
and returns it dynamically allocated.
---
cmdgen.c | 3 +--
pageant.c | 22 ++++++++++++----------
ssh.c | 14 ++++++--------
ssh.h | 2 +-
sshrsa.c | 23 +++++++++--------------
windows/winpgen.c | 25 ++++++++++---------------
windows/winpgnt.c | 11 +++++++----
7 files changed, 46 insertions(+), 54 deletions(-)
diff --git a/cmdgen.c b/cmdgen.c
index e2d1e75f..f4771e1f 100644
--- a/cmdgen.c
+++ b/cmdgen.c
@@ -1017,8 +1017,7 @@ int main(int argc, char **argv)
if (sshver == 1) {
assert(ssh1key);
- fingerprint = snewn(128, char);
- rsa_fingerprint(fingerprint, 128, ssh1key);
+ fingerprint = rsa_ssh1_fingerprint(ssh1key);
} else {
if (ssh2key) {
fingerprint = ssh2_fingerprint(ssh2key->alg,
diff --git a/pageant.c b/pageant.c
index 71256ff3..41a0c4ff 100644
--- a/pageant.c
+++ b/pageant.c
@@ -213,9 +213,9 @@ void pageant_handle_msg(BinarySink *bs,
int i;
struct RSAKey *rkey;
for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
- char fingerprint[128];
- rsa_fingerprint(fingerprint, sizeof(fingerprint), rkey);
+ char *fingerprint = rsa_ssh1_fingerprint(rkey);
plog(logctx, logfn, "returned key: %s", fingerprint);
+ sfree(fingerprint);
}
}
}
@@ -282,10 +282,11 @@ void pageant_handle_msg(BinarySink *bs,
}
if (logfn) {
- char fingerprint[128];
+ char *fingerprint;
reqkey.comment = NULL;
- rsa_fingerprint(fingerprint, sizeof(fingerprint), &reqkey);
+ fingerprint = rsa_ssh1_fingerprint(&reqkey);
plog(logctx, logfn, "requested key: %s", fingerprint);
+ sfree(fingerprint);
}
if ((key = find234(rsakeys, &reqkey, NULL)) == NULL) {
pageant_failure_msg(bs, "key not found", logctx, logfn);
@@ -386,9 +387,9 @@ void pageant_handle_msg(BinarySink *bs,
}
if (logfn) {
- char fingerprint[128];
- rsa_fingerprint(fingerprint, sizeof(fingerprint), key);
+ char *fingerprint = rsa_ssh1_fingerprint(key);
plog(logctx, logfn, "submitted key: %s", fingerprint);
+ sfree(fingerprint);
}
if (add234(rsakeys, key) == key) {
@@ -496,9 +497,9 @@ void pageant_handle_msg(BinarySink *bs,
}
if (logfn) {
- char fingerprint[128];
+ char *fingerprint;
reqkey.comment = NULL;
- rsa_fingerprint(fingerprint, sizeof(fingerprint), &reqkey);
+ fingerprint = rsa_ssh1_fingerprint(&reqkey);
plog(logctx, logfn, "unwanted key: %s", fingerprint);
}
@@ -1316,7 +1317,7 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
nkeys = toint(get_uint32(src));
for (i = 0; i < nkeys; i++) {
struct RSAKey rkey;
- char fingerprint[128];
+ char *fingerprint;
/* public blob and fingerprint */
memset(&rkey, 0, sizeof(rkey));
@@ -1330,7 +1331,7 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
return PAGEANT_ACTION_FAILURE;
}
- rsa_fingerprint(fingerprint, sizeof(fingerprint), &rkey);
+ fingerprint = rsa_ssh1_fingerprint(&rkey);
cbkey.blob = strbuf_new();
rsa_ssh1_public_blob(BinarySink_UPCAST(cbkey.blob), &rkey,
@@ -1341,6 +1342,7 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
strbuf_free(cbkey.blob);
freersakey(&rkey);
sfree(cbkey.comment);
+ sfree(fingerprint);
}
sfree(keylist);
diff --git a/ssh.c b/ssh.c
index e575fc99..641543d4 100644
--- a/ssh.c
+++ b/ssh.c
@@ -4130,13 +4130,10 @@ static void do_ssh1_login(void *vctx)
* Log the host key fingerprint.
*/
if (!get_err(pktin)) {
- char logmsg[80];
+ char *fingerprint = rsa_ssh1_fingerprint(&s->hostkey);
logevent("Host key fingerprint is:");
- strcpy(logmsg, " ");
- s->hostkey.comment = NULL;
- rsa_fingerprint(logmsg + strlen(logmsg),
- sizeof(logmsg) - strlen(logmsg), &s->hostkey);
- logevent(logmsg);
+ logeventf(ssh, " %s", fingerprint);
+ sfree(fingerprint);
}
ssh->v1_remote_protoflags = get_uint32(pktin);
@@ -4186,13 +4183,14 @@ static void do_ssh1_login(void *vctx)
* First format the key into a string.
*/
int len = rsastr_len(&s->hostkey);
- char fingerprint[100];
+ char *fingerprint;
char *keystr = snewn(len, char);
rsastr_fmt(keystr, &s->hostkey);
- rsa_fingerprint(fingerprint, sizeof(fingerprint), &s->hostkey);
+ fingerprint = rsa_ssh1_fingerprint(&s->hostkey);
/* First check against manually configured host keys. */
s->dlgret = verify_ssh_manual_host_key(ssh, fingerprint, NULL, NULL);
+ sfree(fingerprint);
if (s->dlgret == 0) { /* did not match */
bombout(("Host key did not appear in manually configured list"));
sfree(keystr);
diff --git a/ssh.h b/ssh.h
index 2905037a..d4ca6584 100644
--- a/ssh.h
+++ b/ssh.h
@@ -192,7 +192,7 @@ Bignum rsa_ssh1_decrypt(Bignum input, struct RSAKey *key);
void rsasanitise(struct RSAKey *key);
int rsastr_len(struct RSAKey *key);
void rsastr_fmt(char *str, struct RSAKey *key);
-void rsa_fingerprint(char *str, int len, struct RSAKey *key);
+char *rsa_ssh1_fingerprint(struct RSAKey *key);
int rsa_verify(struct RSAKey *key);
void rsa_ssh1_public_blob(BinarySink *bs, struct RSAKey *key,
RsaSsh1Order order);
diff --git a/sshrsa.c b/sshrsa.c
index 51396a5c..2aaaf2d3 100644
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -340,30 +340,25 @@ void rsastr_fmt(char *str, struct RSAKey *key)
* Generate a fingerprint string for the key. Compatible with the
* OpenSSH fingerprint code.
*/
-void rsa_fingerprint(char *str, int len, struct RSAKey *key)
+char *rsa_ssh1_fingerprint(struct RSAKey *key)
{
struct MD5Context md5c;
unsigned char digest[16];
- char buffer[16 * 3 + 40];
- int slen, i;
+ strbuf *out;
+ int i;
MD5Init(&md5c);
put_mp_ssh1(&md5c, key->modulus);
put_mp_ssh1(&md5c, key->exponent);
MD5Final(digest, &md5c);
- sprintf(buffer, "%d ", bignum_bitcount(key->modulus));
+ out = strbuf_new();
+ strbuf_catf(out, "%d ", bignum_bitcount(key->modulus));
for (i = 0; i < 16; i++)
- sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "",
- digest[i]);
- strncpy(str, buffer, len);
- str[len - 1] = '\0';
- slen = strlen(str);
- if (key->comment && slen < len - 1) {
- str[slen] = ' ';
- strncpy(str + slen + 1, key->comment, len - slen - 1);
- str[len - 1] = '\0';
- }
+ strbuf_catf(out, "%s%02x", i ? ":" : "", digest[i]);
+ if (key->comment)
+ strbuf_catf(out, " %s", key->comment);
+ return strbuf_to_str(out);
}
/*
diff --git a/windows/winpgen.c b/windows/winpgen.c
index edde9be2..bc68148f 100644
--- a/windows/winpgen.c
+++ b/windows/winpgen.c
@@ -734,8 +734,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
passphrase);
if (type == SSH_KEYTYPE_SSH1) {
- char buf[128];
- char *savecomment;
+ char *fingerprint, *savecomment;
state->ssh2 = FALSE;
state->commentptr = &state->key.comment;
@@ -746,11 +745,11 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
*/
savecomment = state->key.comment;
state->key.comment = NULL;
- rsa_fingerprint(buf, sizeof(buf),
- &state->key);
+ fingerprint = rsa_ssh1_fingerprint(&state->key);
state->key.comment = savecomment;
+ SetDlgItemText(hwnd, IDC_FINGERPRINT, fingerprint);
+ sfree(fingerprint);
- SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
/*
* Construct a decimal representation
* of the key, for pasting into
@@ -1406,7 +1405,7 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
* Now update the key controls with all the key data.
*/
{
- char *savecomment;
+ char *fp, *savecomment;
/*
* Blank passphrase, initially. This isn't dangerous,
* because we will warn (Are You Sure?) before allowing
@@ -1423,16 +1422,12 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
*/
savecomment = *state->commentptr;
*state->commentptr = NULL;
- if (state->ssh2) {
- char *fp;
+ if (state->ssh2)
fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
- SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
- sfree(fp);
- } else {
- char buf[128];
- rsa_fingerprint(buf, sizeof(buf), &state->key);
- SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
- }
+ else
+ fp = rsa_ssh1_fingerprint(&state->key);
+ SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
+ sfree(fp);
*state->commentptr = savecomment;
/*
* Construct a decimal representation of the key, for
diff --git a/windows/winpgnt.c b/windows/winpgnt.c
index 6b9f1e6c..43266440 100644
--- a/windows/winpgnt.c
+++ b/windows/winpgnt.c
@@ -290,14 +290,16 @@ void keylist_update(void)
if (keylist) {
SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
- char listentry[512], *p;
+ char *listentry, *fp, *p;
+
+ fp = rsa_ssh1_fingerprint(rkey);
+ listentry = dupprintf("ssh1\t%s", fp);
+ sfree(fp);
+
/*
* Replace two spaces in the fingerprint with tabs, for
* nice alignment in the box.
*/
- strcpy(listentry, "ssh1\t");
- p = listentry + strlen(listentry);
- rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
p = strchr(listentry, ' ');
if (p)
*p = '\t';
@@ -306,6 +308,7 @@ void keylist_update(void)
*p = '\t';
SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
0, (LPARAM) listentry);
+ sfree(listentry);
}
for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
char *listentry, *p;
From ff11e10d62ce4bd29f99015909f953adcb2abd43 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 08:12:57 +0100
Subject: [PATCH 344/607] Rename rsa_public_blob_len to mention SSH-1.
It's yet another function with an outdatedly vague name.
---
pageant.c | 2 +-
ssh.h | 2 +-
sshrsa.c | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/pageant.c b/pageant.c
index 41a0c4ff..170655d8 100644
--- a/pageant.c
+++ b/pageant.c
@@ -1073,7 +1073,7 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase,
}
/* Now skip over public blob */
if (type == SSH_KEYTYPE_SSH1) {
- int n = rsa_public_blob_len(p, keylistlen);
+ int n = rsa_ssh1_public_blob_len(p, keylistlen);
if (n < 0) {
*retstr = dupstr("Received broken key list from agent");
sfree(keylist);
diff --git a/ssh.h b/ssh.h
index d4ca6584..ec32d1df 100644
--- a/ssh.h
+++ b/ssh.h
@@ -196,7 +196,7 @@ char *rsa_ssh1_fingerprint(struct RSAKey *key);
int rsa_verify(struct RSAKey *key);
void rsa_ssh1_public_blob(BinarySink *bs, struct RSAKey *key,
RsaSsh1Order order);
-int rsa_public_blob_len(void *data, int maxlen);
+int rsa_ssh1_public_blob_len(void *data, int maxlen);
void freersakey(struct RSAKey *key);
typedef uint32 word32;
diff --git a/sshrsa.c b/sshrsa.c
index 2aaaf2d3..598894e1 100644
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -441,8 +441,8 @@ void rsa_ssh1_public_blob(BinarySink *bs, struct RSAKey *key,
}
}
-/* Given a public blob, determine its length. */
-int rsa_public_blob_len(void *data, int maxlen)
+/* Given an SSH-1 public key blob, determine its length. */
+int rsa_ssh1_public_blob_len(void *data, int maxlen)
{
BinarySource src[1];
From 7f56e1e36527e8ca56706dd149e973fff83c0f8a Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 08:23:07 +0100
Subject: [PATCH 345/607] Remove 'keystr' parameter in get_rsa_ssh1_pub.
This parameter returned a substring of the input, which was used for
two purposes. Firstly, it was used to hash the host and server keys
during the initial SSH-1 key setup phase; secondly, it was used to
check the keys in Pageant against the public key blob of a key
specified on the command line.
Unfortunately, those two purposes didn't agree! The first one needs
just the bare key modulus bytes (without even the SSH-1 mpint length
header); the second needs the entire key blob. So, actually, it seems
to have never worked in SSH-1 to say 'putty -i keyfile' and have PuTTY
find that key in Pageant and not have to ask for the passphrase to
decrypt the version on disk.
Fixed by removing that parameter completely, which simplifies all the
_other_ call sites, and replacing it by custom code in those two
places that each does the actually right thing.
---
cmdgen.c | 2 +-
marshal.h | 4 ++--
pageant.c | 8 ++++----
ssh.c | 23 ++++++++++++++++-------
ssh.h | 3 +--
sshpubk.c | 2 +-
sshrsa.c | 13 +------------
unix/uxpgnt.c | 3 +--
8 files changed, 27 insertions(+), 31 deletions(-)
diff --git a/cmdgen.c b/cmdgen.c
index f4771e1f..2f0bf7b0 100644
--- a/cmdgen.c
+++ b/cmdgen.c
@@ -813,7 +813,7 @@ int main(int argc, char **argv)
ret = rsa_ssh1_loadpub(infilename, BinarySink_UPCAST(blob),
&origcomment, &error);
BinarySource_BARE_INIT(src, blob->u, blob->len);
- get_rsa_ssh1_pub(src, ssh1key, NULL, RSA_SSH1_EXPONENT_FIRST);
+ get_rsa_ssh1_pub(src, ssh1key, RSA_SSH1_EXPONENT_FIRST);
strbuf_free(blob);
ssh1key->comment = dupstr(origcomment);
diff --git a/marshal.h b/marshal.h
index 3b7a089f..90bb94ff 100644
--- a/marshal.h
+++ b/marshal.h
@@ -243,8 +243,8 @@ struct BinarySource {
BinarySource_get_mp_ssh1(BinarySource_UPCAST(src))
#define get_mp_ssh2(src) \
BinarySource_get_mp_ssh2(BinarySource_UPCAST(src))
-#define get_rsa_ssh1_pub(src, rsa, keystr, order) \
- BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, keystr, order)
+#define get_rsa_ssh1_pub(src, rsa, order) \
+ BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, order)
#define get_rsa_ssh1_priv(src, rsa) \
BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa)
diff --git a/pageant.c b/pageant.c
index 170655d8..f9bdd668 100644
--- a/pageant.c
+++ b/pageant.c
@@ -264,7 +264,7 @@ void pageant_handle_msg(BinarySink *bs,
response = NULL;
memset(&reqkey, 0, sizeof(reqkey));
- get_rsa_ssh1_pub(msg, &reqkey, NULL, RSA_SSH1_EXPONENT_FIRST);
+ get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST);
challenge = get_mp_ssh1(msg);
session_id = get_data(msg, 16);
response_type = get_uint32(msg);
@@ -363,7 +363,7 @@ void pageant_handle_msg(BinarySink *bs,
key = snew(struct RSAKey);
memset(key, 0, sizeof(struct RSAKey));
- get_rsa_ssh1_pub(msg, key, NULL, RSA_SSH1_MODULUS_FIRST);
+ get_rsa_ssh1_pub(msg, key, RSA_SSH1_MODULUS_FIRST);
get_rsa_ssh1_priv(msg, key);
/* SSH-1 names p and q the other way round, i.e. we have
@@ -486,7 +486,7 @@ void pageant_handle_msg(BinarySink *bs,
plog(logctx, logfn, "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY");
- get_rsa_ssh1_pub(msg, &reqkey, NULL, RSA_SSH1_EXPONENT_FIRST);
+ get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST);
if (get_err(msg)) {
pageant_failure_msg(bs, "unable to decode request",
@@ -1321,7 +1321,7 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
/* public blob and fingerprint */
memset(&rkey, 0, sizeof(rkey));
- get_rsa_ssh1_pub(src, &rkey, NULL, RSA_SSH1_EXPONENT_FIRST);
+ get_rsa_ssh1_pub(src, &rkey, RSA_SSH1_EXPONENT_FIRST);
comment = get_string(src);
if (get_err(src)) {
diff --git a/ssh.c b/ssh.c
index 641543d4..dd96e7a8 100644
--- a/ssh.c
+++ b/ssh.c
@@ -4082,7 +4082,6 @@ static void do_ssh1_login(void *vctx)
int crLine;
int len;
unsigned char *rsabuf;
- ptrlen keystr1, keystr2;
unsigned long supported_ciphers_mask, supported_auths_mask;
int tried_publickey, tried_agent;
int tis_auth_refused, ccard_auth_refused;
@@ -4123,8 +4122,8 @@ static void do_ssh1_login(void *vctx)
pl = get_data(pktin, 8);
memcpy(s->cookie, pl.ptr, pl.len);
- get_rsa_ssh1_pub(pktin, &s->servkey, &s->keystr1, RSA_SSH1_EXPONENT_FIRST);
- get_rsa_ssh1_pub(pktin, &s->hostkey, &s->keystr2, RSA_SSH1_EXPONENT_FIRST);
+ get_rsa_ssh1_pub(pktin, &s->servkey, RSA_SSH1_EXPONENT_FIRST);
+ get_rsa_ssh1_pub(pktin, &s->hostkey, RSA_SSH1_EXPONENT_FIRST);
/*
* Log the host key fingerprint.
@@ -4153,8 +4152,13 @@ static void do_ssh1_login(void *vctx)
ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER;
MD5Init(&md5c);
- put_data(&md5c, s->keystr2.ptr, s->keystr2.len);
- put_data(&md5c, s->keystr1.ptr, s->keystr1.len);
+ {
+ int i;
+ for (i = (bignum_bitcount(s->hostkey.modulus) + 7) / 8; i-- ;)
+ put_byte(&md5c, bignum_byte(s->hostkey.modulus, i));
+ for (i = (bignum_bitcount(s->servkey.modulus) + 7) / 8; i-- ;)
+ put_byte(&md5c, bignum_byte(s->servkey.modulus, i));
+ }
put_data(&md5c, s->cookie, 8);
MD5Final(s->session_id, &md5c);
@@ -4496,15 +4500,20 @@ static void do_ssh1_login(void *vctx)
}
logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys);
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
- ptrlen keystr;
- get_rsa_ssh1_pub(s->asrc, &s->key, &keystr,
+ size_t start, end;
+ start = s->asrc->pos;
+ get_rsa_ssh1_pub(s->asrc, &s->key,
RSA_SSH1_EXPONENT_FIRST);
+ end = s->asrc->pos;
s->comment = get_string(s->asrc);
if (get_err(s->asrc)) {
logevent("Pageant key list packet was truncated");
break;
}
if (s->publickey_blob) {
+ ptrlen keystr = make_ptrlen(
+ (const char *)s->asrc->data + start, end - start);
+
if (keystr.len == s->publickey_blob->len &&
!memcmp(keystr.ptr, s->publickey_blob->s,
s->publickey_blob->len)) {
diff --git a/ssh.h b/ssh.h
index ec32d1df..b4267854 100644
--- a/ssh.h
+++ b/ssh.h
@@ -183,8 +183,7 @@ struct ec_point *ec_public(const Bignum privateKey, const struct ec_curve *curve
typedef enum { RSA_SSH1_EXPONENT_FIRST, RSA_SSH1_MODULUS_FIRST } RsaSsh1Order;
void BinarySource_get_rsa_ssh1_pub(
- BinarySource *src, struct RSAKey *result,
- ptrlen *keystr, RsaSsh1Order order);
+ BinarySource *src, struct RSAKey *result, RsaSsh1Order order);
void BinarySource_get_rsa_ssh1_priv(
BinarySource *src, struct RSAKey *rsa);
int rsa_ssh1_encrypt(unsigned char *data, int length, struct RSAKey *key);
diff --git a/sshpubk.c b/sshpubk.c
index e3a80b0c..6c63674b 100644
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -65,7 +65,7 @@ static int rsa_ssh1_load_main(FILE * fp, struct RSAKey *key, int pub_only,
goto end; /* reserved field nonzero, panic! */
/* Now the serious stuff. An ordinary SSH-1 public key. */
- get_rsa_ssh1_pub(src, key, NULL, RSA_SSH1_MODULUS_FIRST);
+ get_rsa_ssh1_pub(src, key, RSA_SSH1_MODULUS_FIRST);
/* Next, the comment field. */
comment = get_string(src);
diff --git a/sshrsa.c b/sshrsa.c
index 598894e1..9c99f99b 100644
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -11,31 +11,20 @@
#include "misc.h"
void BinarySource_get_rsa_ssh1_pub(
- BinarySource *src, struct RSAKey *rsa, ptrlen *keystr, RsaSsh1Order order)
+ BinarySource *src, struct RSAKey *rsa, RsaSsh1Order order)
{
- const unsigned char *start, *end;
unsigned bits;
Bignum e, m;
bits = get_uint32(src);
if (order == RSA_SSH1_EXPONENT_FIRST) {
e = get_mp_ssh1(src);
- start = get_ptr(src);
m = get_mp_ssh1(src);
- end = get_ptr(src);
} else {
- start = get_ptr(src);
m = get_mp_ssh1(src);
- end = get_ptr(src);
e = get_mp_ssh1(src);
}
- if (keystr) {
- start += (end-start >= 2 ? 2 : end-start);
- keystr->ptr = start;
- keystr->len = end - start;
- }
-
if (rsa) {
rsa->bits = bits;
rsa->exponent = e;
diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
index e3dfe318..c11bde03 100644
--- a/unix/uxpgnt.c
+++ b/unix/uxpgnt.c
@@ -707,8 +707,7 @@ void run_client(void)
BinarySource_BARE_INIT(src, key->blob->u, key->blob->len);
memset(&rkey, 0, sizeof(rkey));
rkey.comment = dupstr(key->comment);
- get_rsa_ssh1_pub(src, &rkey, NULL,
- RSA_SSH1_EXPONENT_FIRST);
+ get_rsa_ssh1_pub(src, &rkey, RSA_SSH1_EXPONENT_FIRST);
ssh1_write_pubkey(fp, &rkey);
freersakey(&rkey);
} else {
From 15bacbf630ca1b7ffc20170d776cfbae68d8eea0 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 08:37:17 +0100
Subject: [PATCH 346/607] Missing free.
---
pageant.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/pageant.c b/pageant.c
index f9bdd668..7738b68f 100644
--- a/pageant.c
+++ b/pageant.c
@@ -501,6 +501,7 @@ void pageant_handle_msg(BinarySink *bs,
reqkey.comment = NULL;
fingerprint = rsa_ssh1_fingerprint(&reqkey);
plog(logctx, logfn, "unwanted key: %s", fingerprint);
+ sfree(fingerprint);
}
key = find234(rsakeys, &reqkey, NULL);
From 06a14fe8b8b96bb1c64ffa578a4598b0eb388e63 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 12:58:05 +0100
Subject: [PATCH 347/607] Reorganise ssh_keyalg and use it as a vtable.
After Pavel Kryukov pointed out that I have to put _something_ in the
'ssh_key' structure, I thought of an actually useful thing to put
there: why not make it store a pointer to the ssh_keyalg structure?
Then ssh_key becomes a classoid - or perhaps 'traitoid' is a closer
analogy - in the same style as Socket and Plug. And just like Socket
and Plug, I've also arranged a system of wrapper macros that avoid the
need to mention the 'object' whose method you're invoking twice at
each call site.
The new vtable pointer directly replaces an existing field of struct
ec_key (which was usable by several different ssh_keyalgs, so it
already had to store a pointer to the currently active one), and also
replaces the 'alg' field of the ssh2_userkey structure that wraps up a
cryptographic key with its comment field.
I've also taken the opportunity to clean things up a bit in general:
most of the methods now have new and clearer names (e.g. you'd never
know that 'newkey' made a public-only key while 'createkey' made a
public+private key pair unless you went and looked it up, but now
they're called 'new_pub' and 'new_priv' you might be in with a
chance), and I've completely removed the openssh_private_npieces field
after realising that it was duplicating information that is actually
_more_ conveniently obtained by calling the new_priv_openssh method
(formerly openssh_createkey) and throwing away the result.
---
cmdgen.c | 28 ++++------
import.c | 131 ++++++++++++++++++++--------------------------
pageant.c | 44 ++++++++--------
ssh.c | 129 +++++++++++++++++++++------------------------
ssh.h | 67 ++++++++++++++----------
sshdss.c | 38 ++++++++------
sshdssg.c | 2 +
sshecc.c | 119 +++++++++++++++++++++--------------------
sshecdsag.c | 4 +-
sshpubk.c | 29 +++++-----
sshrsa.c | 40 +++++++-------
sshrsag.c | 2 +
windows/winpgen.c | 20 +++----
windows/winpgnt.c | 7 +--
14 files changed, 327 insertions(+), 333 deletions(-)
diff --git a/cmdgen.c b/cmdgen.c
index 2f0bf7b0..7cf920f3 100644
--- a/cmdgen.c
+++ b/cmdgen.c
@@ -248,7 +248,6 @@ int main(int argc, char **argv)
struct RSAKey *ssh1key = NULL;
strbuf *ssh2blob = NULL;
char *ssh2alg = NULL;
- const ssh_keyalg *ssh2algf = NULL;
char *old_passphrase = NULL, *new_passphrase = NULL;
int load_encrypted;
progfn_t progressfn = is_interactive() ? progress_update : no_progress;
@@ -722,22 +721,19 @@ int main(int argc, char **argv)
struct dss_key *dsskey = snew(struct dss_key);
dsa_generate(dsskey, bits, progressfn, &prog);
ssh2key = snew(struct ssh2_userkey);
- ssh2key->data = &dsskey->sshk;
- ssh2key->alg = &ssh_dss;
+ ssh2key->key = &dsskey->sshk;
ssh1key = NULL;
} else if (keytype == ECDSA) {
struct ec_key *ec = snew(struct ec_key);
ec_generate(ec, bits, progressfn, &prog);
ssh2key = snew(struct ssh2_userkey);
- ssh2key->data = &ec->sshk;
- ssh2key->alg = ec->signalg;
+ ssh2key->key = &ec->sshk;
ssh1key = NULL;
} else if (keytype == ED25519) {
struct ec_key *ec = snew(struct ec_key);
ec_edgenerate(ec, bits, progressfn, &prog);
ssh2key = snew(struct ssh2_userkey);
- ssh2key->data = &ec->sshk;
- ssh2key->alg = &ssh_ecdsa_ed25519;
+ ssh2key->key = &ec->sshk;
ssh1key = NULL;
} else {
struct RSAKey *rsakey = snew(struct RSAKey);
@@ -747,8 +743,7 @@ int main(int argc, char **argv)
ssh1key = rsakey;
} else {
ssh2key = snew(struct ssh2_userkey);
- ssh2key->data = &rsakey->sshk;
- ssh2key->alg = &ssh_rsa;
+ ssh2key->key = &rsakey->sshk;
}
}
progressfn(&prog, PROGFN_PROGRESS, INT_MAX, -1);
@@ -838,10 +833,10 @@ int main(int argc, char **argv)
ssh2blob = strbuf_new();
if (ssh2_userkey_loadpub(infilename, &ssh2alg, BinarySink_UPCAST(ssh2blob),
&origcomment, &error)) {
- ssh2algf = find_pubkey_alg(ssh2alg);
- if (ssh2algf)
- bits = ssh2algf->pubkey_bits(
- ssh2algf, make_ptrlen(ssh2blob->s, ssh2blob->len));
+ const ssh_keyalg *alg = find_pubkey_alg(ssh2alg);
+ if (alg)
+ bits = ssh_key_public_bits(
+ alg, make_ptrlen(ssh2blob->s, ssh2blob->len));
else
bits = -1;
} else {
@@ -995,7 +990,7 @@ int main(int argc, char **argv)
if (!ssh2blob) {
assert(ssh2key);
ssh2blob = strbuf_new();
- ssh2key->alg->public_blob(ssh2key->data, BinarySink_UPCAST(ssh2blob));
+ ssh_key_public_blob(ssh2key->key, BinarySink_UPCAST(ssh2blob));
}
ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment,
@@ -1020,8 +1015,7 @@ int main(int argc, char **argv)
fingerprint = rsa_ssh1_fingerprint(ssh1key);
} else {
if (ssh2key) {
- fingerprint = ssh2_fingerprint(ssh2key->alg,
- ssh2key->data);
+ fingerprint = ssh2_fingerprint(ssh2key->key);
} else {
assert(ssh2blob);
fingerprint = ssh2_fingerprint_blob(
@@ -1085,7 +1079,7 @@ int main(int argc, char **argv)
if (ssh1key)
freersakey(ssh1key);
if (ssh2key) {
- ssh2key->alg->freekey(ssh2key->data);
+ ssh_key_free(ssh2key->key);
sfree(ssh2key);
}
diff --git a/import.c b/import.c
index 0b2d271c..ef55b28e 100644
--- a/import.c
+++ b/import.c
@@ -502,6 +502,7 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
{
struct openssh_pem_key *key = load_openssh_pem_key(filename, errmsg_p);
struct ssh2_userkey *retkey;
+ const ssh_keyalg *alg;
BinarySource src[1];
int i, num_integers;
struct ssh2_userkey *retval = NULL;
@@ -661,19 +662,18 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
/* Construct the key */
retkey = snew(struct ssh2_userkey);
- retkey->alg = alg;
- put_stringz(blob, alg->name);
+ put_stringz(blob, alg->ssh_id);
put_stringz(blob, curve->name);
put_stringpl(blob, pubkey.data);
publen = blob->len;
put_mp_ssh2_from_string(blob, privkey.data.ptr, privkey.data.len);
- retkey->data = retkey->alg->createkey(
- retkey->alg, make_ptrlen(blob->u, publen),
+ retkey->key = ssh_key_new_priv(
+ alg, make_ptrlen(blob->u, publen),
make_ptrlen(blob->u + publen, blob->len - publen));
- if (!retkey->data) {
+ if (!retkey->key) {
sfree(retkey);
errmsg = "unable to create key data structure";
goto error;
@@ -740,12 +740,12 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
*/
assert(privptr > 0); /* should have bombed by now if not */
retkey = snew(struct ssh2_userkey);
- retkey->alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss);
- retkey->data = retkey->alg->createkey(
- retkey->alg, make_ptrlen(blob->u, privptr),
+ alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss);
+ retkey->key = ssh_key_new_priv(
+ alg, make_ptrlen(blob->u, privptr),
make_ptrlen(blob->u+privptr, blob->len-privptr));
- if (!retkey->data) {
+ if (!retkey->key) {
sfree(retkey);
errmsg = "unable to create key data structure";
goto error;
@@ -794,9 +794,9 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
* Fetch the key blobs.
*/
pubblob = strbuf_new();
- key->alg->public_blob(key->data, BinarySink_UPCAST(pubblob));
+ ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob));
privblob = strbuf_new();
- key->alg->private_blob(key->data, BinarySink_UPCAST(privblob));
+ ssh_key_private_blob(key->key, BinarySink_UPCAST(privblob));
spareblob = NULL;
outblob = strbuf_new();
@@ -805,7 +805,8 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
* Encode the OpenSSH key blob, and also decide on the header
* line.
*/
- if (key->alg == &ssh_rsa || key->alg == &ssh_dss) {
+ if (ssh_key_alg(key->key) == &ssh_rsa ||
+ ssh_key_alg(key->key) == &ssh_dss) {
strbuf *seq;
/*
@@ -815,7 +816,7 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
* bignums per key type and then construct the actual blob in
* common code after that.
*/
- if (key->alg == &ssh_rsa) {
+ if (ssh_key_alg(key->key) == &ssh_rsa) {
ptrlen n, e, d, p, q, iqmp, dmp1, dmq1;
Bignum bd, bp, bq, bdmp1, bdmq1;
@@ -911,11 +912,11 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
put_ber_id_len(outblob, 16, seq->len, ASN1_CONSTRUCTED);
put_data(outblob, seq->s, seq->len);
strbuf_free(seq);
- } else if (key->alg == &ssh_ecdsa_nistp256 ||
- key->alg == &ssh_ecdsa_nistp384 ||
- key->alg == &ssh_ecdsa_nistp521) {
+ } else if (ssh_key_alg(key->key) == &ssh_ecdsa_nistp256 ||
+ ssh_key_alg(key->key) == &ssh_ecdsa_nistp384 ||
+ ssh_key_alg(key->key) == &ssh_ecdsa_nistp521) {
const unsigned char *oid;
- struct ec_key *ec = FROMFIELD(key->data, struct ec_key, sshk);
+ struct ec_key *ec = FROMFIELD(key->key, struct ec_key, sshk);
int oidlen;
int pointlen;
strbuf *seq, *sub;
@@ -930,7 +931,7 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
* [1]
* BIT STRING (0x00 public key point)
*/
- oid = ec_alg_oid(key->alg, &oidlen);
+ oid = ec_alg_oid(ssh_key_alg(key->key), &oidlen);
pointlen = (ec->publicKey.curve->fieldBits + 7) / 8 * 2;
seq = strbuf_new();
@@ -1340,7 +1341,6 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
{
struct openssh_new_key *key = load_openssh_new_key(filename, errmsg_p);
struct ssh2_userkey *retkey = NULL;
- int i;
struct ssh2_userkey *retval = NULL;
const char *errmsg;
unsigned checkint;
@@ -1428,56 +1428,42 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
goto error;
}
- retkey = NULL;
- for (key_index = 0; key_index < key->nkeys; key_index++) {
- ptrlen keytype, thiskey, comment;
+ retkey = snew(struct ssh2_userkey);
+ retkey->key = NULL;
+ retkey->comment = NULL;
- /*
- * Read the key type, which will tell us how to scan over
- * the key to get to the next one.
- */
- keytype = get_string(src);
+ for (key_index = 0; key_index < key->nkeys; key_index++) {
+ ptrlen comment;
/*
- * Preliminary key type identification, and decide how
- * many pieces of key we expect to see. Currently
- * (conveniently) all key types can be seen as some number
- * of strings, so we just need to know how many of them to
- * skip over. (The numbers below exclude the key comment.)
+ * Identify the key type.
*/
- alg = find_pubkey_alg_len(keytype);
+ alg = find_pubkey_alg_len(get_string(src));
if (!alg) {
errmsg = "private key type not recognised\n";
goto error;
}
- thiskey.ptr = get_ptr(src);
-
/*
- * Skip over the pieces of key.
+ * Read the key. We have to do this even if it's not the one
+ * we want, because it's the only way to find out how much
+ * data to skip past to get to the next key in the file.
*/
- for (i = 0; i < alg->openssh_private_npieces; i++)
- get_string(src);
-
+ retkey->key = ssh_key_new_priv_openssh(alg, src);
if (get_err(src)) {
errmsg = "unable to read entire private key";
goto error;
}
-
- thiskey.len = (const char *)get_ptr(src) - (const char *)thiskey.ptr;
-
- if (key_index == key->key_wanted) {
- BinarySource src[1];
- BinarySource_BARE_INIT(src, thiskey.ptr, thiskey.len);
-
- retkey = snew(struct ssh2_userkey);
- retkey->comment = NULL;
- retkey->alg = alg;
- retkey->data = alg->openssh_createkey(alg, src);
- if (!retkey->data) {
- errmsg = "unable to create key data structure";
- goto error;
- }
+ if (!retkey->key) {
+ errmsg = "unable to create key data structure";
+ goto error;
+ }
+ if (key_index != key->key_wanted) {
+ /*
+ * If this isn't the key we're looking for, throw it away.
+ */
+ ssh_key_free(retkey->key);
+ retkey->key = NULL;
}
/*
@@ -1518,10 +1504,8 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
error:
if (retkey) {
sfree(retkey->comment);
- if (retkey->data) {
- assert(alg);
- alg->freekey(retkey->data);
- }
+ if (retkey->key)
+ ssh_key_free(retkey->key);
sfree(retkey);
}
smemclr(key->keyblob, key->keyblob_size);
@@ -1547,9 +1531,9 @@ int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
* Fetch the key blobs and find out the lengths of things.
*/
pubblob = strbuf_new();
- key->alg->public_blob(key->data, BinarySink_UPCAST(pubblob));
+ ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob));
privblob = strbuf_new();
- key->alg->openssh_fmtkey(key->data, BinarySink_UPCAST(privblob));
+ ssh_key_openssh_blob(key->key, BinarySink_UPCAST(privblob));
/*
* Construct the cleartext version of the blob.
@@ -1597,7 +1581,7 @@ int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
/* Private key. The main private blob goes inline, with no string
* wrapper. */
- put_stringz(cpblob, key->alg->name);
+ put_stringz(cpblob, ssh_key_ssh_id(key->key));
put_data(cpblob, privblob->s, privblob->len);
/* Comment. */
@@ -1669,11 +1653,11 @@ int openssh_auto_write(const Filename *filename, struct ssh2_userkey *key,
* assume that anything not in that fixed list is newer, and hence
* will use the new format.
*/
- if (key->alg == &ssh_dss ||
- key->alg == &ssh_rsa ||
- key->alg == &ssh_ecdsa_nistp256 ||
- key->alg == &ssh_ecdsa_nistp384 ||
- key->alg == &ssh_ecdsa_nistp521)
+ if (ssh_key_alg(key->key) == &ssh_dss ||
+ ssh_key_alg(key->key) == &ssh_rsa ||
+ ssh_key_alg(key->key) == &ssh_ecdsa_nistp256 ||
+ ssh_key_alg(key->key) == &ssh_ecdsa_nistp384 ||
+ ssh_key_alg(key->key) == &ssh_ecdsa_nistp521)
return openssh_pem_write(filename, key, passphrase);
else
return openssh_new_write(filename, key, passphrase);
@@ -2117,7 +2101,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
/*
* Now we break down into RSA versus DSA. In either case we'll
* construct public and private blobs in our own format, and
- * end up feeding them to alg->createkey().
+ * end up feeding them to ssh_key_new_priv().
*/
blob = strbuf_new();
if (type == RSA) {
@@ -2173,11 +2157,10 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
}
retkey = snew(struct ssh2_userkey);
- retkey->alg = alg;
- retkey->data = alg->createkey(
+ retkey->key = ssh_key_new_priv(
alg, make_ptrlen(blob->u, publen),
make_ptrlen(blob->u + publen, blob->len - publen));
- if (!retkey->data) {
+ if (!retkey->key) {
sfree(retkey);
errmsg = "unable to create key data structure";
goto error;
@@ -2216,16 +2199,16 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
* Fetch the key blobs.
*/
pubblob = strbuf_new();
- key->alg->public_blob(key->data, BinarySink_UPCAST(pubblob));
+ ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob));
privblob = strbuf_new();
- key->alg->private_blob(key->data, BinarySink_UPCAST(privblob));
+ ssh_key_private_blob(key->key, BinarySink_UPCAST(privblob));
outblob = NULL;
/*
* Find the sequence of integers to be encoded into the OpenSSH
* key blob, and also decide on the header line.
*/
- if (key->alg == &ssh_rsa) {
+ if (ssh_key_alg(key->key) == &ssh_rsa) {
ptrlen n, e, d, p, q, iqmp;
/*
@@ -2254,7 +2237,7 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
nnumbers = 6;
initial_zero = 0;
type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}";
- } else if (key->alg == &ssh_dss) {
+ } else if (ssh_key_alg(key->key) == &ssh_dss) {
ptrlen p, q, g, y, x;
/*
diff --git a/pageant.c b/pageant.c
index 7738b68f..5c3efb13 100644
--- a/pageant.c
+++ b/pageant.c
@@ -89,7 +89,7 @@ static int cmpkeys_ssh2_asymm(void *av, void *bv)
* Compare purely by public blob.
*/
bblob = strbuf_new();
- b->alg->public_blob(b->data, BinarySink_UPCAST(bblob));
+ ssh_key_public_blob(b->key, BinarySink_UPCAST(bblob));
c = 0;
for (i = 0; i < ablob->len && i < bblob->len; i++) {
@@ -123,7 +123,7 @@ static int cmpkeys_ssh2(void *av, void *bv)
int toret;
ablob = strbuf_new();
- a->alg->public_blob(a->data, BinarySink_UPCAST(ablob));
+ ssh_key_public_blob(a->key, BinarySink_UPCAST(ablob));
apl.ptr = ablob->u;
apl.len = ablob->len;
toret = cmpkeys_ssh2_asymm(&apl, bv);
@@ -151,7 +151,7 @@ void pageant_make_keylist2(BinarySink *bs)
put_uint32(bs, count234(ssh2keys));
for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
strbuf *blob = strbuf_new();
- key->alg->public_blob(key->data, BinarySink_UPCAST(blob));
+ ssh_key_public_blob(key->key, BinarySink_UPCAST(blob));
put_stringsb(bs, blob);
put_stringz(bs, key->comment);
}
@@ -235,8 +235,7 @@ void pageant_handle_msg(BinarySink *bs,
int i;
struct ssh2_userkey *skey;
for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
- char *fingerprint = ssh2_fingerprint(skey->alg,
- skey->data);
+ char *fingerprint = ssh2_fingerprint(skey->key);
plog(logctx, logfn, "returned key: %s %s",
fingerprint, skey->comment);
sfree(fingerprint);
@@ -343,8 +342,8 @@ void pageant_handle_msg(BinarySink *bs,
put_byte(bs, SSH2_AGENT_SIGN_RESPONSE);
signature = strbuf_new();
- key->alg->sign(key->data, sigdata.ptr, sigdata.len,
- BinarySink_UPCAST(signature));
+ ssh_key_sign(key->key, sigdata.ptr, sigdata.len,
+ BinarySink_UPCAST(signature));
put_stringsb(bs, signature);
plog(logctx, logfn, "reply: SSH2_AGENT_SIGN_RESPONSE");
@@ -416,24 +415,25 @@ void pageant_handle_msg(BinarySink *bs,
*/
{
struct ssh2_userkey *key = NULL;
- ptrlen alg;
+ ptrlen algpl;
+ const ssh_keyalg *alg;
plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY");
- alg = get_string(msg);
+ algpl = get_string(msg);
key = snew(struct ssh2_userkey);
- key->data = NULL;
+ key->key = NULL;
key->comment = NULL;
- key->alg = find_pubkey_alg_len(alg);
- if (!key->alg) {
+ alg = find_pubkey_alg_len(algpl);
+ if (!alg) {
pageant_failure_msg(bs, "algorithm unknown", logctx, logfn);
goto add2_cleanup;
}
- key->data = key->alg->openssh_createkey(key->alg, msg);
+ key->key = ssh_key_new_priv_openssh(alg, msg);
- if (!key->data) {
+ if (!key->key) {
pageant_failure_msg(bs, "key setup failed", logctx, logfn);
goto add2_cleanup;
}
@@ -447,7 +447,7 @@ void pageant_handle_msg(BinarySink *bs,
}
if (logfn) {
- char *fingerprint = ssh2_fingerprint(key->alg, key->data);
+ char *fingerprint = ssh2_fingerprint(key->key);
plog(logctx, logfn, "submitted key: %s %s",
fingerprint, key->comment);
sfree(fingerprint);
@@ -467,8 +467,8 @@ void pageant_handle_msg(BinarySink *bs,
add2_cleanup:
if (key) {
- if (key->data)
- key->alg->freekey(key->data);
+ if (key->key)
+ ssh_key_free(key->key);
if (key->comment)
sfree(key->comment);
sfree(key);
@@ -558,7 +558,7 @@ void pageant_handle_msg(BinarySink *bs,
del234(ssh2keys, key);
keylist_update();
- key->alg->freekey(key->data);
+ ssh_key_free(key->key);
sfree(key->comment);
sfree(key);
put_byte(bs, SSH_AGENT_SUCCESS);
@@ -599,7 +599,7 @@ void pageant_handle_msg(BinarySink *bs,
while ((skey = index234(ssh2keys, 0)) != NULL) {
del234(ssh2keys, skey);
- skey->alg->freekey(skey->data);
+ ssh_key_free(skey->key);
sfree(skey->comment);
sfree(skey);
}
@@ -1274,8 +1274,8 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase,
request = strbuf_new_for_agent_query();
put_byte(request, SSH2_AGENTC_ADD_IDENTITY);
- put_stringz(request, skey->alg->name);
- skey->alg->openssh_fmtkey(skey->data, BinarySink_UPCAST(request));
+ put_stringz(request, ssh_key_ssh_id(skey->key));
+ ssh_key_openssh_blob(skey->key, BinarySink_UPCAST(request));
put_stringz(request, skey->comment);
agent_query_synchronous(request, &vresponse, &resplen);
strbuf_free(request);
@@ -1291,7 +1291,7 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase,
sfree(response);
} else {
if (!pageant_add_ssh2_key(skey)) {
- skey->alg->freekey(skey->data);
+ ssh_key_free(skey->key);
sfree(skey); /* already present, don't waste RAM */
}
}
diff --git a/ssh.c b/ssh.c
index dd96e7a8..17202e85 100644
--- a/ssh.c
+++ b/ssh.c
@@ -903,7 +903,7 @@ struct ssh_tag {
const struct ssh_compress *cscomp, *sccomp;
void *cs_comp_ctx, *sc_comp_ctx;
const struct ssh_kex *kex;
- const ssh_keyalg *hostkey;
+ const ssh_keyalg *hostkey_alg;
char *hostkey_str; /* string representation, for easy checking in rekeys */
unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN];
int v2_session_id_len;
@@ -4008,9 +4008,7 @@ static void ssh_disconnect(Ssh ssh, const char *client_reason,
sfree(error);
}
-int verify_ssh_manual_host_key(Ssh ssh, const char *fingerprint,
- const ssh_keyalg *ssh2keytype,
- void *ssh2keydata)
+int verify_ssh_manual_host_key(Ssh ssh, const char *fingerprint, ssh_key *key)
{
if (!conf_get_str_nthstrkey(ssh->conf, CONF_ssh_manual_hostkeys, 0)) {
return -1; /* no manual keys configured */
@@ -4035,7 +4033,7 @@ int verify_ssh_manual_host_key(Ssh ssh, const char *fingerprint,
return 1; /* success */
}
- if (ssh2keydata) {
+ if (key) {
/*
* Construct the base64-encoded public key blob and see if
* that's listed.
@@ -4044,7 +4042,7 @@ int verify_ssh_manual_host_key(Ssh ssh, const char *fingerprint,
char *base64blob;
int atoms, i;
binblob = strbuf_new();
- ssh2keytype->public_blob(ssh2keydata, BinarySink_UPCAST(binblob));
+ ssh_key_public_blob(key, BinarySink_UPCAST(binblob));
atoms = (binblob->len + 2) / 3;
base64blob = snewn(atoms * 4 + 1, char);
for (i = 0; i < atoms; i++)
@@ -4193,7 +4191,7 @@ static void do_ssh1_login(void *vctx)
fingerprint = rsa_ssh1_fingerprint(&s->hostkey);
/* First check against manually configured host keys. */
- s->dlgret = verify_ssh_manual_host_key(ssh, fingerprint, NULL, NULL);
+ s->dlgret = verify_ssh_manual_host_key(ssh, fingerprint, NULL);
sfree(fingerprint);
if (s->dlgret == 0) { /* did not match */
bombout(("Host key did not appear in manually configured list"));
@@ -6349,7 +6347,7 @@ static int ssh_transient_hostkey_cache_cmp(void *av, void *bv)
const struct ssh_transient_hostkey_cache_entry
*a = (const struct ssh_transient_hostkey_cache_entry *)av,
*b = (const struct ssh_transient_hostkey_cache_entry *)bv;
- return strcmp(a->alg->name, b->alg->name);
+ return strcmp(a->alg->ssh_id, b->alg->ssh_id);
}
static int ssh_transient_hostkey_cache_find(void *av, void *bv)
@@ -6357,7 +6355,7 @@ static int ssh_transient_hostkey_cache_find(void *av, void *bv)
const ssh_keyalg *aalg = (const ssh_keyalg *)av;
const struct ssh_transient_hostkey_cache_entry
*b = (const struct ssh_transient_hostkey_cache_entry *)bv;
- return strcmp(aalg->name, b->alg->name);
+ return strcmp(aalg->ssh_id, b->alg->ssh_id);
}
static void ssh_init_transient_hostkey_store(Ssh ssh)
@@ -6376,35 +6374,33 @@ static void ssh_cleanup_transient_hostkey_store(Ssh ssh)
freetree234(ssh->transient_hostkey_cache);
}
-static void ssh_store_transient_hostkey(
- Ssh ssh, const ssh_keyalg *alg, ssh_key *key)
+static void ssh_store_transient_hostkey(Ssh ssh, ssh_key *key)
{
struct ssh_transient_hostkey_cache_entry *ent, *retd;
- if ((ent = find234(ssh->transient_hostkey_cache, (void *)alg,
+ if ((ent = find234(ssh->transient_hostkey_cache, (void *)ssh_key_alg(key),
ssh_transient_hostkey_cache_find)) != NULL) {
strbuf_free(ent->pub_blob);
sfree(ent);
}
ent = snew(struct ssh_transient_hostkey_cache_entry);
- ent->alg = alg;
+ ent->alg = ssh_key_alg(key);
ent->pub_blob = strbuf_new();
- alg->public_blob(key, BinarySink_UPCAST(ent->pub_blob));
+ ssh_key_public_blob(key, BinarySink_UPCAST(ent->pub_blob));
retd = add234(ssh->transient_hostkey_cache, ent);
assert(retd == ent);
}
-static int ssh_verify_transient_hostkey(
- Ssh ssh, const ssh_keyalg *alg, ssh_key *key)
+static int ssh_verify_transient_hostkey(Ssh ssh, ssh_key *key)
{
struct ssh_transient_hostkey_cache_entry *ent;
int toret = FALSE;
- if ((ent = find234(ssh->transient_hostkey_cache, (void *)alg,
+ if ((ent = find234(ssh->transient_hostkey_cache, (void *)ssh_key_alg(key),
ssh_transient_hostkey_cache_find)) != NULL) {
strbuf *this_blob = strbuf_new();
- alg->public_blob(key, BinarySink_UPCAST(this_blob));
+ ssh_key_public_blob(key, BinarySink_UPCAST(this_blob));
if (this_blob->len == ent->pub_blob->len &&
!memcmp(this_blob->s, ent->pub_blob->s,
@@ -6726,9 +6722,9 @@ static void do_ssh2_transport(void *vctx)
if (hostkey_algs[j].id != s->preferred_hk[i])
continue;
if (have_ssh_host_key(ssh->savedhost, ssh->savedport,
- hostkey_algs[j].alg->keytype)) {
+ hostkey_algs[j].alg->cache_id)) {
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
- hostkey_algs[j].alg->name);
+ hostkey_algs[j].alg->ssh_id);
alg->u.hk.hostkey = hostkey_algs[j].alg;
alg->u.hk.warn = warn;
}
@@ -6742,7 +6738,7 @@ static void do_ssh2_transport(void *vctx)
if (hostkey_algs[j].id != s->preferred_hk[i])
continue;
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
- hostkey_algs[j].alg->name);
+ hostkey_algs[j].alg->ssh_id);
alg->u.hk.hostkey = hostkey_algs[j].alg;
alg->u.hk.warn = warn;
}
@@ -6770,7 +6766,7 @@ static void do_ssh2_transport(void *vctx)
continue;
if (ssh_have_transient_hostkey(ssh, hostkey_algs[j].alg)) {
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
- hostkey_algs[j].alg->name);
+ hostkey_algs[j].alg->ssh_id);
alg->u.hk.hostkey = hostkey_algs[j].alg;
alg->u.hk.warn = warn;
}
@@ -6787,8 +6783,8 @@ static void do_ssh2_transport(void *vctx)
*/
assert(ssh->kex);
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
- ssh->hostkey->name);
- alg->u.hk.hostkey = ssh->hostkey;
+ ssh->hostkey_alg->ssh_id);
+ alg->u.hk.hostkey = ssh->hostkey_alg;
alg->u.hk.warn = FALSE;
}
if (s->can_gssapi_keyex) {
@@ -6907,7 +6903,7 @@ static void do_ssh2_transport(void *vctx)
crStopV;
}
ssh->kex = NULL;
- ssh->hostkey = NULL;
+ ssh->hostkey_alg = NULL;
s->cscipher_tobe = NULL;
s->sccipher_tobe = NULL;
s->csmac_tobe = NULL;
@@ -6969,7 +6965,7 @@ static void do_ssh2_transport(void *vctx)
if (alg->u.hk.hostkey == NULL &&
ssh->kex->main_type != KEXTYPE_GSS)
continue;
- ssh->hostkey = alg->u.hk.hostkey;
+ ssh->hostkey_alg = alg->u.hk.hostkey;
s->warn_hk = alg->u.hk.warn;
} else if (i == KEXLIST_CSCIPHER) {
s->cscipher_tobe = alg->u.cipher.cipher;
@@ -7019,11 +7015,11 @@ static void do_ssh2_transport(void *vctx)
ssh->n_uncert_hostkeys = 0;
for (j = 0; j < lenof(hostkey_algs); j++) {
- if (hostkey_algs[j].alg != ssh->hostkey &&
- in_commasep_string(hostkey_algs[j].alg->name,
+ if (hostkey_algs[j].alg != ssh->hostkey_alg &&
+ in_commasep_string(hostkey_algs[j].alg->ssh_id,
str.ptr, str.len) &&
!have_ssh_host_key(ssh->savedhost, ssh->savedport,
- hostkey_algs[j].alg->keytype)) {
+ hostkey_algs[j].alg->cache_id)) {
ssh->uncert_hostkeys[ssh->n_uncert_hostkeys++] = j;
}
}
@@ -7096,21 +7092,21 @@ static void do_ssh2_transport(void *vctx)
if (betteralgs) {
char *old_ba = betteralgs;
betteralgs = dupcat(betteralgs, ",",
- hktype->alg->name,
+ hktype->alg->ssh_id,
(const char *)NULL);
sfree(old_ba);
} else {
- betteralgs = dupstr(hktype->alg->name);
+ betteralgs = dupstr(hktype->alg->ssh_id);
}
}
}
if (betteralgs) {
- s->dlgret = askhk(ssh->frontend, ssh->hostkey->name,
+ s->dlgret = askhk(ssh->frontend, ssh->hostkey_alg->ssh_id,
betteralgs, ssh_dialog_callback, ssh);
sfree(betteralgs);
} else {
s->dlgret = askalg(ssh->frontend, "host key type",
- ssh->hostkey->name,
+ ssh->hostkey_alg->ssh_id,
ssh_dialog_callback, ssh);
}
if (s->dlgret < 0) {
@@ -7257,7 +7253,7 @@ static void do_ssh2_transport(void *vctx)
}
set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
s->hostkeydata = get_string(pktin);
- s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata);
+ s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
s->f = get_mp_ssh2(pktin);
s->sigdata = get_string(pktin);
if (get_err(pktin)) {
@@ -7328,7 +7324,7 @@ static void do_ssh2_transport(void *vctx)
s->hostkeydata = get_string(pktin);
put_stringpl(ssh->exhash_bs, s->hostkeydata);
- s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata);
+ s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
{
strbuf *pubpoint = strbuf_new();
@@ -7530,9 +7526,9 @@ static void do_ssh2_transport(void *vctx)
break;
case SSH2_MSG_KEXGSS_HOSTKEY:
s->hostkeydata = get_string(pktin);
- if (ssh->hostkey) {
- s->hkey = ssh->hostkey->newkey(ssh->hostkey,
- s->hostkeydata);
+ if (ssh->hostkey_alg) {
+ s->hkey = ssh_key_new_pub(ssh->hostkey_alg,
+ s->hostkeydata);
put_string(ssh->exhash_bs,
s->hostkeydata.ptr, s->hostkeydata.len);
}
@@ -7626,7 +7622,7 @@ static void do_ssh2_transport(void *vctx)
s->hostkeydata = get_string(pktin);
put_stringpl(ssh->exhash_bs, s->hostkeydata);
- s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata);
+ s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
rsakeydata = get_string(pktin);
@@ -7765,7 +7761,7 @@ static void do_ssh2_transport(void *vctx)
crStopV;
}
- if (!ssh->hostkey->verifysig(
+ if (!ssh_key_verify(
s->hkey, s->sigdata,
make_ptrlen(s->exchange_hash, ssh->kex->hash->hlen))) {
#ifndef FUZZING
@@ -7776,8 +7772,7 @@ static void do_ssh2_transport(void *vctx)
}
}
- s->keystr = (ssh->hostkey && s->hkey ?
- ssh->hostkey->fmtkey(s->hkey) : NULL);
+ s->keystr = (s->hkey ? ssh_key_cache_str(s->hkey) : NULL);
#ifndef NO_GSSAPI
if (ssh->gss_kex_used) {
/*
@@ -7792,12 +7787,12 @@ static void do_ssh2_transport(void *vctx)
* host key, store it.
*/
if (s->hkey) {
- s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
+ s->fingerprint = ssh2_fingerprint(s->hkey);
logevent("GSS kex provided fallback host key:");
logevent(s->fingerprint);
sfree(s->fingerprint);
s->fingerprint = NULL;
- ssh_store_transient_hostkey(ssh, ssh->hostkey, s->hkey);
+ ssh_store_transient_hostkey(ssh, s->hkey);
} else if (!ssh_have_any_transient_hostkey(ssh)) {
/*
* But if it didn't, then we currently have no
@@ -7812,7 +7807,7 @@ static void do_ssh2_transport(void *vctx)
* startup, and only add the key to the transient
* cache.
*/
- if (ssh->hostkey) {
+ if (ssh->hostkey_alg) {
s->need_gss_transient_hostkey = TRUE;
} else {
/*
@@ -7850,15 +7845,14 @@ static void do_ssh2_transport(void *vctx)
* triggered on purpose to populate the transient cache.
*/
assert(s->hkey); /* only KEXTYPE_GSS lets this be null */
- s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
+ s->fingerprint = ssh2_fingerprint(s->hkey);
if (s->need_gss_transient_hostkey) {
logevent("Post-GSS rekey provided fallback host key:");
logevent(s->fingerprint);
- ssh_store_transient_hostkey(ssh, ssh->hostkey, s->hkey);
+ ssh_store_transient_hostkey(ssh, s->hkey);
s->need_gss_transient_hostkey = FALSE;
- } else if (!ssh_verify_transient_hostkey(
- ssh, ssh->hostkey, s->hkey)) {
+ } else if (!ssh_verify_transient_hostkey(ssh, s->hkey)) {
logevent("Non-GSS rekey after initial GSS kex "
"used host key:");
logevent(s->fingerprint);
@@ -7878,7 +7872,7 @@ static void do_ssh2_transport(void *vctx)
int i, j, nkeys = 0;
char *list = NULL;
for (i = 0; i < lenof(hostkey_algs); i++) {
- if (hostkey_algs[i].alg == ssh->hostkey)
+ if (hostkey_algs[i].alg == ssh->hostkey_alg)
continue;
for (j = 0; j < ssh->n_uncert_hostkeys; j++)
@@ -7889,9 +7883,9 @@ static void do_ssh2_transport(void *vctx)
char *newlist;
if (list)
newlist = dupprintf("%s/%s", list,
- hostkey_algs[i].alg->name);
+ hostkey_algs[i].alg->ssh_id);
else
- newlist = dupprintf("%s", hostkey_algs[i].alg->name);
+ newlist = dupprintf("%s", hostkey_algs[i].alg->ssh_id);
sfree(list);
list = newlist;
nkeys++;
@@ -7911,12 +7905,11 @@ static void do_ssh2_transport(void *vctx)
* Authenticate remote host: verify host key. (We've already
* checked the signature of the exchange hash.)
*/
- s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
+ s->fingerprint = ssh2_fingerprint(s->hkey);
logevent("Host key fingerprint is:");
logevent(s->fingerprint);
/* First check against manually configured host keys. */
- s->dlgret = verify_ssh_manual_host_key(ssh, s->fingerprint,
- ssh->hostkey, s->hkey);
+ s->dlgret = verify_ssh_manual_host_key(ssh, s->fingerprint, s->hkey);
if (s->dlgret == 0) { /* did not match */
bombout(("Host key did not appear in manually configured list"));
crStopV;
@@ -7924,8 +7917,8 @@ static void do_ssh2_transport(void *vctx)
ssh_set_frozen(ssh, 1);
s->dlgret = verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport,
- ssh->hostkey->keytype, s->keystr,
- s->fingerprint,
+ ssh_key_cache_id(s->hkey),
+ s->keystr, s->fingerprint,
ssh_dialog_callback, ssh);
#ifdef FUZZING
s->dlgret = 1;
@@ -7950,12 +7943,12 @@ static void do_ssh2_transport(void *vctx)
ssh->hostkey_str = s->keystr;
s->keystr = NULL;
} else if (ssh->cross_certifying) {
- s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
+ s->fingerprint = ssh2_fingerprint(s->hkey);
logevent("Storing additional host key for this host:");
logevent(s->fingerprint);
sfree(s->fingerprint);
store_host_key(ssh->savedhost, ssh->savedport,
- ssh->hostkey->keytype, s->keystr);
+ ssh_key_cache_id(s->hkey), s->keystr);
ssh->cross_certifying = FALSE;
/*
* Don't forget to store the new key as the one we'll be
@@ -7979,7 +7972,7 @@ static void do_ssh2_transport(void *vctx)
}
sfree(s->keystr);
if (s->hkey) {
- ssh->hostkey->freekey(s->hkey);
+ ssh_key_free(s->hkey);
s->hkey = NULL;
}
@@ -10505,9 +10498,9 @@ static void do_ssh2_userauth(void *vctx)
/* service requested */
put_stringz(s->pktout, "publickey"); /* method */
put_bool(s->pktout, TRUE); /* signature follows */
- put_stringz(s->pktout, key->alg->name);
+ put_stringz(s->pktout, ssh_key_ssh_id(key->key));
pkblob = strbuf_new();
- key->alg->public_blob(key->data, BinarySink_UPCAST(pkblob));
+ ssh_key_public_blob(key->key, BinarySink_UPCAST(pkblob));
put_string(s->pktout, pkblob->s, pkblob->len);
/*
@@ -10529,8 +10522,8 @@ static void do_ssh2_userauth(void *vctx)
put_data(sigdata, s->pktout->data + 5,
s->pktout->length - 5);
sigblob = strbuf_new();
- key->alg->sign(key->data, sigdata->s, sigdata->len,
- BinarySink_UPCAST(sigblob));
+ ssh_key_sign(key->key, sigdata->s, sigdata->len,
+ BinarySink_UPCAST(sigblob));
strbuf_free(sigdata);
ssh2_add_sigblob(ssh, s->pktout, pkblob->s, pkblob->len,
sigblob->s, sigblob->len);
@@ -10540,7 +10533,7 @@ static void do_ssh2_userauth(void *vctx)
ssh2_pkt_send(ssh, s->pktout);
logevent("Sent public key signature");
s->type = AUTH_TYPE_PUBLICKEY;
- key->alg->freekey(key->data);
+ ssh_key_free(key->key);
sfree(key->comment);
sfree(key);
}
@@ -12021,7 +12014,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->sc_comp_ctx = NULL;
ssh->kex = NULL;
ssh->kex_ctx = NULL;
- ssh->hostkey = NULL;
+ ssh->hostkey_alg = NULL;
ssh->hostkey_str = NULL;
ssh->exitcode = -1;
ssh->close_expected = FALSE;
@@ -12538,7 +12531,7 @@ static const struct telnet_special *ssh_get_specials(void *handle)
struct telnet_special uncert[1];
const ssh_keyalg *alg =
hostkey_algs[ssh->uncert_hostkeys[i]].alg;
- uncert[0].name = alg->name;
+ uncert[0].name = alg->ssh_id;
uncert[0].code = TS_LOCALSTART + ssh->uncert_hostkeys[i];
ADD_SPECIALS(uncert);
}
@@ -12607,7 +12600,7 @@ static void ssh_special(void *handle, Telnet_Special code)
queue_idempotent_callback(&ssh->ssh2_transport_icb);
}
} else if (code >= TS_LOCALSTART) {
- ssh->hostkey = hostkey_algs[code - TS_LOCALSTART].alg;
+ ssh->hostkey_alg = hostkey_algs[code - TS_LOCALSTART].alg;
ssh->cross_certifying = TRUE;
if (!ssh->kex_in_progress && !ssh->bare_connection &&
ssh->version == 2) {
diff --git a/ssh.h b/ssh.h
index b4267854..6cc925f9 100644
--- a/ssh.h
+++ b/ssh.h
@@ -77,11 +77,8 @@ void share_setup_x11_channel(void *csv, void *chanv,
typedef void *Bignum;
#endif
-typedef struct ssh_key {
- int dummy;
-} ssh_key;
-
typedef struct ssh_keyalg ssh_keyalg;
+typedef const struct ssh_keyalg *ssh_key;
struct RSAKey {
int bits;
@@ -164,10 +161,9 @@ int ec_ed_alg_and_curve_by_bits(int bits,
const ssh_keyalg **alg);
struct ec_key {
- const ssh_keyalg *signalg;
struct ec_point publicKey;
Bignum privateKey;
- struct ssh_key sshk;
+ ssh_key sshk;
};
struct ec_point *ec_public(const Bignum privateKey, const struct ec_curve *curve);
@@ -391,31 +387,47 @@ struct ssh_kexes {
};
struct ssh_keyalg {
- ssh_key *(*newkey) (const ssh_keyalg *self, ptrlen data);
+ /* Constructors that create an ssh_key */
+ ssh_key *(*new_pub) (const ssh_keyalg *self, ptrlen pub);
+ ssh_key *(*new_priv) (const ssh_keyalg *self, ptrlen pub, ptrlen priv);
+ ssh_key *(*new_priv_openssh) (const ssh_keyalg *self, BinarySource *);
+
+ /* Methods that operate on an existing ssh_key */
void (*freekey) (ssh_key *key);
- char *(*fmtkey) (ssh_key *key);
+ void (*sign) (ssh_key *key, const void *data, int datalen, BinarySink *);
+ int (*verify) (ssh_key *key, ptrlen sig, ptrlen data);
void (*public_blob)(ssh_key *key, BinarySink *);
void (*private_blob)(ssh_key *key, BinarySink *);
- ssh_key *(*createkey) (const ssh_keyalg *self, ptrlen pub, ptrlen priv);
- ssh_key *(*openssh_createkey) (const ssh_keyalg *self, BinarySource *);
- void (*openssh_fmtkey) (ssh_key *key, BinarySink *);
- /* OpenSSH private key blobs, as created by openssh_fmtkey and
- * consumed by openssh_createkey, always (at least so far...) take
- * the form of a number of SSH-2 strings / mpints concatenated
- * end-to-end. Because the new-style OpenSSH private key format
- * stores those blobs without a containing string wrapper, we need
- * to know how many strings each one consists of, so that we can
- * skip over the right number to find the next key in the file.
- * openssh_private_npieces gives that information. */
- int openssh_private_npieces;
+ void (*openssh_blob) (ssh_key *key, BinarySink *);
+ char *(*cache_str) (ssh_key *key);
+
+ /* 'Class methods' that don't deal with an ssh_key at all */
int (*pubkey_bits) (const ssh_keyalg *self, ptrlen blob);
- int (*verifysig) (ssh_key *key, ptrlen sig, ptrlen data);
- void (*sign) (ssh_key *key, const void *data, int datalen, BinarySink *);
- const char *name;
- const char *keytype; /* for host key cache */
- const void *extra; /* private to the public key methods */
+
+ /* Constant data fields giving information about the key type */
+ const char *ssh_id; /* string identifier in the SSH protocol */
+ const char *cache_id; /* identifier used in PuTTY's host key cache */
+ const void *extra; /* private to the public key methods */
};
+#define ssh_key_new_pub(alg, data) ((alg)->new_pub(alg, data))
+#define ssh_key_new_priv(alg, pub, priv) ((alg)->new_priv(alg, pub, priv))
+#define ssh_key_new_priv_openssh(alg, bs) ((alg)->new_priv_openssh(alg, bs))
+
+#define ssh_key_free(key) ((*(key))->freekey(key))
+#define ssh_key_sign(key, data, len, bs) ((*(key))->sign(key, data, len, bs))
+#define ssh_key_verify(key, sig, data) ((*(key))->verify(key, sig, data))
+#define ssh_key_public_blob(key, bs) ((*(key))->public_blob(key, bs))
+#define ssh_key_private_blob(key, bs) ((*(key))->private_blob(key, bs))
+#define ssh_key_openssh_blob(key, bs) ((*(key))->openssh_blob(key, bs))
+#define ssh_key_cache_str(key) ((*(key))->cache_str(key))
+
+#define ssh_key_public_bits(alg, blob) ((alg)->pubkey_bits(alg, blob))
+
+#define ssh_key_alg(key) (*(key))
+#define ssh_key_ssh_id(key) ((*(key))->ssh_id)
+#define ssh_key_cache_id(key) ((*(key))->cache_id)
+
struct ssh_compress {
const char *name;
/* For zlib@openssh.com: if non-NULL, this name will be considered once
@@ -434,8 +446,7 @@ struct ssh_compress {
};
struct ssh2_userkey {
- const ssh_keyalg *alg; /* the key algorithm */
- ssh_key *data; /* the key data */
+ ssh_key *key; /* the key itself */
char *comment; /* the key comment */
};
@@ -782,7 +793,7 @@ void ssh2_write_pubkey(FILE *fp, const char *comment,
const void *v_pub_blob, int pub_len,
int keytype);
char *ssh2_fingerprint_blob(const void *blob, int bloblen);
-char *ssh2_fingerprint(const ssh_keyalg *alg, ssh_key *key);
+char *ssh2_fingerprint(ssh_key *key);
int key_type(const Filename *filename);
const char *key_type_to_str(int type);
diff --git a/sshdss.c b/sshdss.c
index 3c9589fb..c4686645 100644
--- a/sshdss.c
+++ b/sshdss.c
@@ -11,7 +11,7 @@
static void dss_freekey(ssh_key *key); /* forward reference */
-static ssh_key *dss_newkey(const ssh_keyalg *self, ptrlen data)
+static ssh_key *dss_new_pub(const ssh_keyalg *self, ptrlen data)
{
BinarySource src[1];
struct dss_key *dss;
@@ -21,6 +21,7 @@ static ssh_key *dss_newkey(const ssh_keyalg *self, ptrlen data)
return NULL;
dss = snew(struct dss_key);
+ dss->sshk = &ssh_dss;
dss->p = get_mp_ssh2(src);
dss->q = get_mp_ssh2(src);
dss->g = get_mp_ssh2(src);
@@ -53,7 +54,7 @@ static void dss_freekey(ssh_key *key)
sfree(dss);
}
-static char *dss_fmtkey(ssh_key *key)
+static char *dss_cache_str(ssh_key *key)
{
struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
char *p;
@@ -103,7 +104,7 @@ static char *dss_fmtkey(ssh_key *key)
return p;
}
-static int dss_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
+static int dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
{
struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
BinarySource src[1];
@@ -221,7 +222,7 @@ static void dss_private_blob(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, dss->x);
}
-static ssh_key *dss_createkey(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
+static ssh_key *dss_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
{
BinarySource src[1];
ssh_key *sshk;
@@ -231,7 +232,7 @@ static ssh_key *dss_createkey(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
unsigned char digest[20];
Bignum ytest;
- sshk = dss_newkey(self, pub);
+ sshk = dss_new_pub(self, pub);
if (!sshk)
return NULL;
@@ -273,12 +274,13 @@ static ssh_key *dss_createkey(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
return &dss->sshk;
}
-static ssh_key *dss_openssh_createkey(const ssh_keyalg *self,
- BinarySource *src)
+static ssh_key *dss_new_priv_openssh(const ssh_keyalg *self,
+ BinarySource *src)
{
struct dss_key *dss;
dss = snew(struct dss_key);
+ dss->sshk = &ssh_dss;
dss->p = get_mp_ssh2(src);
dss->q = get_mp_ssh2(src);
@@ -296,7 +298,7 @@ static ssh_key *dss_openssh_createkey(const ssh_keyalg *self,
return &dss->sshk;
}
-static void dss_openssh_fmtkey(ssh_key *key, BinarySink *bs)
+static void dss_openssh_blob(ssh_key *key, BinarySink *bs)
{
struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
@@ -313,7 +315,7 @@ static int dss_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
struct dss_key *dss;
int ret;
- sshk = dss_newkey(self, pub);
+ sshk = dss_new_pub(self, pub);
if (!sshk)
return -1;
@@ -485,18 +487,20 @@ static void dss_sign(ssh_key *key, const void *data, int datalen,
}
const ssh_keyalg ssh_dss = {
- dss_newkey,
+ dss_new_pub,
+ dss_new_priv,
+ dss_new_priv_openssh,
+
dss_freekey,
- dss_fmtkey,
+ dss_sign,
+ dss_verify,
dss_public_blob,
dss_private_blob,
- dss_createkey,
- dss_openssh_createkey,
- dss_openssh_fmtkey,
- 5 /* p,q,g,y,x */,
+ dss_openssh_blob,
+ dss_cache_str,
+
dss_pubkey_bits,
- dss_verifysig,
- dss_sign,
+
"ssh-dss",
"dss",
NULL,
diff --git a/sshdssg.c b/sshdssg.c
index 3d7b0ef6..882fbc1d 100644
--- a/sshdssg.c
+++ b/sshdssg.c
@@ -12,6 +12,8 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
unsigned pfirst, qfirst;
int progress;
+ key->sshk = &ssh_dss;
+
/*
* Set up the phase limits for the progress report. We do this
* by passing minus the phase number.
diff --git a/sshecc.c b/sshecc.c
index 0d97395a..ce663bfb 100644
--- a/sshecc.c
+++ b/sshecc.c
@@ -1713,7 +1713,7 @@ static void ecdsa_freekey(ssh_key *key)
sfree(ec);
}
-static ssh_key *ecdsa_newkey(const ssh_keyalg *self, ptrlen data)
+static ssh_key *ecdsa_new_pub(const ssh_keyalg *self, ptrlen data)
{
const struct ecsign_extra *extra =
(const struct ecsign_extra *)self->extra;
@@ -1734,8 +1734,8 @@ static ssh_key *ecdsa_newkey(const ssh_keyalg *self, ptrlen data)
}
ec = snew(struct ec_key);
+ ec->sshk = self;
- ec->signalg = self;
ec->publicKey.curve = curve;
ec->publicKey.infinity = 0;
ec->publicKey.x = NULL;
@@ -1758,7 +1758,7 @@ static ssh_key *ecdsa_newkey(const ssh_keyalg *self, ptrlen data)
return &ec->sshk;
}
-static char *ecdsa_fmtkey(ssh_key *key)
+static char *ecdsa_cache_str(ssh_key *key)
{
struct ec_key *ec = FROMFIELD(key, struct ec_key, sshk);
char *p;
@@ -1810,7 +1810,7 @@ static void ecdsa_public_blob(ssh_key *key, BinarySink *bs)
assert(pointlen >= 2);
- put_stringz(bs, ec->signalg->name);
+ put_stringz(bs, ec->sshk->ssh_id);
put_uint32(bs, pointlen);
/* Unset last bit of y and set first bit of x in its place */
@@ -1824,7 +1824,7 @@ static void ecdsa_public_blob(ssh_key *key, BinarySink *bs)
pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8;
- put_stringz(bs, ec->signalg->name);
+ put_stringz(bs, ec->sshk->ssh_id);
put_stringz(bs, ec->publicKey.curve->name);
put_uint32(bs, (2 * pointlen) + 1);
put_byte(bs, 0x04);
@@ -1864,15 +1864,14 @@ static void ecdsa_private_blob(ssh_key *key, BinarySink *bs)
}
}
-static ssh_key *ecdsa_createkey(const ssh_keyalg *self,
- ptrlen pub, ptrlen priv)
+static ssh_key *ecdsa_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
{
BinarySource src[1];
ssh_key *sshk;
struct ec_key *ec;
struct ec_point *publicKey;
- sshk = ecdsa_newkey(self, pub);
+ sshk = ecdsa_new_pub(self, pub);
if (!sshk)
return NULL;
@@ -1910,8 +1909,8 @@ static ssh_key *ecdsa_createkey(const ssh_keyalg *self,
return &ec->sshk;
}
-static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self,
- BinarySource *src)
+static ssh_key *ed25519_new_priv_openssh(const ssh_keyalg *self,
+ BinarySource *src)
{
struct ec_key *ec;
struct ec_point *publicKey;
@@ -1923,8 +1922,8 @@ static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self,
return NULL;
ec = snew(struct ec_key);
+ ec->sshk = self;
- ec->signalg = self;
ec->publicKey.curve = ec_ed25519();
ec->publicKey.infinity = 0;
ec->privateKey = NULL;
@@ -1966,7 +1965,7 @@ static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self,
return &ec->sshk;
}
-static void ed25519_openssh_fmtkey(ssh_key *key, BinarySink *bs)
+static void ed25519_openssh_blob(ssh_key *key, BinarySink *bs)
{
struct ec_key *ec = FROMFIELD(key, struct ec_key, sshk);
strbuf *pub;
@@ -2002,8 +2001,8 @@ static void ed25519_openssh_fmtkey(ssh_key *key, BinarySink *bs)
strbuf_free(pub);
}
-static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self,
- BinarySource *src)
+static ssh_key *ecdsa_new_priv_openssh(const ssh_keyalg *self,
+ BinarySource *src)
{
const struct ecsign_extra *extra =
(const struct ecsign_extra *)self->extra;
@@ -2017,8 +2016,8 @@ static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self,
assert(curve->type == EC_WEIERSTRASS);
ec = snew(struct ec_key);
+ ec->sshk = self;
- ec->signalg = self;
ec->publicKey.curve = curve;
ec->publicKey.infinity = 0;
ec->publicKey.x = NULL;
@@ -2067,7 +2066,7 @@ static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self,
return &ec->sshk;
}
-static void ecdsa_openssh_fmtkey(ssh_key *key, BinarySink *bs)
+static void ecdsa_openssh_blob(ssh_key *key, BinarySink *bs)
{
struct ec_key *ec = FROMFIELD(key, struct ec_key, sshk);
@@ -2096,7 +2095,7 @@ static int ecdsa_pubkey_bits(const ssh_keyalg *self, ptrlen blob)
struct ec_key *ec;
int ret;
- sshk = ecdsa_newkey(self, blob);
+ sshk = ecdsa_new_pub(self, blob);
if (!sshk)
return -1;
@@ -2107,11 +2106,11 @@ static int ecdsa_pubkey_bits(const ssh_keyalg *self, ptrlen blob)
return ret;
}
-static int ecdsa_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
+static int ecdsa_verify(ssh_key *key, ptrlen sig, ptrlen data)
{
struct ec_key *ec = FROMFIELD(key, struct ec_key, sshk);
const struct ecsign_extra *extra =
- (const struct ecsign_extra *)ec->signalg->extra;
+ (const struct ecsign_extra *)ec->sshk->extra;
BinarySource src[1];
ptrlen sigstr;
int ret;
@@ -2122,7 +2121,7 @@ static int ecdsa_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
BinarySource_BARE_INIT(src, sig.ptr, sig.len);
/* Check the signature starts with the algorithm name */
- if (!ptrlen_eq_string(get_string(src), ec->signalg->name))
+ if (!ptrlen_eq_string(get_string(src), ec->sshk->ssh_id))
return 0;
sigstr = get_string(src);
@@ -2261,7 +2260,7 @@ static void ecdsa_sign(ssh_key *key, const void *data, int datalen,
{
struct ec_key *ec = FROMFIELD(key, struct ec_key, sshk);
const struct ecsign_extra *extra =
- (const struct ecsign_extra *)ec->signalg->extra;
+ (const struct ecsign_extra *)ec->sshk->extra;
unsigned char digest[512 / 8];
int digestLen;
Bignum r = NULL, s = NULL;
@@ -2347,7 +2346,7 @@ static void ecdsa_sign(ssh_key *key, const void *data, int datalen,
}
/* Format the output */
- put_stringz(bs, ec->signalg->name);
+ put_stringz(bs, ec->sshk->ssh_id);
pointlen = ec->publicKey.curve->fieldBits / 8;
put_uint32(bs, pointlen * 2);
@@ -2379,7 +2378,7 @@ static void ecdsa_sign(ssh_key *key, const void *data, int datalen,
assert(s);
/* Format the output */
- put_stringz(bs, ec->signalg->name);
+ put_stringz(bs, ec->sshk->ssh_id);
substr = strbuf_new();
put_mp_ssh2(substr, r);
@@ -2396,18 +2395,20 @@ const struct ecsign_extra sign_extra_ed25519 = {
NULL, 0,
};
const ssh_keyalg ssh_ecdsa_ed25519 = {
- ecdsa_newkey,
+ ecdsa_new_pub,
+ ecdsa_new_priv,
+ ed25519_new_priv_openssh,
+
ecdsa_freekey,
- ecdsa_fmtkey,
+ ecdsa_sign,
+ ecdsa_verify,
ecdsa_public_blob,
ecdsa_private_blob,
- ecdsa_createkey,
- ed25519_openssh_createkey,
- ed25519_openssh_fmtkey,
- 2 /* point, private exponent */,
+ ed25519_openssh_blob,
+ ecdsa_cache_str,
+
ecdsa_pubkey_bits,
- ecdsa_verifysig,
- ecdsa_sign,
+
"ssh-ed25519",
"ssh-ed25519",
&sign_extra_ed25519,
@@ -2422,18 +2423,20 @@ const struct ecsign_extra sign_extra_nistp256 = {
nistp256_oid, lenof(nistp256_oid),
};
const ssh_keyalg ssh_ecdsa_nistp256 = {
- ecdsa_newkey,
+ ecdsa_new_pub,
+ ecdsa_new_priv,
+ ecdsa_new_priv_openssh,
+
ecdsa_freekey,
- ecdsa_fmtkey,
+ ecdsa_sign,
+ ecdsa_verify,
ecdsa_public_blob,
ecdsa_private_blob,
- ecdsa_createkey,
- ecdsa_openssh_createkey,
- ecdsa_openssh_fmtkey,
- 3 /* curve name, point, private exponent */,
+ ecdsa_openssh_blob,
+ ecdsa_cache_str,
+
ecdsa_pubkey_bits,
- ecdsa_verifysig,
- ecdsa_sign,
+
"ecdsa-sha2-nistp256",
"ecdsa-sha2-nistp256",
&sign_extra_nistp256,
@@ -2448,18 +2451,20 @@ const struct ecsign_extra sign_extra_nistp384 = {
nistp384_oid, lenof(nistp384_oid),
};
const ssh_keyalg ssh_ecdsa_nistp384 = {
- ecdsa_newkey,
+ ecdsa_new_pub,
+ ecdsa_new_priv,
+ ecdsa_new_priv_openssh,
+
ecdsa_freekey,
- ecdsa_fmtkey,
+ ecdsa_sign,
+ ecdsa_verify,
ecdsa_public_blob,
ecdsa_private_blob,
- ecdsa_createkey,
- ecdsa_openssh_createkey,
- ecdsa_openssh_fmtkey,
- 3 /* curve name, point, private exponent */,
+ ecdsa_openssh_blob,
+ ecdsa_cache_str,
+
ecdsa_pubkey_bits,
- ecdsa_verifysig,
- ecdsa_sign,
+
"ecdsa-sha2-nistp384",
"ecdsa-sha2-nistp384",
&sign_extra_nistp384,
@@ -2474,18 +2479,20 @@ const struct ecsign_extra sign_extra_nistp521 = {
nistp521_oid, lenof(nistp521_oid),
};
const ssh_keyalg ssh_ecdsa_nistp521 = {
- ecdsa_newkey,
+ ecdsa_new_pub,
+ ecdsa_new_priv,
+ ecdsa_new_priv_openssh,
+
ecdsa_freekey,
- ecdsa_fmtkey,
+ ecdsa_sign,
+ ecdsa_verify,
ecdsa_public_blob,
ecdsa_private_blob,
- ecdsa_createkey,
- ecdsa_openssh_createkey,
- ecdsa_openssh_fmtkey,
- 3 /* curve name, point, private exponent */,
+ ecdsa_openssh_blob,
+ ecdsa_cache_str,
+
ecdsa_pubkey_bits,
- ecdsa_verifysig,
- ecdsa_sign,
+
"ecdsa-sha2-nistp521",
"ecdsa-sha2-nistp521",
&sign_extra_nistp521,
@@ -2559,7 +2566,7 @@ struct ec_key *ssh_ecdhkex_newkey(const struct ssh_kex *kex)
key = snew(struct ec_key);
- key->signalg = NULL;
+ key->sshk = NULL;
key->publicKey.curve = curve;
if (curve->type == EC_MONTGOMERY) {
diff --git a/sshecdsag.c b/sshecdsag.c
index 83eeeb03..7523fa7a 100644
--- a/sshecdsag.c
+++ b/sshecdsag.c
@@ -13,7 +13,7 @@ int ec_generate(struct ec_key *key, int bits, progfn_t pfn,
struct ec_point *publicKey;
if (!ec_nist_alg_and_curve_by_bits(bits, &key->publicKey.curve,
- &key->signalg))
+ &key->sshk))
return 0;
key->privateKey = bignum_random_in_range(One, key->publicKey.curve->w.n);
@@ -40,7 +40,7 @@ int ec_edgenerate(struct ec_key *key, int bits, progfn_t pfn,
struct ec_point *publicKey;
if (!ec_ed_alg_and_curve_by_bits(bits, &key->publicKey.curve,
- &key->signalg))
+ &key->sshk))
return 0;
{
diff --git a/sshpubk.c b/sshpubk.c
index 6c63674b..3732a6f2 100644
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -563,9 +563,7 @@ static int read_blob(FILE *fp, int nlines, BinarySink *bs)
/*
* Magic error return value for when the passphrase is wrong.
*/
-struct ssh2_userkey ssh2_wrong_passphrase = {
- NULL, NULL, NULL
-};
+struct ssh2_userkey ssh2_wrong_passphrase = { NULL, NULL };
const ssh_keyalg *find_pubkey_alg_len(ptrlen name)
{
@@ -741,7 +739,7 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
free_macdata = FALSE;
} else {
macdata = strbuf_new();
- put_stringz(macdata, alg->name);
+ put_stringz(macdata, alg->ssh_id);
put_stringz(macdata, encryption);
put_stringz(macdata, comment);
put_string(macdata, public_blob->s,
@@ -797,12 +795,11 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
* Create and return the key.
*/
ret = snew(struct ssh2_userkey);
- ret->alg = alg;
ret->comment = comment;
- ret->data = alg->createkey(
+ ret->key = ssh_key_new_priv(
alg, make_ptrlen(public_blob->u, public_blob->len),
make_ptrlen(private_blob->u, private_blob->len));
- if (!ret->data) {
+ if (!ret->key) {
sfree(ret);
ret = NULL;
error = "createkey failed";
@@ -1129,7 +1126,7 @@ int ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
fclose(fp);
if (algorithm)
- *algorithm = dupstr(alg->name);
+ *algorithm = dupstr(alg->ssh_id);
return TRUE;
/*
@@ -1252,9 +1249,9 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
* Fetch the key component blobs.
*/
pub_blob = strbuf_new();
- key->alg->public_blob(key->data, BinarySink_UPCAST(pub_blob));
+ ssh_key_public_blob(key->key, BinarySink_UPCAST(pub_blob));
priv_blob = strbuf_new();
- key->alg->private_blob(key->data, BinarySink_UPCAST(priv_blob));
+ ssh_key_private_blob(key->key, BinarySink_UPCAST(priv_blob));
/*
* Determine encryption details, and encrypt the private blob.
@@ -1286,7 +1283,7 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
char header[] = "putty-private-key-file-mac-key";
macdata = strbuf_new();
- put_stringz(macdata, key->alg->name);
+ put_stringz(macdata, ssh_key_ssh_id(key->key));
put_stringz(macdata, cipherstr);
put_stringz(macdata, key->comment);
put_string(macdata, pub_blob->s, pub_blob->len);
@@ -1333,7 +1330,7 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
sfree(priv_blob_encrypted);
return 0;
}
- fprintf(fp, "PuTTY-User-Key-File-2: %s\n", key->alg->name);
+ fprintf(fp, "PuTTY-User-Key-File-2: %s\n", ssh_key_ssh_id(key->key));
fprintf(fp, "Encryption: %s\n", cipherstr);
fprintf(fp, "Comment: %s\n", key->comment);
fprintf(fp, "Public-Lines: %d\n", base64_lines(pub_blob->len));
@@ -1425,7 +1422,7 @@ char *ssh2_pubkey_openssh_str(struct ssh2_userkey *key)
char *ret;
blob = strbuf_new();
- key->alg->public_blob(key->data, BinarySink_UPCAST(blob));
+ ssh_key_public_blob(key->key, BinarySink_UPCAST(blob));
ret = ssh2_pubkey_openssh_str_internal(
key->comment, blob->s, blob->len);
strbuf_free(blob);
@@ -1510,7 +1507,7 @@ char *ssh2_fingerprint_blob(const void *blob, int bloblen)
if (!get_err(src)) {
alg = find_pubkey_alg_len(algname);
if (alg) {
- int bits = alg->pubkey_bits(alg, make_ptrlen(blob, bloblen));
+ int bits = ssh_key_public_bits(alg, make_ptrlen(blob, bloblen));
return dupprintf("%.*s %d %s", PTRLEN_PRINTF(algname),
bits, fingerprint_str);
} else {
@@ -1526,10 +1523,10 @@ char *ssh2_fingerprint_blob(const void *blob, int bloblen)
}
}
-char *ssh2_fingerprint(const ssh_keyalg *alg, ssh_key *data)
+char *ssh2_fingerprint(ssh_key *data)
{
strbuf *blob = strbuf_new();
- alg->public_blob(data, BinarySink_UPCAST(blob));
+ ssh_key_public_blob(data, BinarySink_UPCAST(blob));
char *ret = ssh2_fingerprint_blob(blob->s, blob->len);
strbuf_free(blob);
return ret;
diff --git a/sshrsa.c b/sshrsa.c
index 9c99f99b..f66af550 100644
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -474,7 +474,7 @@ void freersakey(struct RSAKey *key)
static void rsa2_freekey(ssh_key *key); /* forward reference */
-static ssh_key *rsa2_newkey(const ssh_keyalg *self, ptrlen data)
+static ssh_key *rsa2_new_pub(const ssh_keyalg *self, ptrlen data)
{
BinarySource src[1];
struct RSAKey *rsa;
@@ -484,6 +484,7 @@ static ssh_key *rsa2_newkey(const ssh_keyalg *self, ptrlen data)
return NULL;
rsa = snew(struct RSAKey);
+ rsa->sshk = &ssh_rsa;
rsa->exponent = get_mp_ssh2(src);
rsa->modulus = get_mp_ssh2(src);
rsa->private_exponent = NULL;
@@ -505,7 +506,7 @@ static void rsa2_freekey(ssh_key *key)
sfree(rsa);
}
-static char *rsa2_fmtkey(ssh_key *key)
+static char *rsa2_cache_str(ssh_key *key)
{
struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
char *p;
@@ -536,14 +537,14 @@ static void rsa2_private_blob(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, rsa->iqmp);
}
-static ssh_key *rsa2_createkey(const ssh_keyalg *self,
+static ssh_key *rsa2_new_priv(const ssh_keyalg *self,
ptrlen pub, ptrlen priv)
{
BinarySource src[1];
ssh_key *sshk;
struct RSAKey *rsa;
- sshk = rsa2_newkey(self, pub);
+ sshk = rsa2_new_pub(self, pub);
if (!sshk)
return NULL;
@@ -562,12 +563,13 @@ static ssh_key *rsa2_createkey(const ssh_keyalg *self,
return &rsa->sshk;
}
-static ssh_key *rsa2_openssh_createkey(const ssh_keyalg *self,
- BinarySource *src)
+static ssh_key *rsa2_new_priv_openssh(const ssh_keyalg *self,
+ BinarySource *src)
{
struct RSAKey *rsa;
rsa = snew(struct RSAKey);
+ rsa->sshk = &ssh_rsa;
rsa->comment = NULL;
rsa->modulus = get_mp_ssh2(src);
@@ -585,7 +587,7 @@ static ssh_key *rsa2_openssh_createkey(const ssh_keyalg *self,
return &rsa->sshk;
}
-static void rsa2_openssh_fmtkey(ssh_key *key, BinarySink *bs)
+static void rsa2_openssh_blob(ssh_key *key, BinarySink *bs)
{
struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
@@ -603,7 +605,7 @@ static int rsa2_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
struct RSAKey *rsa;
int ret;
- sshk = rsa2_newkey(self, pub);
+ sshk = rsa2_new_pub(self, pub);
if (!sshk)
return -1;
@@ -645,7 +647,7 @@ static const unsigned char asn1_weird_stuff[] = {
#define ASN1_LEN ( (int) sizeof(asn1_weird_stuff) )
-static int rsa2_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
+static int rsa2_verify(ssh_key *key, ptrlen sig, ptrlen data)
{
struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
BinarySource src[1];
@@ -744,18 +746,20 @@ static void rsa2_sign(ssh_key *key, const void *data, int datalen,
}
const ssh_keyalg ssh_rsa = {
- rsa2_newkey,
+ rsa2_new_pub,
+ rsa2_new_priv,
+ rsa2_new_priv_openssh,
+
rsa2_freekey,
- rsa2_fmtkey,
+ rsa2_sign,
+ rsa2_verify,
rsa2_public_blob,
rsa2_private_blob,
- rsa2_createkey,
- rsa2_openssh_createkey,
- rsa2_openssh_fmtkey,
- 6 /* n,e,d,iqmp,q,p */,
+ rsa2_openssh_blob,
+ rsa2_cache_str,
+
rsa2_pubkey_bits,
- rsa2_verifysig,
- rsa2_sign,
+
"ssh-rsa",
"rsa2",
NULL,
@@ -763,7 +767,7 @@ const ssh_keyalg ssh_rsa = {
struct RSAKey *ssh_rsakex_newkey(const void *data, int len)
{
- ssh_key *sshk = rsa2_newkey(&ssh_rsa, make_ptrlen(data, len));
+ ssh_key *sshk = rsa2_new_pub(&ssh_rsa, make_ptrlen(data, len));
if (!sshk)
return NULL;
return FROMFIELD(sshk, struct RSAKey, sshk);
diff --git a/sshrsag.c b/sshrsag.c
index d754890d..7800ec94 100644
--- a/sshrsag.c
+++ b/sshrsag.c
@@ -14,6 +14,8 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
Bignum pm1, qm1, phi_n;
unsigned pfirst, qfirst;
+ key->sshk = &ssh_rsa;
+
/*
* Set up the phase limits for the progress report. We do this
* by passing minus the phase number.
diff --git a/windows/winpgen.c b/windows/winpgen.c
index bc68148f..fecd4f00 100644
--- a/windows/winpgen.c
+++ b/windows/winpgen.c
@@ -769,7 +769,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
savecomment = state->ssh2key.comment;
state->ssh2key.comment = NULL;
- fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
+ fp = ssh2_fingerprint(state->ssh2key.key);
state->ssh2key.comment = savecomment;
SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
@@ -1321,8 +1321,8 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
} else {
if (state->ssh2) {
strbuf *blob = strbuf_new();
- state->ssh2key.alg->public_blob(
- state->ssh2key.data, BinarySink_UPCAST(blob));
+ ssh_key_public_blob(
+ state->ssh2key.key, BinarySink_UPCAST(blob));
ssh2_write_pubkey(fp, state->ssh2key.comment,
blob->u, blob->len,
SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
@@ -1365,17 +1365,13 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
if (state->ssh2) {
if (state->keytype == DSA) {
- state->ssh2key.data = &state->dsskey.sshk;
- state->ssh2key.alg = &ssh_dss;
+ state->ssh2key.key = &state->dsskey.sshk;
} else if (state->keytype == ECDSA) {
- state->ssh2key.data = &state->eckey.sshk;
- state->ssh2key.alg = state->eckey.signalg;
+ state->ssh2key.key = &state->eckey.sshk;
} else if (state->keytype == ED25519) {
- state->ssh2key.data = &state->eckey.sshk;
- state->ssh2key.alg = &ssh_ecdsa_ed25519;
+ state->ssh2key.key = &state->eckey.sshk;
} else {
- state->ssh2key.data = &state->key.sshk;
- state->ssh2key.alg = &ssh_rsa;
+ state->ssh2key.key = &state->key.sshk;
}
state->commentptr = &state->ssh2key.comment;
} else {
@@ -1423,7 +1419,7 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
savecomment = *state->commentptr;
*state->commentptr = NULL;
if (state->ssh2)
- fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
+ fp = ssh2_fingerprint(state->ssh2key.key);
else
fp = rsa_ssh1_fingerprint(&state->key);
SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
diff --git a/windows/winpgnt.c b/windows/winpgnt.c
index 43266440..4aff8338 100644
--- a/windows/winpgnt.c
+++ b/windows/winpgnt.c
@@ -339,7 +339,7 @@ void keylist_update(void)
* stop and leave out a tab character. Urgh.
*/
- p = ssh2_fingerprint(skey->alg, skey->data);
+ p = ssh2_fingerprint(skey->key);
listentry = dupprintf("%s\t%s", p, skey->comment);
sfree(p);
@@ -350,7 +350,8 @@ void keylist_update(void)
break;
listentry[pos++] = '\t';
}
- if (skey->alg != &ssh_dss && skey->alg != &ssh_rsa) {
+ if (ssh_key_alg(skey->key) != &ssh_dss &&
+ ssh_key_alg(skey->key) != &ssh_rsa) {
/*
* Remove the bit-count field, which is between the
* first and second \t.
@@ -647,7 +648,7 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
if (selectedArray[itemNum] == rCount + i) {
pageant_delete_ssh2_key(skey);
- skey->alg->freekey(skey->data);
+ ssh_key_free(skey->key);
sfree(skey);
itemNum--;
}
From 025599ec999dd8454f2a8fdd11c08329ac608571 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 14:30:28 +0100
Subject: [PATCH 348/607] Unix PuTTYgen: switch to /dev/urandom by default.
The general wisdom these days - in particular as given by the Linux
urandom(4) man page - seems to be that there's no need to use the
blocking /dev/random any more unless you're running at very early boot
time when the system random pool is at serious risk of not having any
entropy in it at all.
In case of non-Linux systems that don't think /dev/urandom is a
standard name, I fall back to /dev/random if /dev/urandom can't be
found.
---
unix/uxgen.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/unix/uxgen.c b/unix/uxgen.c
index f593a960..ed14188d 100644
--- a/unix/uxgen.c
+++ b/unix/uxgen.c
@@ -16,8 +16,26 @@ char *get_random_data(int len, const char *device)
int fd;
int ngot, ret;
- if (!device)
- device = "/dev/random";
+ if (!device) {
+ static const char *const default_devices[] = {
+ "/dev/urandom", "/dev/random"
+ };
+ size_t i;
+
+ for (i = 0; i < lenof(default_devices); i++) {
+ if (access(default_devices[i], R_OK) == 0) {
+ device = default_devices[i];
+ break;
+ }
+ }
+
+ if (!device) {
+ sfree(buf);
+ fprintf(stderr, "puttygen: cannot find a readable "
+ "random number source; use --random-device\n");
+ return NULL;
+ }
+ }
fd = open(device, O_RDONLY);
if (fd < 0) {
From 6142013abc701f235772e3caf94246420b1ac9d4 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 14:41:31 +0100
Subject: [PATCH 349/607] Windows PuTTYgen: switch to CryptGenRandom.
We now only use the mouse-movement based entropy collection system if
the system CPRNG fails to provide us with as much entropy as we want.
---
windows/winnoise.c | 44 ++++++++------
windows/winpgen.c | 145 +++++++++++++++++++++++++++------------------
windows/winstuff.h | 5 ++
3 files changed, 120 insertions(+), 74 deletions(-)
diff --git a/windows/winnoise.c b/windows/winnoise.c
index 31364546..1dd0481b 100644
--- a/windows/winnoise.c
+++ b/windows/winnoise.c
@@ -19,6 +19,29 @@ DECL_WINDOWS_FUNCTION(static, BOOL, CryptReleaseContext,
(HCRYPTPROV, DWORD));
static HMODULE wincrypt_module = NULL;
+int win_read_random(void *buf, unsigned wanted)
+{
+ int toret = FALSE;
+ HCRYPTPROV crypt_provider;
+
+ if (!wincrypt_module) {
+ wincrypt_module = load_system32_dll("advapi32.dll");
+ GET_WINDOWS_FUNCTION(wincrypt_module, CryptAcquireContextA);
+ GET_WINDOWS_FUNCTION(wincrypt_module, CryptGenRandom);
+ GET_WINDOWS_FUNCTION(wincrypt_module, CryptReleaseContext);
+ }
+
+ if (wincrypt_module && p_CryptAcquireContextA &&
+ p_CryptGenRandom && p_CryptReleaseContext &&
+ p_CryptAcquireContextA(&crypt_provider, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT)) {
+ toret = p_CryptGenRandom(crypt_provider, wanted, buf);
+ p_CryptReleaseContext(crypt_provider, 0);
+ }
+
+ return toret;
+}
+
/*
* This function is called once, at PuTTY startup.
*/
@@ -28,8 +51,8 @@ void noise_get_heavy(void (*func) (void *, int))
HANDLE srch;
WIN32_FIND_DATA finddata;
DWORD pid;
- HCRYPTPROV crypt_provider;
char winpath[MAX_PATH + 3];
+ BYTE buf[32];
GetWindowsDirectory(winpath, sizeof(winpath));
strcat(winpath, "\\*");
@@ -44,22 +67,9 @@ void noise_get_heavy(void (*func) (void *, int))
pid = GetCurrentProcessId();
func(&pid, sizeof(pid));
- if (!wincrypt_module) {
- wincrypt_module = load_system32_dll("advapi32.dll");
- GET_WINDOWS_FUNCTION(wincrypt_module, CryptAcquireContextA);
- GET_WINDOWS_FUNCTION(wincrypt_module, CryptGenRandom);
- GET_WINDOWS_FUNCTION(wincrypt_module, CryptReleaseContext);
- }
-
- if (wincrypt_module && p_CryptAcquireContextA &&
- p_CryptGenRandom && p_CryptReleaseContext &&
- p_CryptAcquireContextA(&crypt_provider, NULL, NULL, PROV_RSA_FULL,
- CRYPT_VERIFYCONTEXT)) {
- BYTE buf[32];
- if (p_CryptGenRandom(crypt_provider, 32, buf)) {
- func(buf, sizeof(buf));
- }
- p_CryptReleaseContext(crypt_provider, 0);
+ if (win_read_random(buf, sizeof(buf))) {
+ func(buf, sizeof(buf));
+ smemclr(buf, sizeof(buf));
}
read_random_seed(func);
diff --git a/windows/winpgen.c b/windows/winpgen.c
index fecd4f00..d2840113 100644
--- a/windows/winpgen.c
+++ b/windows/winpgen.c
@@ -809,14 +809,45 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
burnstr(passphrase);
}
+static void start_generating_key(HWND hwnd, struct MainDlgState *state)
+{
+ static const char generating_msg[] =
+ "Please wait while a key is generated...";
+
+ struct rsa_key_thread_params *params;
+ DWORD threadid;
+
+ SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
+ SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
+ MAKELPARAM(0, PROGRESSRANGE));
+ SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
+
+ params = snew(struct rsa_key_thread_params);
+ params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
+ params->dialog = hwnd;
+ params->key_bits = state->key_bits;
+ params->curve_bits = state->curve_bits;
+ params->keytype = state->keytype;
+ params->key = &state->key;
+ params->dsskey = &state->dsskey;
+
+ if (!CreateThread(NULL, 0, generate_key_thread,
+ params, 0, &threadid)) {
+ MessageBox(hwnd, "Out of thread resources",
+ "Key generation error",
+ MB_OK | MB_ICONERROR);
+ sfree(params);
+ } else {
+ state->generation_thread_exists = TRUE;
+ }
+}
+
/*
* Dialog-box function for the main PuTTYgen dialog box.
*/
static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
- static const char generating_msg[] =
- "Please wait while a key is generated...";
static const char entropy_msg[] =
"Please generate some randomness by moving the mouse over the blank area.";
struct MainDlgState *state;
@@ -1009,9 +1040,6 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
state->entropy_got, 0);
if (state->entropy_got >= state->entropy_required) {
- struct rsa_key_thread_params *params;
- DWORD threadid;
-
/*
* Seed the entropy pool
*/
@@ -1020,29 +1048,7 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
sfree(state->entropy);
state->collecting_entropy = FALSE;
- SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
- SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
- MAKELPARAM(0, PROGRESSRANGE));
- SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
-
- params = snew(struct rsa_key_thread_params);
- params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
- params->dialog = hwnd;
- params->key_bits = state->key_bits;
- params->curve_bits = state->curve_bits;
- params->keytype = state->keytype;
- params->key = &state->key;
- params->dsskey = &state->dsskey;
-
- if (!CreateThread(NULL, 0, generate_key_thread,
- params, 0, &threadid)) {
- MessageBox(hwnd, "Out of thread resources",
- "Key generation error",
- MB_OK | MB_ICONERROR);
- sfree(params);
- } else {
- state->generation_thread_exists = TRUE;
- }
+ start_generating_key(hwnd, state);
}
}
break;
@@ -1102,6 +1108,8 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
state =
(struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (!state->generation_thread_exists) {
+ unsigned raw_entropy_required;
+ unsigned char *raw_entropy_buf;
BOOL ok;
state->key_bits = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
if (!ok)
@@ -1149,39 +1157,62 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
break;
}
- ui_set_state(hwnd, state, 1);
- SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
- state->key_exists = FALSE;
- state->collecting_entropy = TRUE;
-
- /*
- * My brief statistical tests on mouse movements
- * suggest that there are about 2.5 bits of
- * randomness in the x position, 2.5 in the y
- * position, and 1.7 in the message time, making
- * 5.7 bits of unpredictability per mouse movement.
- * However, other people have told me it's far less
- * than that, so I'm going to be stupidly cautious
- * and knock that down to a nice round 2. With this
- * method, we require two words per mouse movement,
- * so with 2 bits per mouse movement we expect 2
- * bits every 2 words.
- */
if (state->keytype == RSA || state->keytype == DSA)
- state->entropy_required = (state->key_bits / 2) * 2;
+ raw_entropy_required = (state->key_bits / 2) * 2;
else if (state->keytype == ECDSA)
- state->entropy_required = (state->curve_bits / 2) * 2;
+ raw_entropy_required = (state->curve_bits / 2) * 2;
else
- state->entropy_required = 256;
-
- state->entropy_got = 0;
- state->entropy_size = (state->entropy_required *
- sizeof(unsigned));
- state->entropy = snewn(state->entropy_required, unsigned);
+ raw_entropy_required = 256;
+
+ raw_entropy_buf = snewn(raw_entropy_required, unsigned char);
+ if (win_read_random(raw_entropy_buf, raw_entropy_required)) {
+ /*
+ * If we can get the entropy we need from
+ * CryptGenRandom, just do that, and go straight
+ * to the key-generation phase.
+ */
+ random_add_heavynoise(raw_entropy_buf,
+ raw_entropy_required);
+ start_generating_key(hwnd, state);
+ } else {
+ /*
+ * Manual entropy input, by making the user wave
+ * the mouse over the window a lot.
+ *
+ * My brief statistical tests on mouse movements
+ * suggest that there are about 2.5 bits of
+ * randomness in the x position, 2.5 in the y
+ * position, and 1.7 in the message time, making
+ * 5.7 bits of unpredictability per mouse
+ * movement. However, other people have told me
+ * it's far less than that, so I'm going to be
+ * stupidly cautious and knock that down to a nice
+ * round 2. With this method, we require two words
+ * per mouse movement, so with 2 bits per mouse
+ * movement we expect 2 bits every 2 words, i.e.
+ * the number of _words_ of mouse data we want to
+ * collect is just the same as the number of
+ * _bits_ of entropy we want.
+ */
+ state->entropy_required = raw_entropy_required;
+
+ ui_set_state(hwnd, state, 1);
+ SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
+ state->key_exists = FALSE;
+ state->collecting_entropy = TRUE;
+
+ state->entropy_got = 0;
+ state->entropy_size = (state->entropy_required *
+ sizeof(unsigned));
+ state->entropy = snewn(state->entropy_required, unsigned);
+
+ SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
+ MAKELPARAM(0, state->entropy_required));
+ SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
+ }
- SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
- MAKELPARAM(0, state->entropy_required));
- SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
+ smemclr(raw_entropy_buf, raw_entropy_required);
+ sfree(raw_entropy_buf);
}
break;
case IDC_SAVE:
diff --git a/windows/winstuff.h b/windows/winstuff.h
index a41974f1..20e8b97e 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -607,6 +607,11 @@ void remove_session_from_jumplist(const char * const sessionname);
void clear_jumplist(void);
BOOL set_explicit_app_user_model_id();
+/*
+ * Exports from winnoise.c.
+ */
+int win_read_random(void *buf, unsigned wanted); /* returns TRUE on success */
+
/*
* Extra functions in winstore.c over and above the interface in
* storage.h.
From f1fae1bfaa6c409f0a37dcaada73ebe2fb8f1b35 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 14:53:29 +0100
Subject: [PATCH 350/607] Fix a Windows warning on a strange cast.
The specific thing that's strange about it is that it's _not_ an error
even though the compiler is quite justified in being suspicious about
it! The MS APIs define two different structures to have identical
formats.
---
windows/wingss.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/windows/wingss.c b/windows/wingss.c
index c9bd2b31..4b511b2a 100644
--- a/windows/wingss.c
+++ b/windows/wingss.c
@@ -369,8 +369,23 @@ static void localexp_to_exp_lifetime(TimeStamp *localexp,
if (expiry)
*expiry = GSS_NO_EXPIRATION;
- if (!LocalFileTimeToFileTime(localexp, &expUTC))
- return;
+ /*
+ * Type oddity: localexp is a pointer to 'TimeStamp', whereas
+ * LocalFileTimeToFileTime expects a pointer to FILETIME. However,
+ * despite having different formal type names from the compiler's
+ * point of view, these two structures are specified to be
+ * isomorphic in the MS documentation, so it's legitimate to copy
+ * between them:
+ *
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa380511(v=vs.85).aspx
+ */
+ {
+ FILETIME localexp_ft;
+ enum { vorpal_sword = 1 / (sizeof(*localexp) == sizeof(localexp_ft)) };
+ memcpy(&localexp_ft, localexp, sizeof(localexp_ft));
+ if (!LocalFileTimeToFileTime(&localexp_ft, &expUTC))
+ return;
+ }
TIME_WIN_TO_POSIX(expUTC, exp);
delta = exp - now;
From 869a0f5f710129f775d8d9384ce46e6115e1cfeb Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 15:05:44 +0100
Subject: [PATCH 351/607] Fix Windows warning about GetVersionEx deprecation.
Rather than squelching the warning, I'm actually paying attention to
the deprecation, in that I'm allowing for the possibility that the
function might stop existing or stop returning success.
---
windows/window.c | 29 +++++++++++------------------
windows/winjump.c | 6 ++----
windows/winmisc.c | 37 ++++++++++++++++++++++++++++++++++---
windows/winpgnt.c | 10 ++--------
windows/winprint.c | 2 +-
windows/winstuff.h | 4 ++--
6 files changed, 52 insertions(+), 36 deletions(-)
diff --git a/windows/window.c b/windows/window.c
index 88a251fe..c635720d 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -374,23 +374,15 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
* config box. */
defuse_showwindow();
- if (!init_winver())
- {
- char *str = dupprintf("%s Fatal Error", appname);
- MessageBox(NULL, "Windows refuses to report a version",
- str, MB_OK | MB_ICONEXCLAMATION);
- sfree(str);
- return 1;
- }
+ init_winver();
/*
* If we're running a version of Windows that doesn't support
* WM_MOUSEWHEEL, find out what message number we should be
* using instead.
*/
- if (osVersion.dwMajorVersion < 4 ||
- (osVersion.dwMajorVersion == 4 &&
- osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT))
+ if (osMajorVersion < 4 ||
+ (osMajorVersion == 4 && osPlatformId != VER_PLATFORM_WIN32_NT))
wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG");
init_help();
@@ -3111,8 +3103,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
int n;
char *buff;
- if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
- osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
+ if (osPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
+ osPlatformId == VER_PLATFORM_WIN32s)
+ break; /* no Unicode */
if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
break; /* fall back to DefWindowProc */
@@ -3317,10 +3310,10 @@ static void sys_cursor_update(void)
SetCaretPos(caret_x, caret_y);
/* IMM calls on Win98 and beyond only */
- if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
+ if (osPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
- if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
- osVersion.dwMinorVersion == 0) return; /* 95 */
+ if (osPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
+ osMinorVersion == 0) return; /* 95 */
/* we should have the IMM functions */
hIMC = ImmGetContext(hwnd);
@@ -4672,7 +4665,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
/* XXX how do we know what the max size of the keys array should
* be is? There's indication on MS' website of an Inquire/InquireEx
* functioning returning a KBINFO structure which tells us. */
- if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT && p_ToUnicodeEx) {
+ if (osPlatformId == VER_PLATFORM_WIN32_NT && p_ToUnicodeEx) {
r = p_ToUnicodeEx(wParam, scan, keystate, keys_unicode,
lenof(keys_unicode), 0, kbd_layout);
} else {
@@ -5671,7 +5664,7 @@ void do_beep(void *frontend, int mode)
* We must beep in different ways depending on whether this
* is a 95-series or NT-series OS.
*/
- if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ if (osPlatformId == VER_PLATFORM_WIN32_NT)
Beep(800, 100);
else
MessageBeep(-1);
diff --git a/windows/winjump.c b/windows/winjump.c
index d0dea863..7e7b34e8 100644
--- a/windows/winjump.c
+++ b/windows/winjump.c
@@ -688,8 +688,7 @@ void clear_jumplist(void)
/* Adds a saved session to the Windows 7 jumplist. */
void add_session_to_jumplist(const char * const sessionname)
{
- if ((osVersion.dwMajorVersion < 6) ||
- (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))
+ if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1))
return; /* do nothing on pre-Win7 systems */
if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
@@ -703,8 +702,7 @@ void add_session_to_jumplist(const char * const sessionname)
/* Removes a saved session from the Windows jumplist. */
void remove_session_from_jumplist(const char * const sessionname)
{
- if ((osVersion.dwMajorVersion < 6) ||
- (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))
+ if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1))
return; /* do nothing on pre-Win7 systems */
if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) {
diff --git a/windows/winmisc.c b/windows/winmisc.c
index e65ba1c9..4a53757e 100644
--- a/windows/winmisc.c
+++ b/windows/winmisc.c
@@ -4,13 +4,14 @@
#include
#include
+#include
#include "putty.h"
#ifndef SECURITY_WIN32
#define SECURITY_WIN32
#endif
#include
-OSVERSIONINFO osVersion;
+DWORD osMajorVersion, osMinorVersion, osPlatformId;
char *platform_get_x_display(void) {
/* We may as well check for DISPLAY in case it's useful. */
@@ -186,11 +187,41 @@ void dll_hijacking_protection(void)
}
}
-BOOL init_winver(void)
+void init_winver(void)
{
+ OSVERSIONINFO osVersion;
+ static HMODULE kernel32_module;
+ DECL_WINDOWS_FUNCTION(static, BOOL, GetVersionExA, (LPOSVERSIONINFO));
+
+ if (!kernel32_module) {
+ kernel32_module = load_system32_dll("kernel32.dll");
+ /* Deliberately don't type-check this function, because that
+ * would involve using its declaration in a header file which
+ * triggers a deprecation warning. I know it's deprecated (see
+ * below) and don't need telling. */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, GetVersionExA);
+ }
+
ZeroMemory(&osVersion, sizeof(osVersion));
osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
- return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
+ if (p_GetVersionExA && p_GetVersionExA(&osVersion)) {
+ osMajorVersion = osVersion.dwMajorVersion;
+ osMinorVersion = osVersion.dwMinorVersion;
+ osPlatformId = osVersion.dwPlatformId;
+ } else {
+ /*
+ * GetVersionEx is deprecated, so allow for it perhaps going
+ * away in future API versions. If it's not there, simply
+ * assume that's because Windows is too _new_, so fill in the
+ * variables we care about to a value that will always compare
+ * higher than any given test threshold.
+ *
+ * Normally we should be checking against the presence of a
+ * specific function if possible in any case.
+ */
+ osMajorVersion = osMinorVersion = UINT_MAX; /* a very high number */
+ osPlatformId = VER_PLATFORM_WIN32_NT; /* not Win32s or Win95-like */
+ }
}
HMODULE load_system32_dll(const char *libname)
diff --git a/windows/winpgnt.c b/windows/winpgnt.c
index 4aff8338..9c57a2d6 100644
--- a/windows/winpgnt.c
+++ b/windows/winpgnt.c
@@ -1129,14 +1129,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
* Determine whether we're an NT system (should have security
* APIs) or a non-NT system (don't do security).
*/
- if (!init_winver())
- {
- modalfatalbox("Windows refuses to report a version");
- }
- if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
- has_security = TRUE;
- } else
- has_security = FALSE;
+ init_winver();
+ has_security = (osPlatformId == VER_PLATFORM_WIN32_NT);
if (has_security) {
#ifndef NO_SECURITY
diff --git a/windows/winprint.c b/windows/winprint.c
index 6a1a74c2..083f6e1c 100644
--- a/windows/winprint.c
+++ b/windows/winprint.c
@@ -105,7 +105,7 @@ printer_enum *printer_start_enum(int *nprinters_ptr)
* PRINTER_INFO_5 is recommended.
* Bletch.
*/
- if (osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT) {
+ if (osPlatformId != VER_PLATFORM_WIN32_NT) {
ret->enum_level = 5;
} else {
ret->enum_level = 4;
diff --git a/windows/winstuff.h b/windows/winstuff.h
index 20e8b97e..5dc18fb1 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -521,9 +521,9 @@ void show_help(HWND hwnd);
/*
* Exports from winmisc.c.
*/
-extern OSVERSIONINFO osVersion;
+GLOBAL DWORD osMajorVersion, osMinorVersion, osPlatformId;
+void init_winver(void);
void dll_hijacking_protection(void);
-BOOL init_winver(void);
HMODULE load_system32_dll(const char *libname);
const char *win_strerror(int error);
void restrict_process_acl(void);
From 405800290dd0bcfe0e509b7a46763e42be5f3e35 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 15:38:06 +0100
Subject: [PATCH 352/607] Fix assertion failure on ssh.com export of ECDSA.
It's a key type that format doesn't know how to handle, but that's no
excuse to fail an assertion - we have a perfectly good failure code we
can return from the export function, so we should use it.
---
import.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/import.c b/import.c
index ef55b28e..218670ee 100644
--- a/import.c
+++ b/import.c
@@ -2265,8 +2265,7 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
initial_zero = 1;
type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}";
} else {
- assert(0); /* zoinks! */
- exit(1); /* XXX: GCC doesn't understand assert() on some systems. */
+ goto error; /* unsupported key type */
}
outblob = strbuf_new();
From 0603256964a9f3c7e918affd0f904f6a3a234e1f Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 15:38:57 +0100
Subject: [PATCH 353/607] Unix Pageant: add alias '-L' for '--private-openssh'.
Matches the -L option in Unix PuTTYgen, and is much easier to type.
---
unix/uxpgnt.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
index c11bde03..0e37c1a0 100644
--- a/unix/uxpgnt.c
+++ b/unix/uxpgnt.c
@@ -108,7 +108,7 @@ static void usage(void)
printf(" pageant -a [key files]\n");
printf(" pageant -d [key identifiers]\n");
printf(" pageant --public [key identifiers]\n");
- printf(" pageant --public-openssh [key identifiers]\n");
+ printf(" pageant ( --public-openssh | -L ) [key identifiers]\n");
printf(" pageant -l\n");
printf(" pageant -D\n");
printf("Lifetime options, for running Pageant as an agent:\n");
@@ -121,7 +121,7 @@ static void usage(void)
printf(" -a add key(s) to the existing agent\n");
printf(" -l list currently loaded key fingerprints and comments\n");
printf(" --public print public keys in RFC 4716 format\n");
- printf(" --public-openssh print public keys in OpenSSH format\n");
+ printf(" --public-openssh, -L print public keys in OpenSSH format\n");
printf(" -d delete key(s) from the agent\n");
printf(" -D delete all keys from the agent\n");
printf("Other options:\n");
@@ -1052,7 +1052,7 @@ int main(int argc, char **argv)
add_keyact(KEYACT_CLIENT_LIST, NULL);
} else if (!strcmp(p, "--public")) {
curr_keyact = KEYACT_CLIENT_PUBLIC;
- } else if (!strcmp(p, "--public-openssh")) {
+ } else if (!strcmp(p, "--public-openssh") || !strcmp(p, "-L")) {
curr_keyact = KEYACT_CLIENT_PUBLIC_OPENSSH;
} else if (!strcmp(p, "-X")) {
life = LIFE_X11;
From f4314b8d66f12b045ce22697fae4fd8df47bce58 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 3 Jun 2018 21:48:08 +0100
Subject: [PATCH 354/607] Fix a few compiler warnings from MinGW.
A few variables that gcc couldn't tell I'd initialised on all the
important paths, a variable that didn't really need to be there
anyway, and yet another use of GET_WINDOWS_FUNCTION_NO_TYPECHECK.
---
windows/wincons.c | 2 +-
windows/window.c | 2 +-
windows/winnet.c | 6 +++++-
windows/winser.c | 6 +++---
4 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/windows/wincons.c b/windows/wincons.c
index 120bffe8..e315f64a 100644
--- a/windows/wincons.c
+++ b/windows/wincons.c
@@ -354,7 +354,7 @@ static void console_data_untrusted(HANDLE hout, const char *data, int len)
int console_get_userpass_input(prompts_t *p)
{
- HANDLE hin, hout;
+ HANDLE hin = INVALID_HANDLE_VALUE, hout = INVALID_HANDLE_VALUE;
size_t curr_prompt;
/*
diff --git a/windows/window.c b/windows/window.c
index c635720d..1a7bb472 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -1016,7 +1016,7 @@ void update_specials_menu(void *frontend)
static void update_mouse_pointer(void)
{
- LPTSTR curstype;
+ LPTSTR curstype = NULL;
int force_visible = FALSE;
static int forced_visible = FALSE;
switch (busy_status) {
diff --git a/windows/winnet.c b/windows/winnet.c
index ac2eae27..b5efc955 100644
--- a/windows/winnet.c
+++ b/windows/winnet.c
@@ -290,7 +290,11 @@ void sk_init(void)
GET_WINDOWS_FUNCTION(winsock_module, WSAAsyncSelect);
GET_WINDOWS_FUNCTION(winsock_module, WSAEventSelect);
- GET_WINDOWS_FUNCTION(winsock_module, select);
+ /* We don't type-check select because at least some MinGW versions
+ * of the Windows API headers seem to disagree with the
+ * documentation on whether the 'struct timeval *' pointer is
+ * const or not. */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, select);
GET_WINDOWS_FUNCTION(winsock_module, WSAGetLastError);
GET_WINDOWS_FUNCTION(winsock_module, WSAEnumNetworkEvents);
GET_WINDOWS_FUNCTION(winsock_module, WSAStartup);
diff --git a/windows/winser.c b/windows/winser.c
index 646cd254..976f8955 100644
--- a/windows/winser.c
+++ b/windows/winser.c
@@ -291,12 +291,12 @@ static void serial_free(void *handle)
static void serial_reconfig(void *handle, Conf *conf)
{
Serial serial = (Serial) handle;
- const char *err;
- err = serial_configure(serial, serial->port, conf);
+ serial_configure(serial, serial->port, conf);
/*
- * FIXME: what should we do if err returns something?
+ * FIXME: what should we do if that call returned a non-NULL error
+ * message?
*/
}
From 10a4f1156c59d44ef146f1c4ec665785025b1057 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 4 Jun 2018 19:10:57 +0100
Subject: [PATCH 355/607] Add a GDB Python script to pretty-print Bignum.
I've been playing around with GDB's Python scripting system recently,
and this is a thing I've always thought it would be nice to be able to
do: if you load this script (which, on Ubuntu 18.04's gdb, is as
simple as 'source contrib/gdb.py' at the gdb prompt, or similar), then
variables of type Bignum will be printed as (e.g.) 'Bignum(0x12345)',
or 'Bignum(NULL)' if they're null pointers, or a fallback
representation if they're non-null pointers but gdb can't read
anything sensible from them.
---
contrib/gdb.py | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 contrib/gdb.py
diff --git a/contrib/gdb.py b/contrib/gdb.py
new file mode 100644
index 00000000..14574bb2
--- /dev/null
+++ b/contrib/gdb.py
@@ -0,0 +1,37 @@
+import gdb
+import re
+import gdb.printing
+
+class PuTTYBignumPrettyPrinter(gdb.printing.PrettyPrinter):
+ "Pretty-print PuTTY's Bignum type."
+ name = "Bignum"
+
+ def __init__(self, val):
+ super(PuTTYBignumPrettyPrinter, self).__init__(self.name)
+ self.val = val
+
+ def to_string(self):
+ type_BignumInt = gdb.lookup_type("BignumInt")
+ type_BignumIntPtr = type_BignumInt.pointer()
+ BIGNUM_INT_BITS = 8 * type_BignumInt.sizeof
+ array = self.val.cast(type_BignumIntPtr)
+ aget = lambda i: int(array[i]) & ((1 << BIGNUM_INT_BITS)-1)
+
+ try:
+ length = aget(0)
+ value = 0
+ for i in range(length):
+ value |= aget(i+1) << (BIGNUM_INT_BITS * i)
+ return "Bignum({:#x})".format(value)
+
+ except gdb.MemoryError:
+ address = int(array)
+ if address == 0:
+ return "Bignum(NULL)".format(address)
+ return "Bignum(invalid @ {:#x})".format(address)
+
+rcpp = gdb.printing.RegexpCollectionPrettyPrinter("PuTTY")
+rcpp.add_printer(PuTTYBignumPrettyPrinter.name, "^Bignum$",
+ PuTTYBignumPrettyPrinter)
+
+gdb.printing.register_pretty_printer(None, rcpp)
From accb6931ce7f49336436c7e51c577af2ed4c2d1f Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 4 Jun 2018 19:13:13 +0100
Subject: [PATCH 356/607] Add HTTP redirects for the Windows on Arm installers.
There's always one - I did everything else in the build script, but
forgot to arrange for the wa32 and wa64 output subdirs to have a
.htaccess redirect from a fixed name like 'putty-arm64-installer.msi'
to whatever the real file name is in that particular build.
---
Buildscr | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Buildscr b/Buildscr
index 5f67d065..08be3ec6 100644
--- a/Buildscr
+++ b/Buildscr
@@ -267,4 +267,4 @@ in-dest putty do echo "AddType application/octet-stream .hlp" >> .htaccess
in-dest putty do echo "AddType application/octet-stream .cnt" >> .htaccess
in-dest putty do set -- putty*.tar.gz; for k in '' .gpg; do echo RedirectMatch temp '(.*/)'putty.tar.gz$$k\$$ '$$1'"$$1$$k" >> .htaccess; done
# And one in each binary directory, providing links for the installers.
-in-dest putty do for params in "w32 putty-installer" "w64 putty-64bit-installer"; do (set -- $$params; subdir=$$1; installername=$$2; cd $$subdir && for ext in msi exe; do set -- putty*installer.$$ext; if test -f $$1; then for k in '' .gpg; do echo RedirectMatch temp '(.*/)'$${installername}.$$ext$$k\$$ '$$1'"$$1$$k" >> .htaccess; done; fi; done); done
+in-dest putty do for params in "w32 putty-installer" "w64 putty-64bit-installer" "wa32 putty-arm32-installer" "wa64 putty-arm64-installer"; do (set -- $$params; subdir=$$1; installername=$$2; cd $$subdir && for ext in msi exe; do set -- putty*installer.$$ext; if test -f $$1; then for k in '' .gpg; do echo RedirectMatch temp '(.*/)'$${installername}.$$ext$$k\$$ '$$1'"$$1$$k" >> .htaccess; done; fi; done); done
From 22d2c721013ee229d154af43a074539e439ffbba Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 4 Jun 2018 19:14:33 +0100
Subject: [PATCH 357/607] x11_get_auth_from_authfile: correct MAX_RECORD_SIZE.
I reset this to a very small value during testing, because my real
.Xauthority file is not absurdly enormous, so this was the easiest way
to check the algorithm that periodically moves everything up the
buffer.
Then that test found and fixed a bug, and of course my temporary test
value of MAX_RECORD_SIZE got swept up in the 'git commit -a --amend',
and pushed with the rest of the refactoring, and I didn't notice until
today.
---
x11fwd.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/x11fwd.c b/x11fwd.c
index bd4c9289..190b6f68 100644
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -450,7 +450,15 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
int displaynum;
int ideal_match = FALSE;
char *ourhostname;
- const size_t MAX_RECORD_SIZE = 0x80, BUF_SIZE = 2 * MAX_RECORD_SIZE;
+
+ /* A maximally sized (wildly implausible) .Xauthority record
+ * consists of a 16-bit integer to start with, then four strings,
+ * each of which has a 16-bit length field followed by that many
+ * bytes of data (i.e. up to 0xFFFF bytes). */
+ const size_t MAX_RECORD_SIZE = 2 + 4 * (2+0xFFFF);
+
+ /* We'll want a buffer of twice that size (see below). */
+ const size_t BUF_SIZE = 2 * MAX_RECORD_SIZE;
/*
* Normally we should look for precisely the details specified in
From 452114c3d3f4340d14f1117055f0deaecb9e64ef Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 6 Jun 2018 06:42:52 +0100
Subject: [PATCH 358/607] New memory management macro 'snew_plus'.
This formalises my occasional habit of using a single malloc to make a
block that contains a header structure and a data buffer that a field
of the structure will point to, allowing it to be freed in one go
later. Previously I had to do this by hand, losing the type-checking
advantages of snew; now I've written an snew-style macro to do the
job, plus an accessor macro to cleanly get the auxiliary buffer
pointer afterwards, and switched existing instances of the pattern
over to using that.
---
puttymem.h | 15 +++++++++++++++
sshshare.c | 11 ++++-------
unix/gtkfont.c | 10 ++++------
3 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/puttymem.h b/puttymem.h
index 941aded3..b927f1ba 100644
--- a/puttymem.h
+++ b/puttymem.h
@@ -49,4 +49,19 @@ void safefree(void *);
((type *)snrealloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr), \
(n), sizeof(type)))
+/*
+ * For cases where you want to allocate a struct plus a subsidiary
+ * data buffer in one step, this macro lets you add a constant to the
+ * amount malloced.
+ *
+ * Since the return value is already cast to the struct type, a
+ * pointer to that many bytes of extra data can be conveniently
+ * obtained by simply adding 1 to the returned pointer!
+ * snew_plus_get_aux is a handy macro that does that and casts the
+ * result to void *, so you can assign it straight to wherever you
+ * wanted it.
+ */
+#define snew_plus(type, extra) ((type *)snmalloc(1, sizeof(type) + (extra)))
+#define snew_plus_get_aux(ptr) ((void *)((ptr) + 1))
+
#endif
diff --git a/sshshare.c b/sshshare.c
index 895c99c8..d5faa3ab 100644
--- a/sshshare.c
+++ b/sshshare.c
@@ -932,17 +932,14 @@ static void share_closing(Plug plug, const char *error_msg, int error_code,
static void share_xchannel_add_message(
struct share_xchannel *xc, int type, const void *data, int len)
{
- unsigned char *block;
struct share_xchannel_message *msg;
/*
- * Be a little tricksy here by allocating a single memory block
- * containing both the 'struct share_xchannel_message' and the
- * actual data. Simplifies freeing it later.
+ * Allocate the 'struct share_xchannel_message' and the actual
+ * data in one unit.
*/
- block = smalloc(sizeof(struct share_xchannel_message) + len);
- msg = (struct share_xchannel_message *)block;
- msg->data = block + sizeof(struct share_xchannel_message);
+ msg = snew_plus(struct share_xchannel_message, len);
+ msg->data = snew_plus_get_aux(msg);
msg->datalen = len;
msg->type = type;
memcpy(msg->data, data, len);
diff --git a/unix/gtkfont.c b/unix/gtkfont.c
index 4dd7919c..086669e2 100644
--- a/unix/gtkfont.c
+++ b/unix/gtkfont.c
@@ -258,7 +258,6 @@ struct xlfd_decomposed {
static struct xlfd_decomposed *xlfd_decompose(const char *xlfd)
{
- void *mem;
char *p, *components[14];
struct xlfd_decomposed *dec;
int i;
@@ -266,15 +265,14 @@ static struct xlfd_decomposed *xlfd_decompose(const char *xlfd)
if (!xlfd)
return NULL;
- mem = smalloc(sizeof(struct xlfd_decomposed) + strlen(xlfd) + 1);
- p = ((char *)mem) + sizeof(struct xlfd_decomposed);
+ dec = snew_plus(struct xlfd_decomposed, strlen(xlfd) + 1);
+ p = snew_plus_get_aux(dec);
strcpy(p, xlfd);
- dec = (struct xlfd_decomposed *)mem;
for (i = 0; i < 14; i++) {
if (*p != '-') {
/* Malformed XLFD: not enough '-' */
- sfree(mem);
+ sfree(dec);
return NULL;
}
*p++ = '\0';
@@ -283,7 +281,7 @@ static struct xlfd_decomposed *xlfd_decompose(const char *xlfd)
}
if (*p) {
/* Malformed XLFD: too many '-' */
- sfree(mem);
+ sfree(dec);
return NULL;
}
From 61a972c3326abe0a0276cda48ac15c9195f4b161 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 6 Jun 2018 07:19:57 +0100
Subject: [PATCH 359/607] Make share_got_pkt_from_server take a const pointer.
It was horrible - even if harmless in practice - that it wrote the
NATed channel id over its input buffer, and I think it's worth the
extra memory management to avoid doing that.
---
ssh.h | 2 +-
sshshare.c | 10 +++++++---
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/ssh.h b/ssh.h
index 6cc925f9..1c0f276c 100644
--- a/ssh.h
+++ b/ssh.h
@@ -27,7 +27,7 @@ extern Socket ssh_connection_sharing_init(
void **state);
int ssh_share_test_for_upstream(const char *host, int port, Conf *conf);
void share_got_pkt_from_server(void *ctx, int type,
- unsigned char *pkt, int pktlen);
+ const void *pkt, int pktlen);
void share_activate(void *state, const char *server_verstring);
void sharestate_free(void *state);
int share_ndownstreams(void *state);
diff --git a/sshshare.c b/sshshare.c
index d5faa3ab..30fe7360 100644
--- a/sshshare.c
+++ b/sshshare.c
@@ -1129,8 +1129,9 @@ void share_setup_x11_channel(void *csv, void *chanv,
}
void share_got_pkt_from_server(void *csv, int type,
- unsigned char *pkt, int pktlen)
+ const void *vpkt, int pktlen)
{
+ const unsigned char *pkt = (const unsigned char *)vpkt;
struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)csv;
struct share_globreq *globreq;
size_t id_pos;
@@ -1203,8 +1204,11 @@ void share_got_pkt_from_server(void *csv, int type,
/*
* The normal case: this id refers to an open channel.
*/
- PUT_32BIT(pkt + id_pos, chan->downstream_id);
- send_packet_to_downstream(cs, type, pkt, pktlen, chan);
+ unsigned char *rewritten = snewn(pktlen, unsigned char);
+ memcpy(rewritten, pkt, pktlen);
+ PUT_32BIT(rewritten + id_pos, chan->downstream_id);
+ send_packet_to_downstream(cs, type, rewritten, pktlen, chan);
+ sfree(rewritten);
/*
* Update the channel state, for messages that need it.
From ce6c65aba1e11e7293cefc0d9d2bd7377582edf5 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 5 Jun 2018 08:20:10 +0100
Subject: [PATCH 360/607] Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
---
ssh.c | 381 ++++++++++++++++++++++++++++++----------------------------
1 file changed, 196 insertions(+), 185 deletions(-)
diff --git a/ssh.c b/ssh.c
index 17202e85..7de32b50 100644
--- a/ssh.c
+++ b/ssh.c
@@ -359,28 +359,29 @@ enum {
#define crMaybeWaitUntil(c) do { while (!(c)) crReturn(0); } while (0)
#define crMaybeWaitUntilV(c) do { while (!(c)) crReturnV; } while (0)
-struct Packet;
-
-static struct Packet *ssh1_pkt_init(int pkt_type);
-static struct Packet *ssh2_pkt_init(int pkt_type);
-static void ssh_pkt_ensure(struct Packet *, int length);
-static void ssh_pkt_adddata(struct Packet *, const void *data, int len);
-static int ssh2_pkt_construct(Ssh, struct Packet *);
-static void ssh2_pkt_send(Ssh, struct Packet *);
-static void ssh2_pkt_send_noqueue(Ssh, struct Packet *);
+typedef struct PktIn PktIn;
+typedef struct PktOut PktOut;
+
+static struct PktOut *ssh1_pkt_init(int pkt_type);
+static struct PktOut *ssh2_pkt_init(int pkt_type);
+static void ssh_pkt_ensure(struct PktOut *, int length);
+static void ssh_pkt_adddata(struct PktOut *, const void *data, int len);
+static int ssh2_pkt_construct(Ssh, struct PktOut *);
+static void ssh2_pkt_send(Ssh, struct PktOut *);
+static void ssh2_pkt_send_noqueue(Ssh, struct PktOut *);
static void do_ssh1_login(void *vctx);
static void do_ssh2_userauth(void *vctx);
static void ssh2_connection_setup(Ssh ssh);
static void do_ssh2_connection(void *vctx);
static void ssh_channel_init(struct ssh_channel *c);
-static struct ssh_channel *ssh_channel_msg(Ssh ssh, struct Packet *pktin);
+static struct ssh_channel *ssh_channel_msg(Ssh ssh, PktIn *pktin);
static void ssh_channel_got_eof(struct ssh_channel *c);
static void ssh2_channel_check_close(struct ssh_channel *c);
static void ssh_channel_close_local(struct ssh_channel *c, char const *reason);
static void ssh_channel_destroy(struct ssh_channel *c);
static void ssh_channel_unthrottle(struct ssh_channel *c, int bufsize);
-static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin);
-static void ssh2_general_packet_processing(Ssh ssh, struct Packet *pktin);
+static void ssh2_msg_something_unimplemented(Ssh ssh, PktIn *pktin);
+static void ssh2_general_packet_processing(Ssh ssh, PktIn *pktin);
static void ssh1_login_input(Ssh ssh);
static void ssh2_userauth_input(Ssh ssh);
static void ssh2_connection_input(Ssh ssh);
@@ -496,9 +497,9 @@ enum { /* channel types */
CHAN_ZOMBIE
};
-typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin);
-typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx);
-typedef void (*cchandler_fn_t)(struct ssh_channel *, struct Packet *, void *);
+typedef void (*handler_fn_t)(Ssh ssh, PktIn *pktin);
+typedef void (*chandler_fn_t)(Ssh ssh, PktIn *pktin, void *ctx);
+typedef void (*cchandler_fn_t)(struct ssh_channel *, PktIn *, void *);
/*
* Each channel has a queue of outstanding CHANNEL_REQUESTS and their
@@ -669,30 +670,26 @@ struct ssh_portfwd {
((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )
-struct Packet {
+struct PktIn {
int refcount;
- long length; /* length of packet: see below */
- long forcepad; /* SSH-2: force padding to at least this length */
- int type; /* only used for incoming packets */
+ long length; /* length relative to 'body' */
+ int type;
unsigned long sequence; /* SSH-2 incoming sequence number */
unsigned char *data; /* allocated storage */
unsigned char *body; /* offset of payload within `data' */
long maxlen; /* amount of storage allocated for `data' */
long encrypted_len; /* for SSH-2 total-size counting */
+ BinarySource_IMPLEMENTATION;
+};
- /*
- * A note on the 'length' field above.
- *
- * Incoming packets are set up so that pkt->length is measured
- * relative to pkt->body, which itself points to a few bytes after
- * pkt->data (skipping some uninteresting header fields including
- * the packet type code).
- *
- * During construction of an outgoing packet, however, pkt->length
- * is measured relative to the base pointer pkt->data, and
- * pkt->body is not really used for anything until the packet is
- * ready for sending.
- */
+struct PktOut {
+ long length; /* length relative to 'data' */
+ int type;
+ long forcepad; /* SSH-2: force padding to at least this length */
+ unsigned char *data; /* allocated storage */
+ unsigned char *body; /* offset of payload within `data' */
+ long maxlen; /* amount of storage allocated for `data' */
+ long encrypted_len; /* for SSH-2 total-size counting */
/* Extra metadata used in SSH packet logging mode, allowing us to
* log in the packet header line that the packet came from a
@@ -703,7 +700,6 @@ struct Packet {
const char *additional_log_text;
BinarySink_IMPLEMENTATION;
- BinarySource_IMPLEMENTATION;
};
static void ssh1_protocol_setup(Ssh ssh);
@@ -722,15 +718,15 @@ static void ssh2_timer(void *ctx, unsigned long now);
static int ssh2_timer_update(Ssh ssh, unsigned long rekey_time);
#ifndef NO_GSSAPI
static void ssh2_gss_update(Ssh ssh, int definitely_rekeying);
-static struct Packet *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
- const char *authtype);
+static PktOut *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
+ const char *authtype);
#endif
-static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin);
-static void ssh_unref_packet(struct Packet *pkt);
+static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin);
+static void ssh_unref_packet(PktIn *pkt);
struct PacketQueueNode {
struct PacketQueueNode *next, *prev;
- struct Packet *pkt;
+ PktIn *pkt;
};
struct PacketQueue {
@@ -743,7 +739,7 @@ static void pq_init(struct PacketQueue *pq)
pq->end.pkt = NULL;
}
-static void pq_push(struct PacketQueue *pq, struct Packet *pkt)
+static void pq_push(struct PacketQueue *pq, PktIn *pkt)
{
struct PacketQueueNode *node = snew(struct PacketQueueNode);
node->pkt = pkt;
@@ -753,7 +749,7 @@ static void pq_push(struct PacketQueue *pq, struct Packet *pkt)
node->prev->next = node;
}
-static void pq_push_front(struct PacketQueue *pq, struct Packet *pkt)
+static void pq_push_front(struct PacketQueue *pq, PktIn *pkt)
{
struct PacketQueueNode *node = snew(struct PacketQueueNode);
node->pkt = pkt;
@@ -763,15 +759,15 @@ static void pq_push_front(struct PacketQueue *pq, struct Packet *pkt)
node->prev->next = node;
}
-static struct Packet *pq_peek(struct PacketQueue *pq)
+static PktIn *pq_peek(struct PacketQueue *pq)
{
return pq->end.next->pkt; /* works even if next == &end, because
* end.pkt is NULL */
}
-static struct Packet *pq_pop(struct PacketQueue *pq)
+static PktIn *pq_pop(struct PacketQueue *pq)
{
- struct Packet *pkt;
+ PktIn *pkt;
struct PacketQueueNode *node;
node = pq->end.next;
@@ -788,7 +784,7 @@ static struct Packet *pq_pop(struct PacketQueue *pq)
static void pq_clear(struct PacketQueue *pq)
{
- struct Packet *pkt;
+ PktIn *pkt;
while ((pkt = pq_pop(pq)) != NULL)
ssh_unref_packet(pkt);
}
@@ -816,20 +812,20 @@ struct rdpkt1_state_tag {
long len, pad, biglen;
unsigned long realcrc, gotcrc;
int chunk;
- struct Packet *pktin;
+ PktIn *pktin;
};
struct rdpkt2_state_tag {
long len, pad, payload, packetlen, maclen;
int cipherblk;
unsigned long incoming_sequence;
- struct Packet *pktin;
+ PktIn *pktin;
};
struct rdpkt2_bare_state_tag {
long packetlen;
unsigned long incoming_sequence;
- struct Packet *pktin;
+ PktIn *pktin;
};
struct queued_handler;
@@ -946,7 +942,7 @@ struct ssh_tag {
int sent_console_eof;
int got_pty; /* affects EOF behaviour on main channel */
- struct Packet **queue;
+ PktOut **queue;
int queuelen, queuesize;
int queueing;
unsigned char *deferred_send_data;
@@ -1021,7 +1017,7 @@ struct ssh_tag {
struct rdpkt2_state_tag rdpkt2_state;
struct rdpkt2_bare_state_tag rdpkt2_bare_state;
- void (*general_packet_processing)(Ssh ssh, struct Packet *pkt);
+ void (*general_packet_processing)(Ssh ssh, PktIn *pkt);
void (*current_incoming_data_fn) (Ssh ssh);
void (*current_user_input_fn) (Ssh ssh);
@@ -1377,28 +1373,34 @@ static void c_write_str(Ssh ssh, const char *buf)
c_write(ssh, buf, strlen(buf));
}
-static void ssh_unref_packet(struct Packet *pkt)
+static void ssh_unref_packet(PktIn *pkt)
{
if (--pkt->refcount <= 0) {
sfree(pkt->data);
sfree(pkt);
}
}
+
+static void ssh_free_pktout(PktOut *pkt)
+{
+ sfree(pkt->data);
+ sfree(pkt);
+}
+
static void ssh_pkt_BinarySink_write(BinarySink *bs,
const void *data, size_t len);
-static struct Packet *ssh_new_packet(void)
+static PktOut *ssh_new_packet(void)
{
- struct Packet *pkt = snew(struct Packet);
+ PktOut *pkt = snew(PktOut);
BinarySink_INIT(pkt, ssh_pkt_BinarySink_write);
pkt->body = pkt->data = NULL;
pkt->maxlen = 0;
- pkt->refcount = 1;
return pkt;
}
-static void ssh1_log_incoming_packet(Ssh ssh, const struct Packet *pkt)
+static void ssh1_log_incoming_packet(Ssh ssh, const PktIn *pkt)
{
int nblanks = 0;
struct logblank_t blanks[4];
@@ -1428,7 +1430,7 @@ static void ssh1_log_incoming_packet(Ssh ssh, const struct Packet *pkt)
0, NULL);
}
-static void ssh1_log_outgoing_packet(Ssh ssh, const struct Packet *pkt)
+static void ssh1_log_outgoing_packet(Ssh ssh, const PktOut *pkt)
{
int nblanks = 0;
struct logblank_t blanks[4];
@@ -1508,7 +1510,10 @@ static void ssh1_rdpkt(Ssh ssh)
crBegin(ssh->ssh1_rdpkt_crstate);
while (1) {
- st->pktin = ssh_new_packet();
+ st->pktin = snew(PktIn);
+ st->pktin->body = st->pktin->data = NULL;
+ st->pktin->maxlen = 0;
+ st->pktin->refcount = 1;
st->pktin->type = 0;
st->pktin->length = 0;
@@ -1613,7 +1618,7 @@ static void ssh1_rdpkt(Ssh ssh)
crFinishV;
}
-static void ssh2_log_incoming_packet(Ssh ssh, const struct Packet *pkt)
+static void ssh2_log_incoming_packet(Ssh ssh, const PktIn *pkt)
{
int nblanks = 0;
struct logblank_t blanks[4];
@@ -1644,7 +1649,7 @@ static void ssh2_log_incoming_packet(Ssh ssh, const struct Packet *pkt)
0, NULL);
}
-static void ssh2_log_outgoing_packet(Ssh ssh, const struct Packet *pkt)
+static void ssh2_log_outgoing_packet(Ssh ssh, const PktOut *pkt)
{
int nblanks = 0;
struct logblank_t blanks[4];
@@ -1753,7 +1758,10 @@ static void ssh2_rdpkt(Ssh ssh)
crBegin(ssh->ssh2_rdpkt_crstate);
while (1) {
- st->pktin = ssh_new_packet();
+ st->pktin = snew(PktIn);
+ st->pktin->body = st->pktin->data = NULL;
+ st->pktin->maxlen = 0;
+ st->pktin->refcount = 1;
st->pktin->type = 0;
st->pktin->length = 0;
@@ -2077,7 +2085,10 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
crStopV;
}
- st->pktin = ssh_new_packet();
+ st->pktin = snew(PktIn);
+ st->pktin->body = NULL;
+ st->pktin->maxlen = 0;
+ st->pktin->refcount = 1;
st->pktin->data = snewn(st->packetlen, unsigned char);
st->pktin->encrypted_len = st->packetlen;
@@ -2127,7 +2138,7 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
crFinishV;
}
-static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p)
+static int s_wrpkt_prepare(Ssh ssh, PktOut *pkt, int *offset_p)
{
int pad, biglen, i, pktoffs;
unsigned long crc;
@@ -2188,17 +2199,17 @@ static int s_write(Ssh ssh, void *data, int len)
return sk_write(ssh->s, data, len);
}
-static void s_wrpkt(Ssh ssh, struct Packet *pkt)
+static void s_wrpkt(Ssh ssh, PktOut *pkt)
{
int len, backlog, offset;
len = s_wrpkt_prepare(ssh, pkt, &offset);
backlog = s_write(ssh, pkt->data + offset, len);
if (backlog > SSH_MAX_BACKLOG)
ssh_throttle_all(ssh, 1, backlog);
- ssh_unref_packet(pkt);
+ ssh_free_pktout(pkt);
}
-static void s_wrpkt_defer(Ssh ssh, struct Packet *pkt)
+static void s_wrpkt_defer(Ssh ssh, PktOut *pkt)
{
int len, offset;
len = s_wrpkt_prepare(ssh, pkt, &offset);
@@ -2211,7 +2222,7 @@ static void s_wrpkt_defer(Ssh ssh, struct Packet *pkt)
memcpy(ssh->deferred_send_data + ssh->deferred_len,
pkt->data + offset, len);
ssh->deferred_len += len;
- ssh_unref_packet(pkt);
+ ssh_free_pktout(pkt);
}
/*
@@ -2219,11 +2230,11 @@ static void s_wrpkt_defer(Ssh ssh, struct Packet *pkt)
* (This all-at-once interface used to be the only one, but now SSH-1
* packets can also be constructed incrementally.)
*/
-static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap)
+static PktOut *construct_packet(Ssh ssh, int pkttype, va_list ap)
{
int argtype;
Bignum bn;
- struct Packet *pkt;
+ PktOut *pkt;
pkt = ssh1_pkt_init(pkttype);
@@ -2263,7 +2274,7 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap)
static void send_packet(Ssh ssh, int pkttype, ...)
{
- struct Packet *pkt;
+ PktOut *pkt;
va_list ap;
va_start(ap, pkttype);
pkt = construct_packet(ssh, pkttype, ap);
@@ -2273,7 +2284,7 @@ static void send_packet(Ssh ssh, int pkttype, ...)
static void defer_packet(Ssh ssh, int pkttype, ...)
{
- struct Packet *pkt;
+ PktOut *pkt;
va_list ap;
va_start(ap, pkttype);
pkt = construct_packet(ssh, pkttype, ap);
@@ -2304,7 +2315,7 @@ static int ssh_versioncmp(const char *a, const char *b)
/*
* Packet construction functions. Mostly shared between SSH-1 and SSH-2.
*/
-static void ssh_pkt_ensure(struct Packet *pkt, int length)
+static void ssh_pkt_ensure(PktOut *pkt, int length)
{
if (pkt->maxlen < length) {
unsigned char *body = pkt->body;
@@ -2314,7 +2325,7 @@ static void ssh_pkt_ensure(struct Packet *pkt, int length)
if (body) pkt->body = pkt->data + offset;
}
}
-static void ssh_pkt_adddata(struct Packet *pkt, const void *data, int len)
+static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len)
{
pkt->length += len;
ssh_pkt_ensure(pkt, pkt->length);
@@ -2324,13 +2335,13 @@ static void ssh_pkt_adddata(struct Packet *pkt, const void *data, int len)
static void ssh_pkt_BinarySink_write(BinarySink *bs,
const void *data, size_t len)
{
- struct Packet *pkt = BinarySink_DOWNCAST(bs, struct Packet);
+ PktOut *pkt = BinarySink_DOWNCAST(bs, PktOut);
ssh_pkt_adddata(pkt, data, len);
}
-static struct Packet *ssh1_pkt_init(int pkt_type)
+static PktOut *ssh1_pkt_init(int pkt_type)
{
- struct Packet *pkt = ssh_new_packet();
+ PktOut *pkt = ssh_new_packet();
pkt->length = 4 + 8; /* space for length + max padding */
put_byte(pkt, pkt_type);
pkt->body = pkt->data + pkt->length;
@@ -2340,9 +2351,9 @@ static struct Packet *ssh1_pkt_init(int pkt_type)
return pkt;
}
-static struct Packet *ssh2_pkt_init(int pkt_type)
+static PktOut *ssh2_pkt_init(int pkt_type)
{
- struct Packet *pkt = ssh_new_packet();
+ PktOut *pkt = ssh_new_packet();
pkt->length = 5; /* space for packet length + padding length */
pkt->forcepad = 0;
pkt->type = pkt_type;
@@ -2358,7 +2369,7 @@ static struct Packet *ssh2_pkt_init(int pkt_type)
* put the MAC on it. Final packet, ready to be sent, is stored in
* pkt->data. Total length is returned.
*/
-static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt)
+static int ssh2_pkt_construct(Ssh ssh, PktOut *pkt)
{
int cipherblk, maclen, padding, unencrypted_prefix, i;
@@ -2489,13 +2500,13 @@ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt)
* CBC.
*/
-static void ssh2_pkt_defer_noqueue(Ssh, struct Packet *, int);
+static void ssh2_pkt_defer_noqueue(Ssh, PktOut *, int);
static void ssh_pkt_defersend(Ssh);
/*
* Send an SSH-2 packet immediately, without queuing or deferring.
*/
-static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt)
+static void ssh2_pkt_send_noqueue(Ssh ssh, PktOut *pkt)
{
int len;
int backlog;
@@ -2520,13 +2531,13 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt)
queue_idempotent_callback(&ssh->ssh2_transport_icb);
}
- ssh_unref_packet(pkt);
+ ssh_free_pktout(pkt);
}
/*
* Defer an SSH-2 packet.
*/
-static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
+static void ssh2_pkt_defer_noqueue(Ssh ssh, PktOut *pkt, int noignore)
{
int len;
if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
@@ -2536,7 +2547,7 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
* Interpose an SSH_MSG_IGNORE to ensure that user data don't
* get encrypted with a known IV.
*/
- struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ PktOut *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
put_stringz(ipkt, "");
ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);
}
@@ -2550,19 +2561,19 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
memcpy(ssh->deferred_send_data + ssh->deferred_len, pkt->body, len);
ssh->deferred_len += len;
ssh->deferred_data_size += pkt->encrypted_len;
- ssh_unref_packet(pkt);
+ ssh_free_pktout(pkt);
}
/*
* Queue an SSH-2 packet.
*/
-static void ssh2_pkt_queue(Ssh ssh, struct Packet *pkt)
+static void ssh2_pkt_queue(Ssh ssh, PktOut *pkt)
{
assert(ssh->queueing);
if (ssh->queuelen >= ssh->queuesize) {
ssh->queuesize = ssh->queuelen + 32;
- ssh->queue = sresize(ssh->queue, ssh->queuesize, struct Packet *);
+ ssh->queue = sresize(ssh->queue, ssh->queuesize, PktOut *);
}
ssh->queue[ssh->queuelen++] = pkt;
@@ -2572,7 +2583,7 @@ static void ssh2_pkt_queue(Ssh ssh, struct Packet *pkt)
* Either queue or send a packet, depending on whether queueing is
* set.
*/
-static void ssh2_pkt_send(Ssh ssh, struct Packet *pkt)
+static void ssh2_pkt_send(Ssh ssh, PktOut *pkt)
{
if (ssh->queueing)
ssh2_pkt_queue(ssh, pkt);
@@ -2584,7 +2595,7 @@ static void ssh2_pkt_send(Ssh ssh, struct Packet *pkt)
* Either queue or defer a packet, depending on whether queueing is
* set.
*/
-static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt)
+static void ssh2_pkt_defer(Ssh ssh, PktOut *pkt)
{
if (ssh->queueing)
ssh2_pkt_queue(ssh, pkt);
@@ -2633,8 +2644,7 @@ static void ssh_pkt_defersend(Ssh ssh)
* Send a packet whose length needs to be disguised (typically
* passwords or keyboard-interactive responses).
*/
-static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt,
- int padsize)
+static void ssh2_pkt_send_with_padding(Ssh ssh, PktOut *pkt, int padsize)
{
#if 0
if (0) {
@@ -2738,7 +2748,7 @@ void bndebug(char *string, Bignum b)
* that it optionally breaks it open and fiddle with it to work around
* BUG_SSH2_RSA_PADDING.
*/
-static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt,
+static void ssh2_add_sigblob(Ssh ssh, PktOut *pkt,
const void *pkblob, int pkblob_len,
const void *sigblob, int sigblob_len)
{
@@ -3430,7 +3440,7 @@ static void ssh_process_incoming_data(void *ctx)
static void ssh_process_pq_full(void *ctx)
{
Ssh ssh = (Ssh)ctx;
- struct Packet *pktin;
+ PktIn *pktin;
while ((pktin = pq_pop(&ssh->pq_full)) != NULL) {
if (ssh->general_packet_processing)
@@ -3995,7 +4005,7 @@ static void ssh_disconnect(Ssh ssh, const char *client_reason,
send_packet(ssh, SSH1_MSG_DISCONNECT, PKT_STR, wire_reason,
PKT_END);
} else if (ssh->version == 2) {
- struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+ PktOut *pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
put_uint32(pktout, code);
put_stringz(pktout, wire_reason);
put_stringz(pktout, "en"); /* language tag */
@@ -4061,8 +4071,8 @@ int verify_ssh_manual_host_key(Ssh ssh, const char *fingerprint, ssh_key *key)
return 0;
}
-static void ssh1_coro_wrapper_initial(Ssh ssh, struct Packet *pktin);
-static void ssh1_coro_wrapper_session(Ssh ssh, struct Packet *pktin);
+static void ssh1_coro_wrapper_initial(Ssh ssh, PktIn *pktin);
+static void ssh1_coro_wrapper_session(Ssh ssh, PktIn *pktin);
static void ssh1_connection_input(Ssh ssh);
/*
@@ -4071,7 +4081,7 @@ static void ssh1_connection_input(Ssh ssh);
static void do_ssh1_login(void *vctx)
{
Ssh ssh = (Ssh)vctx;
- struct Packet *pktin;
+ PktIn *pktin;
int i, j, ret;
ptrlen pl;
@@ -5069,7 +5079,7 @@ static void ssh_channel_try_eof(struct ssh_channel *c)
PKT_END);
c->closes |= CLOSES_SENT_EOF;
} else {
- struct Packet *pktout;
+ PktOut *pktout;
pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
put_uint32(pktout, c->remoteid);
ssh2_pkt_send(ssh, pktout);
@@ -5134,7 +5144,7 @@ void sshfwd_unthrottle(struct ssh_channel *c, int bufsize)
ssh_channel_unthrottle(c, bufsize);
}
-static void ssh_queueing_handler(Ssh ssh, struct Packet *pktin)
+static void ssh_queueing_handler(Ssh ssh, PktIn *pktin)
{
struct queued_handler *qh = ssh->qhead;
@@ -5200,7 +5210,7 @@ static void ssh_queue_handler(Ssh ssh, int msg1, int msg2,
ssh->qtail = qh;
}
-static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx)
+static void ssh_rportfwd_succfail(Ssh ssh, PktIn *pktin, void *ctx)
{
struct ssh_rportfwd *rpf, *pf = (struct ssh_rportfwd *)ctx;
@@ -5256,7 +5266,7 @@ void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
sfree(realpf);
}
-static void ssh_sharing_global_request_response(Ssh ssh, struct Packet *pktin,
+static void ssh_sharing_global_request_response(Ssh ssh, PktIn *pktin,
void *ctx)
{
share_got_pkt_from_server(ctx, pktin->type,
@@ -5432,7 +5442,7 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
* forwarding failed. */
if (epf->remote) {
struct ssh_rportfwd *rpf = epf->remote;
- struct Packet *pktout;
+ PktOut *pktout;
/*
* Cancel the port forwarding at the server
@@ -5574,7 +5584,7 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
SSH1_SMSG_FAILURE,
ssh_rportfwd_succfail, pf);
} else {
- struct Packet *pktout;
+ PktOut *pktout;
pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
put_stringz(pktout, "tcpip-forward");
put_bool(pktout, 1);/* want reply */
@@ -5593,7 +5603,7 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
}
}
-static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin)
+static void ssh1_smsg_stdout_stderr_data(Ssh ssh, PktIn *pktin)
{
ptrlen string;
int bufsize;
@@ -5612,7 +5622,7 @@ static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
+static void ssh1_smsg_x11_open(Ssh ssh, PktIn *pktin)
{
/* Remote side is trying to open a channel to talk to our
* X-Server. Give them back a local channel number. */
@@ -5641,7 +5651,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
+static void ssh1_smsg_agent_open(Ssh ssh, PktIn *pktin)
{
/* Remote side is trying to open a channel to talk to our
* agent. Give them back a local channel number. */
@@ -5667,7 +5677,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
+static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
{
/* Remote side is trying to open a channel to talk to a
* forwarded port. Give them back a local channel number. */
@@ -5719,7 +5729,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
sfree(pf.dhost);
}
-static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
+static void ssh1_msg_channel_open_confirmation(Ssh ssh, PktIn *pktin)
{
struct ssh_channel *c;
@@ -5742,7 +5752,7 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh1_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
+static void ssh1_msg_channel_open_failure(Ssh ssh, PktIn *pktin)
{
struct ssh_channel *c;
@@ -5755,7 +5765,7 @@ static void ssh1_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
+static void ssh1_msg_channel_close(Ssh ssh, PktIn *pktin)
{
/* Remote side closes a channel. */
struct ssh_channel *c;
@@ -5831,7 +5841,7 @@ static int ssh_channel_data(struct ssh_channel *c, int is_stderr,
return 0;
}
-static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
+static void ssh1_msg_channel_data(Ssh ssh, PktIn *pktin)
{
/* Data sent down one of our channels. */
ptrlen data;
@@ -5849,7 +5859,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh1_smsg_exit_status(Ssh ssh, struct Packet *pktin)
+static void ssh1_smsg_exit_status(Ssh ssh, PktIn *pktin)
{
ssh->exitcode = get_uint32(pktin);
logeventf(ssh, "Server sent command exit status %d", ssh->exitcode);
@@ -5888,7 +5898,7 @@ int ssh_agent_forwarding_permitted(Ssh ssh)
static void do_ssh1_connection(void *vctx)
{
Ssh ssh = (Ssh)vctx;
- struct Packet *pktin;
+ PktIn *pktin;
crBegin(ssh->do_ssh1_connection_crstate);
@@ -5969,7 +5979,7 @@ static void do_ssh1_connection(void *vctx)
ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;
if (!conf_get_int(ssh->conf, CONF_nopty)) {
- struct Packet *pkt;
+ PktOut *pkt;
/* Unpick the terminal-speed string. */
/* XXX perhaps we should allow no speeds to be sent. */
ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
@@ -6095,19 +6105,19 @@ static void do_ssh1_connection(void *vctx)
/*
* Handle the top-level SSH-2 protocol.
*/
-static void ssh1_msg_debug(Ssh ssh, struct Packet *pktin)
+static void ssh1_msg_debug(Ssh ssh, PktIn *pktin)
{
ptrlen msg = get_string(pktin);
logeventf(ssh, "Remote debug message: %.*s", PTRLEN_PRINTF(msg));
}
-static void ssh1_msg_disconnect(Ssh ssh, struct Packet *pktin)
+static void ssh1_msg_disconnect(Ssh ssh, PktIn *pktin)
{
ptrlen msg = get_string(pktin);
bombout(("Server sent disconnect message:\n\"%.*s\"", PTRLEN_PRINTF(msg)));
}
-static void ssh_msg_ignore(Ssh ssh, struct Packet *pktin)
+static void ssh_msg_ignore(Ssh ssh, PktIn *pktin)
{
/* Do nothing, because we're ignoring it! Duhh. */
}
@@ -6122,14 +6132,14 @@ static void ssh1_connection_input(Ssh ssh)
do_ssh1_connection(ssh);
}
-static void ssh1_coro_wrapper_initial(Ssh ssh, struct Packet *pktin)
+static void ssh1_coro_wrapper_initial(Ssh ssh, PktIn *pktin)
{
pktin->refcount++; /* avoid packet being freed when we return */
pq_push(&ssh->pq_ssh1_login, pktin);
queue_idempotent_callback(&ssh->ssh1_login_icb);
}
-static void ssh1_coro_wrapper_session(Ssh ssh, struct Packet *pktin)
+static void ssh1_coro_wrapper_session(Ssh ssh, PktIn *pktin)
{
pktin->refcount++; /* avoid packet being freed when we return */
pq_push(&ssh->pq_ssh1_connection, pktin);
@@ -6434,7 +6444,7 @@ static int ssh_have_any_transient_hostkey(Ssh ssh)
static void do_ssh2_transport(void *vctx)
{
Ssh ssh = (Ssh)vctx;
- struct Packet *pktin;
+ PktIn *pktin;
enum kexlist {
KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER,
@@ -6482,7 +6492,7 @@ static void do_ssh2_transport(void *vctx)
int userauth_succeeded; /* for delayed compression */
int pending_compression;
int got_session_id;
- struct Packet *pktout;
+ PktOut *pktout;
int dlgret;
int guessok;
int ignorepkt;
@@ -8300,7 +8310,7 @@ static int ssh_send_channel_data(struct ssh_channel *c, const char *buf,
static int ssh2_try_send(struct ssh_channel *c)
{
Ssh ssh = c->ssh;
- struct Packet *pktout;
+ PktOut *pktout;
int ret;
while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) {
@@ -8400,10 +8410,10 @@ static void ssh_channel_init(struct ssh_channel *c)
/*
* Construct the common parts of a CHANNEL_OPEN.
*/
-static struct Packet *ssh2_chanopen_init(struct ssh_channel *c,
+static PktOut *ssh2_chanopen_init(struct ssh_channel *c,
const char *type)
{
- struct Packet *pktout;
+ PktOut *pktout;
pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
put_stringz(pktout, type);
@@ -8448,11 +8458,11 @@ static void ssh2_queue_chanreq_handler(struct ssh_channel *c,
* the server initiated channel closure before we saw the response)
* and the handler should free any storage it's holding.
*/
-static struct Packet *ssh2_chanreq_init(struct ssh_channel *c,
+static PktOut *ssh2_chanreq_init(struct ssh_channel *c,
const char *type,
cchandler_fn_t handler, void *ctx)
{
- struct Packet *pktout;
+ PktOut *pktout;
assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE)));
pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
@@ -8488,7 +8498,7 @@ static void ssh_channel_unthrottle(struct ssh_channel *c, int bufsize)
/*
* Potentially enlarge the window on an SSH-2 channel.
*/
-static void ssh2_handle_winadj_response(struct ssh_channel *, struct Packet *,
+static void ssh2_handle_winadj_response(struct ssh_channel *, PktIn *,
void *);
static void ssh2_set_window(struct ssh_channel *c, int newwin)
{
@@ -8527,7 +8537,7 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin)
* "Significant" is arbitrarily defined as half the window size.
*/
if (newwin / 2 >= c->v.v2.locwindow) {
- struct Packet *pktout;
+ PktOut *pktout;
unsigned *up;
/*
@@ -8569,7 +8579,7 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin)
* If the channel is shared, pass the message on to downstream and
* also return NULL (meaning the caller should ignore this message).
*/
-static struct ssh_channel *ssh_channel_msg(Ssh ssh, struct Packet *pktin)
+static struct ssh_channel *ssh_channel_msg(Ssh ssh, PktIn *pktin)
{
unsigned localid = get_uint32(pktin);
struct ssh_channel *c;
@@ -8602,7 +8612,7 @@ static struct ssh_channel *ssh_channel_msg(Ssh ssh, struct Packet *pktin)
}
static void ssh2_handle_winadj_response(struct ssh_channel *c,
- struct Packet *pktin, void *ctx)
+ PktIn *pktin, void *ctx)
{
unsigned *sizep = ctx;
@@ -8625,7 +8635,7 @@ static void ssh2_handle_winadj_response(struct ssh_channel *c,
c->v.v2.throttle_state = UNTHROTTLED;
}
-static void ssh2_msg_channel_response(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_channel_response(Ssh ssh, PktIn *pktin)
{
struct ssh_channel *c = ssh_channel_msg(ssh, pktin);
struct outstanding_channel_request *ocr;
@@ -8649,7 +8659,7 @@ static void ssh2_msg_channel_response(Ssh ssh, struct Packet *pktin)
ssh2_channel_check_close(c);
}
-static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_channel_window_adjust(Ssh ssh, PktIn *pktin)
{
struct ssh_channel *c;
c = ssh_channel_msg(ssh, pktin);
@@ -8661,7 +8671,7 @@ static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_channel_data(Ssh ssh, PktIn *pktin)
{
ptrlen data;
unsigned ext_type = 0; /* 0 means not extended */
@@ -8823,7 +8833,7 @@ static void ssh_channel_destroy(struct ssh_channel *c)
static void ssh2_channel_check_close(struct ssh_channel *c)
{
Ssh ssh = c->ssh;
- struct Packet *pktout;
+ PktOut *pktout;
assert(ssh->version == 2);
if (c->halfopen) {
@@ -8897,7 +8907,7 @@ static void ssh_channel_got_eof(struct ssh_channel *c)
}
}
-static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_channel_eof(Ssh ssh, PktIn *pktin)
{
struct ssh_channel *c;
@@ -8908,7 +8918,7 @@ static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
ssh2_channel_check_close(c);
}
-static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_channel_close(Ssh ssh, PktIn *pktin)
{
struct ssh_channel *c;
@@ -8985,7 +8995,7 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_channel_open_confirmation(Ssh ssh, PktIn *pktin)
{
struct ssh_channel *c;
@@ -9029,7 +9039,7 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
ssh_channel_try_eof(c); /* in case we had a pending EOF */
}
-static char *ssh2_channel_open_failure_error_text(struct Packet *pktin)
+static char *ssh2_channel_open_failure_error_text(PktIn *pktin)
{
static const char *const reasons[] = {
NULL,
@@ -9056,7 +9066,7 @@ static char *ssh2_channel_open_failure_error_text(struct Packet *pktin)
return dupprintf("%s [%.*s]", reason_code_string, PTRLEN_PRINTF(reason));
}
-static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_channel_open_failure(Ssh ssh, PktIn *pktin)
{
struct ssh_channel *c;
@@ -9095,13 +9105,13 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
sfree(c);
}
-static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_channel_request(Ssh ssh, PktIn *pktin)
{
ptrlen type;
int want_reply;
int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
struct ssh_channel *c;
- struct Packet *pktout;
+ PktOut *pktout;
c = ssh_channel_msg(ssh, pktin);
if (!c)
@@ -9260,10 +9270,10 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
}
}
-static void ssh2_msg_global_request(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_global_request(Ssh ssh, PktIn *pktin)
{
int want_reply;
- struct Packet *pktout;
+ PktOut *pktout;
get_string(pktin); /* ignore request type (see below) */
want_reply = get_bool(pktin);
@@ -9304,7 +9314,7 @@ void ssh_sharing_remove_x11_display(Ssh ssh, struct X11FakeAuth *auth)
x11_free_fake_auth(auth);
}
-static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
{
ptrlen type;
int peerport;
@@ -9312,7 +9322,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
struct ssh_channel *c;
unsigned remid, winsize, pktsize;
unsigned our_winsize_override = 0;
- struct Packet *pktout;
+ PktOut *pktout;
type = get_string(pktin);
c = snew(struct ssh_channel);
@@ -9480,7 +9490,7 @@ void sshfwd_x11_is_local(struct ssh_channel *c)
* Buffer banner messages for later display at some convenient point,
* if we're going to display them.
*/
-static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_userauth_banner(Ssh ssh, PktIn *pktin)
{
/* Arbitrary limit to prevent unbounded inflation of buffer */
if (conf_get_int(ssh->conf, CONF_ssh_show_banner) &&
@@ -9507,14 +9517,14 @@ static void ssh2_send_ttymode(BinarySink *bs,
}
}
-static void ssh2_setup_x11(struct ssh_channel *c, struct Packet *pktin,
+static void ssh2_setup_x11(struct ssh_channel *c, PktIn *pktin,
void *ctx)
{
struct ssh2_setup_x11_state {
int crLine;
};
Ssh ssh = c->ssh;
- struct Packet *pktout;
+ PktOut *pktout;
crStateP(ssh2_setup_x11_state, ctx);
crBeginState;
@@ -9543,14 +9553,14 @@ static void ssh2_setup_x11(struct ssh_channel *c, struct Packet *pktin,
crFinishFreeV;
}
-static void ssh2_setup_agent(struct ssh_channel *c, struct Packet *pktin,
+static void ssh2_setup_agent(struct ssh_channel *c, PktIn *pktin,
void *ctx)
{
struct ssh2_setup_agent_state {
int crLine;
};
Ssh ssh = c->ssh;
- struct Packet *pktout;
+ PktOut *pktout;
crStateP(ssh2_setup_agent_state, ctx);
crBeginState;
@@ -9575,14 +9585,14 @@ static void ssh2_setup_agent(struct ssh_channel *c, struct Packet *pktin,
crFinishFreeV;
}
-static void ssh2_setup_pty(struct ssh_channel *c, struct Packet *pktin,
+static void ssh2_setup_pty(struct ssh_channel *c, PktIn *pktin,
void *ctx)
{
struct ssh2_setup_pty_state {
int crLine;
};
Ssh ssh = c->ssh;
- struct Packet *pktout;
+ PktOut *pktout;
crStateP(ssh2_setup_pty_state, ctx);
crBeginState;
@@ -9630,7 +9640,7 @@ static void ssh2_setup_pty(struct ssh_channel *c, struct Packet *pktin,
crFinishFreeV;
}
-static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin,
+static void ssh2_setup_env(struct ssh_channel *c, PktIn *pktin,
void *ctx)
{
struct ssh2_setup_env_state {
@@ -9638,7 +9648,7 @@ static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin,
int num_env, env_left, env_ok;
};
Ssh ssh = c->ssh;
- struct Packet *pktout;
+ PktOut *pktout;
crStateP(ssh2_setup_env_state, ctx);
crBeginState;
@@ -9699,7 +9709,7 @@ static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin,
/*
* Handle the SSH-2 userauth layer.
*/
-static void ssh2_msg_userauth(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_userauth(Ssh ssh, PktIn *pktin)
{
pktin->refcount++; /* avoid packet being freed when we return */
pq_push(&ssh->pq_ssh2_userauth, pktin);
@@ -9724,7 +9734,7 @@ static void ssh2_msg_userauth(Ssh ssh, struct Packet *pktin)
static void do_ssh2_userauth(void *vctx)
{
Ssh ssh = (Ssh)vctx;
- struct Packet *pktin;
+ PktIn *pktin;
struct do_ssh2_userauth_state {
int crLine;
@@ -9766,7 +9776,7 @@ static void do_ssh2_userauth(void *vctx)
int keyi, nkeys;
ptrlen pk, alg, comment;
int len;
- struct Packet *pktout;
+ PktOut *pktout;
Filename *keyfile;
#ifndef NO_GSSAPI
Ssh_gss_ctx gss_ctx;
@@ -11156,7 +11166,7 @@ static void ssh2_userauth_input(Ssh ssh)
/*
* Handle the SSH-2 connection layer.
*/
-static void ssh2_msg_connection(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_connection(Ssh ssh, PktIn *pktin)
{
pktin->refcount++; /* avoid packet being freed when we return */
pq_push(&ssh->pq_ssh2_connection, pktin);
@@ -11164,7 +11174,7 @@ static void ssh2_msg_connection(Ssh ssh, struct Packet *pktin)
}
static void ssh2_response_connection(struct ssh_channel *c,
- struct Packet *pktin, void *ctx)
+ PktIn *pktin, void *ctx)
{
if (pktin)
ssh2_msg_connection(c->ssh, pktin);
@@ -11207,11 +11217,11 @@ static void ssh2_connection_setup(Ssh ssh)
static void do_ssh2_connection(void *vctx)
{
Ssh ssh = (Ssh)vctx;
- struct Packet *pktin;
+ PktIn *pktin;
struct do_ssh2_connection_state {
int crLine;
- struct Packet *pktout;
+ PktOut *pktout;
};
crState(do_ssh2_connection_state);
@@ -11483,7 +11493,7 @@ static void ssh2_connection_input(Ssh ssh)
/*
* Handlers for SSH-2 messages that might arrive at any moment.
*/
-static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_disconnect(Ssh ssh, PktIn *pktin)
{
/* log reason code in disconnect message */
char *buf;
@@ -11511,7 +11521,7 @@ static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin)
sfree(buf);
}
-static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_debug(Ssh ssh, PktIn *pktin)
{
/* log the debug message */
ptrlen msg;
@@ -11523,7 +11533,7 @@ static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin)
logeventf(ssh, "Remote debug message: %.*s", PTRLEN_PRINTF(msg));
}
-static void ssh2_msg_transport(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_transport(Ssh ssh, PktIn *pktin)
{
pktin->refcount++; /* avoid packet being freed when we return */
pq_push(&ssh->pq_ssh2_transport, pktin);
@@ -11535,7 +11545,7 @@ static void ssh2_msg_transport(Ssh ssh, struct Packet *pktin)
* This only applies to packets whose meaning PuTTY understands.
* Entirely unknown packets are handled below.
*/
-static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin)
{
char *buf = dupprintf("Server protocol violation: unexpected %s packet",
ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,
@@ -11544,9 +11554,9 @@ static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin)
sfree(buf);
}
-static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_something_unimplemented(Ssh ssh, PktIn *pktin)
{
- struct Packet *pktout;
+ PktOut *pktout;
pktout = ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED);
put_uint32(pktout, pktin->sequence);
/*
@@ -11689,29 +11699,30 @@ static void ssh2_bare_connection_protocol_setup(Ssh ssh)
}
#ifndef NO_GSSAPI
-static struct Packet *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
- const char *authtype)
+static PktOut *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
+ const char *authtype)
{
- struct Packet *p = ssh2_pkt_init(0);
- int micoffset = p->length;
+ strbuf *sb;
+ PktOut *p;
Ssh_gss_buf buf;
Ssh_gss_buf mic;
/*
- * The mic is computed over the session id + intended packet, so we
- * build an artificial packet with a prepended session id.
+ * The mic is computed over the session id + intended
+ * USERAUTH_REQUEST packet.
*/
- put_string(p, ssh->v2_session_id, ssh->v2_session_id_len);
- put_byte(p, SSH2_MSG_USERAUTH_REQUEST);
- put_stringz(p, ssh->username);
- put_stringz(p, "ssh-connection");
- put_stringz(p, authtype);
+ sb = strbuf_new();
+ put_string(sb, ssh->v2_session_id, ssh->v2_session_id_len);
+ put_byte(sb, SSH2_MSG_USERAUTH_REQUEST);
+ put_stringz(sb, ssh->username);
+ put_stringz(sb, "ssh-connection");
+ put_stringz(sb, authtype);
/* Compute the mic */
- buf.value = (char *)p->data + micoffset;
- buf.length = p->length - micoffset;
+ buf.value = sb->s;
+ buf.length = sb->len;
ssh->gsslib->get_mic(ssh->gsslib, gss_ctx, &buf, &mic);
- ssh_unref_packet(p);
+ strbuf_free(sb);
/* Now we can build the real packet */
if (strcmp(authtype, "gssapi-with-mic") == 0) {
@@ -11962,7 +11973,7 @@ static void ssh2_timer(void *ctx, unsigned long now)
(void) ssh2_timer_update(ssh, 0);
}
-static void ssh2_general_packet_processing(Ssh ssh, struct Packet *pktin)
+static void ssh2_general_packet_processing(Ssh ssh, PktIn *pktin)
{
ssh->incoming_data_size += pktin->encrypted_len;
if (!ssh->kex_in_progress &&
@@ -12200,7 +12211,7 @@ static void ssh_free(void *handle)
sfree(ssh->savedhost);
while (ssh->queuelen-- > 0)
- ssh_unref_packet(ssh->queue[ssh->queuelen]);
+ ssh_free_pktout(ssh->queue[ssh->queuelen]);
sfree(ssh->queue);
while (ssh->qhead) {
@@ -12413,7 +12424,7 @@ static int ssh_sendbuffer(void *handle)
static void ssh_size(void *handle, int width, int height)
{
Ssh ssh = (Ssh) handle;
- struct Packet *pktout;
+ PktOut *pktout;
ssh->term_width = width;
ssh->term_height = height;
@@ -12560,7 +12571,7 @@ static const struct telnet_special *ssh_get_specials(void *handle)
static void ssh_special(void *handle, Telnet_Special code)
{
Ssh ssh = (Ssh) handle;
- struct Packet *pktout;
+ PktOut *pktout;
if (code == TS_EOF) {
if (ssh->state != SSH_STATE_SESSION) {
@@ -12689,7 +12700,7 @@ void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type,
const void *data, int datalen,
const char *additional_log_text)
{
- struct Packet *pkt;
+ PktOut *pkt;
pkt = ssh2_pkt_init(type);
pkt->downstream_id = id;
@@ -12728,7 +12739,7 @@ void ssh_send_port_open(void *channel, const char *hostname, int port,
{
struct ssh_channel *c = (struct ssh_channel *)channel;
Ssh ssh = c->ssh;
- struct Packet *pktout;
+ PktOut *pktout;
logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org);
From bf3c9df54a9ed55088d7c381f3798095ae48767e Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 6 Jun 2018 07:17:09 +0100
Subject: [PATCH 361/607] Remove body and length fields from PktIn.
They were duplicating values stored in the BinarySource substructure.
Mostly they're not referred to directly any more (instead, we call
get_foo to access the BinarySource); and when they are, we can switch
to reading the same values back out of the BinarySource anyway.
---
ssh.c | 93 ++++++++++++++++++++++++++++-------------------------------
1 file changed, 44 insertions(+), 49 deletions(-)
diff --git a/ssh.c b/ssh.c
index 7de32b50..e8a90e69 100644
--- a/ssh.c
+++ b/ssh.c
@@ -672,11 +672,9 @@ struct ssh_portfwd {
struct PktIn {
int refcount;
- long length; /* length relative to 'body' */
int type;
unsigned long sequence; /* SSH-2 incoming sequence number */
unsigned char *data; /* allocated storage */
- unsigned char *body; /* offset of payload within `data' */
long maxlen; /* amount of storage allocated for `data' */
long encrypted_len; /* for SSH-2 total-size counting */
BinarySource_IMPLEMENTATION;
@@ -809,14 +807,14 @@ static int pq_empty_on_to_front_of(struct PacketQueue *src,
}
struct rdpkt1_state_tag {
- long len, pad, biglen;
+ long len, pad, biglen, length;
unsigned long realcrc, gotcrc;
int chunk;
PktIn *pktin;
};
struct rdpkt2_state_tag {
- long len, pad, payload, packetlen, maclen;
+ long len, pad, payload, packetlen, maclen, length;
int cipherblk;
unsigned long incoming_sequence;
PktIn *pktin;
@@ -1407,7 +1405,8 @@ static void ssh1_log_incoming_packet(Ssh ssh, const PktIn *pkt)
ptrlen str;
BinarySource src[1];
- BinarySource_BARE_INIT(src, pkt->body, pkt->length);
+ BinarySource_BARE_INIT(src, BinarySource_UPCAST(pkt)->data,
+ BinarySource_UPCAST(pkt)->len);
if (ssh->logomitdata &&
(pkt->type == SSH1_SMSG_STDOUT_DATA ||
@@ -1424,10 +1423,8 @@ static void ssh1_log_incoming_packet(Ssh ssh, const PktIn *pkt)
nblanks++;
}
}
- log_packet(ssh->logctx, PKT_INCOMING, pkt->type,
- ssh1_pkt_type(pkt->type),
- pkt->body, pkt->length, nblanks, blanks, NULL,
- 0, NULL);
+ log_packet(ssh->logctx, PKT_INCOMING, pkt->type, ssh1_pkt_type(pkt->type),
+ src->data, src->len, nblanks, blanks, NULL, 0, NULL);
}
static void ssh1_log_outgoing_packet(Ssh ssh, const PktOut *pkt)
@@ -1511,12 +1508,12 @@ static void ssh1_rdpkt(Ssh ssh)
while (1) {
st->pktin = snew(PktIn);
- st->pktin->body = st->pktin->data = NULL;
+ st->pktin->data = NULL;
st->pktin->maxlen = 0;
st->pktin->refcount = 1;
st->pktin->type = 0;
- st->pktin->length = 0;
+ st->length = 0;
{
unsigned char lenbuf[4];
@@ -1527,7 +1524,7 @@ static void ssh1_rdpkt(Ssh ssh)
st->pad = 8 - (st->len % 8);
st->biglen = st->len + st->pad;
- st->pktin->length = st->len - 5;
+ st->length = st->len - 5;
if (st->biglen < 0) {
bombout(("Extremely large packet length from server suggests"
@@ -1561,13 +1558,12 @@ static void ssh1_rdpkt(Ssh ssh)
crStopV;
}
- st->pktin->body = st->pktin->data + st->pad + 1;
-
if (ssh->v1_compressing) {
unsigned char *decompblk;
int decomplen;
if (!zlib_decompress_block(ssh->sc_comp_ctx,
- st->pktin->body - 1, st->pktin->length + 1,
+ st->pktin->data + st->pad,
+ st->length + 1,
&decompblk, &decomplen)) {
bombout(("Zlib decompression encountered invalid data"));
ssh_unref_packet(st->pktin);
@@ -1578,26 +1574,25 @@ static void ssh1_rdpkt(Ssh ssh)
st->pktin->maxlen = st->pad + decomplen;
st->pktin->data = sresize(st->pktin->data, st->pktin->maxlen,
unsigned char);
- st->pktin->body = st->pktin->data + st->pad + 1;
}
- memcpy(st->pktin->body - 1, decompblk, decomplen);
+ memcpy(st->pktin->data + st->pad, decompblk, decomplen);
sfree(decompblk);
- st->pktin->length = decomplen - 1;
+ st->length = decomplen - 1;
}
- st->pktin->type = st->pktin->body[-1];
+ st->pktin->type = st->pktin->data[st->pad];
/*
- * Now pktin->body and pktin->length identify the semantic content
- * of the packet, excluding the initial type byte.
+ * Now we know the bounds of the semantic content of the
+ * packet, excluding the initial type byte.
*/
+ BinarySource_INIT(st->pktin, st->pktin->data + st->pad + 1,
+ st->length);
if (ssh->logctx)
ssh1_log_incoming_packet(ssh, st->pktin);
- BinarySource_INIT(st->pktin, st->pktin->body, st->pktin->length);
-
/*
* Mild layer violation: if the message is a DISCONNECT, we
* should unset the close_expected flag, because now we _do_
@@ -1625,7 +1620,8 @@ static void ssh2_log_incoming_packet(Ssh ssh, const PktIn *pkt)
ptrlen str;
BinarySource src[1];
- BinarySource_BARE_INIT(src, pkt->body, pkt->length);
+ BinarySource_BARE_INIT(src, BinarySource_UPCAST(pkt)->data,
+ BinarySource_UPCAST(pkt)->len);
if (ssh->logomitdata &&
(pkt->type == SSH2_MSG_CHANNEL_DATA ||
@@ -1645,7 +1641,7 @@ static void ssh2_log_incoming_packet(Ssh ssh, const PktIn *pkt)
log_packet(ssh->logctx, PKT_INCOMING, pkt->type,
ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->type),
- pkt->body, pkt->length, nblanks, blanks, &pkt->sequence,
+ src->data, src->len, nblanks, blanks, &pkt->sequence,
0, NULL);
}
@@ -1759,12 +1755,12 @@ static void ssh2_rdpkt(Ssh ssh)
while (1) {
st->pktin = snew(PktIn);
- st->pktin->body = st->pktin->data = NULL;
+ st->pktin->data = NULL;
st->pktin->maxlen = 0;
st->pktin->refcount = 1;
st->pktin->type = 0;
- st->pktin->length = 0;
+ st->length = 0;
if (ssh->sccipher)
st->cipherblk = ssh->sccipher->blksize;
else
@@ -1984,13 +1980,13 @@ static void ssh2_rdpkt(Ssh ssh)
*/
st->payload = st->len - st->pad - 1;
- st->pktin->length = st->payload + 5;
+ st->length = st->payload + 5;
st->pktin->encrypted_len = st->packetlen;
st->pktin->sequence = st->incoming_sequence++;
- st->pktin->length = st->packetlen - st->pad;
- assert(st->pktin->length >= 0);
+ st->length = st->packetlen - st->pad;
+ assert(st->length >= 0);
/*
* Decompress packet payload.
@@ -2000,7 +1996,7 @@ static void ssh2_rdpkt(Ssh ssh)
int newlen;
if (ssh->sccomp &&
ssh->sccomp->decompress(ssh->sc_comp_ctx,
- st->pktin->data + 5, st->pktin->length - 5,
+ st->pktin->data + 5, st->length - 5,
&newpayload, &newlen)) {
if (st->pktin->maxlen < newlen + 5) {
st->pktin->maxlen = newlen + 5;
@@ -2008,7 +2004,7 @@ static void ssh2_rdpkt(Ssh ssh)
st->pktin->maxlen,
unsigned char);
}
- st->pktin->length = 5 + newlen;
+ st->length = 5 + newlen;
memcpy(st->pktin->data + 5, newpayload, newlen);
sfree(newpayload);
}
@@ -2019,24 +2015,22 @@ static void ssh2_rdpkt(Ssh ssh)
* with no type byte are forbidden, so treat them as deserving
* an SSH_MSG_UNIMPLEMENTED.
*/
- if (st->pktin->length <= 5) { /* == 5 we hope, but robustness */
+ if (st->length <= 5) { /* == 5 we hope, but robustness */
ssh2_msg_something_unimplemented(ssh, st->pktin);
crStopV;
}
/*
- * pktin->body and pktin->length should identify the semantic
- * content of the packet, excluding the initial type byte.
+ * Now we can identify the semantic content of the packet,
+ * and also the initial type byte.
*/
st->pktin->type = st->pktin->data[5];
- st->pktin->body = st->pktin->data + 6;
- st->pktin->length -= 6;
- assert(st->pktin->length >= 0); /* one last double-check */
+ st->length -= 6;
+ assert(st->length >= 0); /* one last double-check */
+ BinarySource_INIT(st->pktin, st->pktin->data + 6, st->length);
if (ssh->logctx)
ssh2_log_incoming_packet(ssh, st->pktin);
- BinarySource_INIT(st->pktin, st->pktin->body, st->pktin->length);
-
/*
* Mild layer violation: if the message is a DISCONNECT, we
* should unset the close_expected flag, because now we _do_
@@ -2086,7 +2080,6 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
}
st->pktin = snew(PktIn);
- st->pktin->body = NULL;
st->pktin->maxlen = 0;
st->pktin->refcount = 1;
st->pktin->data = snewn(st->packetlen, unsigned char);
@@ -2107,8 +2100,7 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
* content of the packet, excluding the initial type byte.
*/
st->pktin->type = st->pktin->data[0];
- st->pktin->body = st->pktin->data + 1;
- st->pktin->length = st->packetlen - 1;
+ BinarySource_INIT(st->pktin, st->pktin->data + 1, st->packetlen - 1);
/*
* Log incoming packet, possibly omitting sensitive fields.
@@ -2116,8 +2108,6 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
if (ssh->logctx)
ssh2_log_incoming_packet(ssh, st->pktin);
- BinarySource_INIT(st->pktin, st->pktin->body, st->pktin->length);
-
/*
* Mild layer violation: if the message is a DISCONNECT, we
* should unset the close_expected flag, because now we _do_
@@ -5270,7 +5260,8 @@ static void ssh_sharing_global_request_response(Ssh ssh, PktIn *pktin,
void *ctx)
{
share_got_pkt_from_server(ctx, pktin->type,
- pktin->body, pktin->length);
+ BinarySource_UPCAST(pktin)->data,
+ BinarySource_UPCAST(pktin)->len);
}
void ssh_sharing_queue_global_request(Ssh ssh, void *share_ctx)
@@ -7051,7 +7042,9 @@ static void do_ssh2_transport(void *vctx)
put_string(ssh->exhash_bs, s->our_kexinit, s->our_kexinitlen);
sfree(s->our_kexinit);
/* Include the type byte in the hash of server's KEXINIT */
- put_string(ssh->exhash_bs, pktin->body - 1, pktin->length + 1);
+ put_string(ssh->exhash_bs,
+ (const char *)BinarySource_UPCAST(pktin)->data - 1,
+ BinarySource_UPCAST(pktin)->len + 1);
if (s->warn_kex) {
ssh_set_frozen(ssh, 1);
@@ -8605,7 +8598,8 @@ static struct ssh_channel *ssh_channel_msg(Ssh ssh, PktIn *pktin)
}
if (c->type == CHAN_SHARING) {
share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- pktin->body, pktin->length);
+ BinarySource_UPCAST(pktin)->data,
+ BinarySource_UPCAST(pktin)->len);
return NULL;
}
return c;
@@ -9389,7 +9383,8 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
* to sshshare.c.
*/
share_got_pkt_from_server(realpf->share_ctx, pktin->type,
- pktin->body, pktin->length);
+ BinarySource_UPCAST(pktin)->data,
+ BinarySource_UPCAST(pktin)->len);
sfree(c);
return;
}
From ea04bf3da98173b22325dee30acca9146cc24851 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 5 Jun 2018 18:13:08 +0100
Subject: [PATCH 362/607] Remove data and maxlen fields from PktIn.
These were only used in the rdpkt coroutines, during construction of
the incoming packet; once it's complete, they're never touched again.
So really they should have been fields in the rdpkt coroutines' state
- and now they are.
The new memory allocation strategy for incoming packets is to defer
creation of the returned pktin structure until we know how big its
data buffer will really need to be, and then use snew_plus to make the
PktIn and the payload block in the same allocation.
When we have to read and keep some amount of the packet before
allocating the returned structure, we do it by having a persistent
buffer in the rdpkt state, which is retained for the whole connection
and only freed once in ssh_free.
---
ssh.c | 233 +++++++++++++++++++++++++++++++++-------------------------
1 file changed, 134 insertions(+), 99 deletions(-)
diff --git a/ssh.c b/ssh.c
index e8a90e69..9a71cd5d 100644
--- a/ssh.c
+++ b/ssh.c
@@ -674,8 +674,6 @@ struct PktIn {
int refcount;
int type;
unsigned long sequence; /* SSH-2 incoming sequence number */
- unsigned char *data; /* allocated storage */
- long maxlen; /* amount of storage allocated for `data' */
long encrypted_len; /* for SSH-2 total-size counting */
BinarySource_IMPLEMENTATION;
};
@@ -807,21 +805,26 @@ static int pq_empty_on_to_front_of(struct PacketQueue *src,
}
struct rdpkt1_state_tag {
- long len, pad, biglen, length;
+ long len, pad, biglen, length, maxlen;
+ unsigned char *data;
unsigned long realcrc, gotcrc;
int chunk;
PktIn *pktin;
};
struct rdpkt2_state_tag {
- long len, pad, payload, packetlen, maclen, length;
+ long len, pad, payload, packetlen, maclen, length, maxlen;
+ unsigned char *buf;
+ size_t bufsize;
+ unsigned char *data;
int cipherblk;
unsigned long incoming_sequence;
PktIn *pktin;
};
struct rdpkt2_bare_state_tag {
- long packetlen;
+ long packetlen, maxlen;
+ unsigned char *data;
unsigned long incoming_sequence;
PktIn *pktin;
};
@@ -1373,10 +1376,8 @@ static void c_write_str(Ssh ssh, const char *buf)
static void ssh_unref_packet(PktIn *pkt)
{
- if (--pkt->refcount <= 0) {
- sfree(pkt->data);
+ if (--pkt->refcount <= 0)
sfree(pkt);
- }
}
static void ssh_free_pktout(PktOut *pkt)
@@ -1507,12 +1508,7 @@ static void ssh1_rdpkt(Ssh ssh)
crBegin(ssh->ssh1_rdpkt_crstate);
while (1) {
- st->pktin = snew(PktIn);
- st->pktin->data = NULL;
- st->pktin->maxlen = 0;
- st->pktin->refcount = 1;
-
- st->pktin->type = 0;
+ st->maxlen = 0;
st->length = 0;
{
@@ -1533,14 +1529,20 @@ static void ssh1_rdpkt(Ssh ssh)
crStopV;
}
- st->pktin->maxlen = st->biglen;
- st->pktin->data = snewn(st->biglen, unsigned char);
+ /*
+ * Allocate the packet to return, now we know its length.
+ */
+ st->pktin = snew_plus(PktIn, st->biglen);
+ st->pktin->refcount = 1;
+ st->pktin->type = 0;
+
+ st->maxlen = st->biglen;
+ st->data = snew_plus_get_aux(st->pktin);
crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data,
- st->pktin->data, st->biglen));
+ &ssh->incoming_data, st->data, st->biglen));
- if (ssh->cipher && detect_attack(ssh->crcda_ctx, st->pktin->data,
+ if (ssh->cipher && detect_attack(ssh->crcda_ctx, st->data,
st->biglen, NULL)) {
bombout(("Network attack (CRC compensation) detected!"));
ssh_unref_packet(st->pktin);
@@ -1548,10 +1550,10 @@ static void ssh1_rdpkt(Ssh ssh)
}
if (ssh->cipher)
- ssh->cipher->decrypt(ssh->v1_cipher_ctx, st->pktin->data, st->biglen);
+ ssh->cipher->decrypt(ssh->v1_cipher_ctx, st->data, st->biglen);
- st->realcrc = crc32_compute(st->pktin->data, st->biglen - 4);
- st->gotcrc = GET_32BIT(st->pktin->data + st->biglen - 4);
+ st->realcrc = crc32_compute(st->data, st->biglen - 4);
+ st->gotcrc = GET_32BIT(st->data + st->biglen - 4);
if (st->gotcrc != st->realcrc) {
bombout(("Incorrect CRC received on packet"));
ssh_unref_packet(st->pktin);
@@ -1562,33 +1564,36 @@ static void ssh1_rdpkt(Ssh ssh)
unsigned char *decompblk;
int decomplen;
if (!zlib_decompress_block(ssh->sc_comp_ctx,
- st->pktin->data + st->pad,
- st->length + 1,
+ st->data + st->pad, st->length + 1,
&decompblk, &decomplen)) {
bombout(("Zlib decompression encountered invalid data"));
ssh_unref_packet(st->pktin);
crStopV;
}
- if (st->pktin->maxlen < st->pad + decomplen) {
- st->pktin->maxlen = st->pad + decomplen;
- st->pktin->data = sresize(st->pktin->data, st->pktin->maxlen,
- unsigned char);
+ if (st->maxlen < st->pad + decomplen) {
+ PktIn *old_pktin = st->pktin;
+
+ st->maxlen = st->pad + decomplen;
+ st->pktin = snew_plus(PktIn, st->maxlen);
+ *st->pktin = *old_pktin; /* structure copy */
+ st->data = snew_plus_get_aux(st->pktin);
+
+ smemclr(old_pktin, st->biglen);
+ sfree(old_pktin);
}
- memcpy(st->pktin->data + st->pad, decompblk, decomplen);
+ memcpy(st->data + st->pad, decompblk, decomplen);
sfree(decompblk);
st->length = decomplen - 1;
}
- st->pktin->type = st->pktin->data[st->pad];
-
/*
- * Now we know the bounds of the semantic content of the
- * packet, excluding the initial type byte.
+ * Now we can find the bounds of the semantic content of the
+ * packet, and the initial type byte.
*/
- BinarySource_INIT(st->pktin, st->pktin->data + st->pad + 1,
- st->length);
+ st->pktin->type = st->data[st->pad];
+ BinarySource_INIT(st->pktin, st->data + st->pad + 1, st->length);
if (ssh->logctx)
ssh1_log_incoming_packet(ssh, st->pktin);
@@ -1753,13 +1758,11 @@ static void ssh2_rdpkt(Ssh ssh)
crBegin(ssh->ssh2_rdpkt_crstate);
- while (1) {
- st->pktin = snew(PktIn);
- st->pktin->data = NULL;
- st->pktin->maxlen = 0;
- st->pktin->refcount = 1;
+ st->buf = NULL;
+ st->bufsize = 0;
- st->pktin->type = 0;
+ while (1) {
+ st->maxlen = 0;
st->length = 0;
if (ssh->sccipher)
st->cipherblk = ssh->sccipher->blksize;
@@ -1789,14 +1792,18 @@ static void ssh2_rdpkt(Ssh ssh)
* detecting it before we decrypt anything.
*/
- /* May as well allocate the whole lot now. */
- st->pktin->data = snewn(OUR_V2_PACKETLIMIT + st->maclen,
- unsigned char);
+ /*
+ * Make sure we have buffer space for a maximum-size packet.
+ */
+ int buflimit = OUR_V2_PACKETLIMIT + st->maclen;
+ if (st->bufsize < buflimit) {
+ st->bufsize = buflimit;
+ st->buf = sresize(st->buf, st->bufsize, unsigned char);
+ }
/* Read an amount corresponding to the MAC. */
crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data,
- st->pktin->data, st->maclen));
+ &ssh->incoming_data, st->buf, st->maclen));
st->packetlen = 0;
ssh->scmac->start(ssh->sc_mac_ctx);
@@ -1807,51 +1814,59 @@ static void ssh2_rdpkt(Ssh ssh)
/* Read another cipher-block's worth, and tack it onto the end. */
crMaybeWaitUntilV(bufchain_try_fetch_consume(
&ssh->incoming_data,
- st->pktin->data + (st->packetlen +
- st->maclen),
+ st->buf + (st->packetlen + st->maclen),
st->cipherblk));
/* Decrypt one more block (a little further back in the stream). */
ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->pktin->data + st->packetlen,
+ st->buf + st->packetlen,
st->cipherblk);
/* Feed that block to the MAC. */
put_data(ssh->sc_mac_bs,
- st->pktin->data + st->packetlen, st->cipherblk);
+ st->buf + st->packetlen, st->cipherblk);
st->packetlen += st->cipherblk;
/* See if that gives us a valid packet. */
if (ssh->scmac->verresult(ssh->sc_mac_ctx,
- st->pktin->data + st->packetlen) &&
- ((st->len = toint(GET_32BIT(st->pktin->data))) ==
+ st->buf + st->packetlen) &&
+ ((st->len = toint(GET_32BIT(st->buf))) ==
st->packetlen-4))
break;
if (st->packetlen >= OUR_V2_PACKETLIMIT) {
bombout(("No valid incoming packet found"));
- ssh_unref_packet(st->pktin);
crStopV;
}
}
- st->pktin->maxlen = st->packetlen + st->maclen;
- st->pktin->data = sresize(st->pktin->data, st->pktin->maxlen,
- unsigned char);
+ st->maxlen = st->packetlen + st->maclen;
+
+ /*
+ * Now transfer the data into an output packet.
+ */
+ st->pktin = snew_plus(PktIn, st->maxlen);
+ st->pktin->refcount = 1;
+ st->pktin->type = 0;
+ st->data = snew_plus_get_aux(st->pktin);
+ memcpy(st->data, st->buf, st->maxlen);
} else if (ssh->scmac && ssh->scmac_etm) {
- st->pktin->data = snewn(4, unsigned char);
+ if (st->bufsize < 4) {
+ st->bufsize = 4;
+ st->buf = sresize(st->buf, st->bufsize, unsigned char);
+ }
/*
* OpenSSH encrypt-then-MAC mode: the packet length is
* unencrypted, unless the cipher supports length encryption.
*/
crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data, st->pktin->data, 4));
+ &ssh->incoming_data, st->buf, 4));
/* Cipher supports length decryption, so do it */
if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
/* Keep the packet the same though, so the MAC passes */
unsigned char len[4];
- memcpy(len, st->pktin->data, 4);
+ memcpy(len, st->buf, 4);
ssh->sccipher->decrypt_length(ssh->sc_cipher_ctx, len, 4, st->incoming_sequence);
st->len = toint(GET_32BIT(len));
} else {
- st->len = toint(GET_32BIT(st->pktin->data));
+ st->len = toint(GET_32BIT(st->buf));
}
/*
@@ -1861,7 +1876,6 @@ static void ssh2_rdpkt(Ssh ssh)
if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT ||
st->len % st->cipherblk != 0) {
bombout(("Incoming packet length field was garbled"));
- ssh_unref_packet(st->pktin);
crStopV;
}
@@ -1871,24 +1885,26 @@ static void ssh2_rdpkt(Ssh ssh)
st->packetlen = st->len + 4;
/*
- * Allocate memory for the rest of the packet.
+ * Allocate the packet to return, now we know its length.
*/
- st->pktin->maxlen = st->packetlen + st->maclen;
- st->pktin->data = sresize(st->pktin->data, st->pktin->maxlen,
- unsigned char);
+ st->pktin = snew_plus(PktIn, OUR_V2_PACKETLIMIT + st->maclen);
+ st->pktin->refcount = 1;
+ st->pktin->type = 0;
+ st->data = snew_plus_get_aux(st->pktin);
+ memcpy(st->data, st->buf, 4);
/*
* Read the remainder of the packet.
*/
crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data, st->pktin->data + 4,
+ &ssh->incoming_data, st->data + 4,
st->packetlen + st->maclen - 4));
/*
* Check the MAC.
*/
if (ssh->scmac
- && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data,
+ && !ssh->scmac->verify(ssh->sc_mac_ctx, st->data,
st->len + 4, st->incoming_sequence)) {
bombout(("Incorrect MAC received on packet"));
ssh_unref_packet(st->pktin);
@@ -1898,10 +1914,12 @@ static void ssh2_rdpkt(Ssh ssh)
/* Decrypt everything between the length field and the MAC. */
if (ssh->sccipher)
ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->pktin->data + 4,
- st->packetlen - 4);
+ st->data + 4, st->packetlen - 4);
} else {
- st->pktin->data = snewn(st->cipherblk, unsigned char);
+ if (st->bufsize < st->cipherblk) {
+ st->bufsize = st->cipherblk;
+ st->buf = sresize(st->buf, st->bufsize, unsigned char);
+ }
/*
* Acquire and decrypt the first block of the packet. This will
@@ -1909,16 +1927,16 @@ static void ssh2_rdpkt(Ssh ssh)
*/
crMaybeWaitUntilV(bufchain_try_fetch_consume(
&ssh->incoming_data,
- st->pktin->data, st->cipherblk));
+ st->buf, st->cipherblk));
if (ssh->sccipher)
ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->pktin->data, st->cipherblk);
+ st->buf, st->cipherblk);
/*
* Now get the length figure.
*/
- st->len = toint(GET_32BIT(st->pktin->data));
+ st->len = toint(GET_32BIT(st->buf));
/*
* _Completely_ silly lengths should be stomped on before they
@@ -1937,31 +1955,34 @@ static void ssh2_rdpkt(Ssh ssh)
st->packetlen = st->len + 4;
/*
- * Allocate memory for the rest of the packet.
+ * Allocate the packet to return, now we know its length.
*/
- st->pktin->maxlen = st->packetlen + st->maclen;
- st->pktin->data = sresize(st->pktin->data, st->pktin->maxlen,
- unsigned char);
+ st->maxlen = st->packetlen + st->maclen;
+ st->pktin = snew_plus(PktIn, st->maxlen);
+ st->pktin->refcount = 1;
+ st->pktin->type = 0;
+ st->data = snew_plus_get_aux(st->pktin);
+ memcpy(st->data, st->buf, st->cipherblk);
/*
* Read and decrypt the remainder of the packet.
*/
crMaybeWaitUntilV(bufchain_try_fetch_consume(
&ssh->incoming_data,
- st->pktin->data + st->cipherblk,
+ st->data + st->cipherblk,
st->packetlen + st->maclen - st->cipherblk));
/* Decrypt everything _except_ the MAC. */
if (ssh->sccipher)
ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->pktin->data + st->cipherblk,
+ st->data + st->cipherblk,
st->packetlen - st->cipherblk);
/*
* Check the MAC.
*/
if (ssh->scmac
- && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data,
+ && !ssh->scmac->verify(ssh->sc_mac_ctx, st->data,
st->len + 4, st->incoming_sequence)) {
bombout(("Incorrect MAC received on packet"));
ssh_unref_packet(st->pktin);
@@ -1969,7 +1990,7 @@ static void ssh2_rdpkt(Ssh ssh)
}
}
/* Get and sanity-check the amount of random padding. */
- st->pad = st->pktin->data[4];
+ st->pad = st->data[4];
if (st->pad < 4 || st->len - st->pad < 1) {
bombout(("Invalid padding length on received packet"));
ssh_unref_packet(st->pktin);
@@ -1996,16 +2017,21 @@ static void ssh2_rdpkt(Ssh ssh)
int newlen;
if (ssh->sccomp &&
ssh->sccomp->decompress(ssh->sc_comp_ctx,
- st->pktin->data + 5, st->length - 5,
+ st->data + 5, st->length - 5,
&newpayload, &newlen)) {
- if (st->pktin->maxlen < newlen + 5) {
- st->pktin->maxlen = newlen + 5;
- st->pktin->data = sresize(st->pktin->data,
- st->pktin->maxlen,
- unsigned char);
+ if (st->maxlen < newlen + 5) {
+ PktIn *old_pktin = st->pktin;
+
+ st->maxlen = newlen + 5;
+ st->pktin = snew_plus(PktIn, st->maxlen);
+ *st->pktin = *old_pktin; /* structure copy */
+ st->data = snew_plus_get_aux(st->pktin);
+
+ smemclr(old_pktin, st->packetlen + st->maclen);
+ sfree(old_pktin);
}
st->length = 5 + newlen;
- memcpy(st->pktin->data + 5, newpayload, newlen);
+ memcpy(st->data + 5, newpayload, newlen);
sfree(newpayload);
}
}
@@ -2019,14 +2045,15 @@ static void ssh2_rdpkt(Ssh ssh)
ssh2_msg_something_unimplemented(ssh, st->pktin);
crStopV;
}
+
/*
* Now we can identify the semantic content of the packet,
* and also the initial type byte.
*/
- st->pktin->type = st->pktin->data[5];
+ st->pktin->type = st->data[5];
st->length -= 6;
assert(st->length >= 0); /* one last double-check */
- BinarySource_INIT(st->pktin, st->pktin->data + 6, st->length);
+ BinarySource_INIT(st->pktin, st->data + 6, st->length);
if (ssh->logctx)
ssh2_log_incoming_packet(ssh, st->pktin);
@@ -2079,10 +2106,13 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
crStopV;
}
- st->pktin = snew(PktIn);
- st->pktin->maxlen = 0;
+ /*
+ * Allocate the packet to return, now we know its length.
+ */
+ st->pktin = snew_plus(PktIn, st->packetlen);
+ st->maxlen = 0;
st->pktin->refcount = 1;
- st->pktin->data = snewn(st->packetlen, unsigned char);
+ st->data = snew_plus_get_aux(st->pktin);
st->pktin->encrypted_len = st->packetlen;
@@ -2092,15 +2122,14 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
* Read the remainder of the packet.
*/
crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data,
- st->pktin->data, st->packetlen));
+ &ssh->incoming_data, st->data, st->packetlen));
/*
- * pktin->body and pktin->length should identify the semantic
- * content of the packet, excluding the initial type byte.
+ * The data we just read is precisely the initial type byte
+ * followed by the packet payload.
*/
- st->pktin->type = st->pktin->data[0];
- BinarySource_INIT(st->pktin, st->pktin->data + 1, st->packetlen - 1);
+ st->pktin->type = st->data[0];
+ BinarySource_INIT(st->pktin, st->data + 1, st->packetlen - 1);
/*
* Log incoming packet, possibly omitting sensitive fields.
@@ -12045,6 +12074,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->ssh1_rdpkt_crstate = 0;
ssh->ssh2_rdpkt_crstate = 0;
ssh->ssh2_bare_rdpkt_crstate = 0;
+ ssh->rdpkt2_state.buf = NULL;
ssh->do_ssh1_connection_crstate = 0;
ssh->do_ssh_init_state = NULL;
ssh->do_ssh_connection_init_state = NULL;
@@ -12205,6 +12235,11 @@ static void ssh_free(void *handle)
dh_cleanup(ssh->kex_ctx);
sfree(ssh->savedhost);
+ if (ssh->rdpkt2_state.buf) {
+ smemclr(ssh->rdpkt2_state.buf, ssh->rdpkt2_state.bufsize);
+ sfree(ssh->rdpkt2_state.buf);
+ }
+
while (ssh->queuelen-- > 0)
ssh_free_pktout(ssh->queue[ssh->queuelen]);
sfree(ssh->queue);
From 8c4680a97264258e1412213a65a7e3db0eda2040 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 6 Jun 2018 07:17:32 +0100
Subject: [PATCH 363/607] Replace PktOut body pointer with a prefix length.
The body pointer was used after encryption to mark the start of the
fully wire-ready packet by ssh2_pkt_construct, and before encryption
by the log_outgoing_packet functions. Now the former returns a nice
modern ptrlen (it never really needed to store the pointer _in_ the
packet structure anyway), and the latter uses an integer 'prefix'
field, which isn't very different in concept but saves effort on
reallocs.
---
ssh.c | 75 ++++++++++++++++++++++++-----------------------------------
1 file changed, 30 insertions(+), 45 deletions(-)
diff --git a/ssh.c b/ssh.c
index 9a71cd5d..d7d77e17 100644
--- a/ssh.c
+++ b/ssh.c
@@ -366,7 +366,7 @@ static struct PktOut *ssh1_pkt_init(int pkt_type);
static struct PktOut *ssh2_pkt_init(int pkt_type);
static void ssh_pkt_ensure(struct PktOut *, int length);
static void ssh_pkt_adddata(struct PktOut *, const void *data, int len);
-static int ssh2_pkt_construct(Ssh, struct PktOut *);
+static ptrlen ssh2_pkt_construct(Ssh, struct PktOut *);
static void ssh2_pkt_send(Ssh, struct PktOut *);
static void ssh2_pkt_send_noqueue(Ssh, struct PktOut *);
static void do_ssh1_login(void *vctx);
@@ -679,11 +679,11 @@ struct PktIn {
};
struct PktOut {
- long length; /* length relative to 'data' */
+ long prefix; /* bytes up to and including type field */
+ long length; /* total bytes, including prefix */
int type;
long forcepad; /* SSH-2: force padding to at least this length */
unsigned char *data; /* allocated storage */
- unsigned char *body; /* offset of payload within `data' */
long maxlen; /* amount of storage allocated for `data' */
long encrypted_len; /* for SSH-2 total-size counting */
@@ -1393,7 +1393,7 @@ static PktOut *ssh_new_packet(void)
PktOut *pkt = snew(PktOut);
BinarySink_INIT(pkt, ssh_pkt_BinarySink_write);
- pkt->body = pkt->data = NULL;
+ pkt->data = NULL;
pkt->maxlen = 0;
return pkt;
@@ -1435,14 +1435,8 @@ static void ssh1_log_outgoing_packet(Ssh ssh, const PktOut *pkt)
ptrlen str;
BinarySource src[1];
- /*
- * For outgoing packets, pkt->length represents the length of the
- * whole packet starting at pkt->data (including some header), and
- * pkt->body refers to the point within that where the log-worthy
- * payload begins.
- */
- BinarySource_BARE_INIT(src, pkt->body,
- pkt->length - (pkt->body - pkt->data));
+ BinarySource_BARE_INIT(src, pkt->data + pkt->prefix,
+ pkt->length - pkt->prefix);
if (ssh->logomitdata &&
(pkt->type == SSH1_CMSG_STDIN_DATA ||
@@ -1657,14 +1651,8 @@ static void ssh2_log_outgoing_packet(Ssh ssh, const PktOut *pkt)
ptrlen str;
BinarySource src[1];
- /*
- * For outgoing packets, pkt->length represents the length of the
- * whole packet starting at pkt->data (including some header), and
- * pkt->body refers to the point within that where the log-worthy
- * payload begins.
- */
- BinarySource_BARE_INIT(src, pkt->body,
- pkt->length - (pkt->body - pkt->data));
+ BinarySource_BARE_INIT(src, pkt->data + pkt->prefix,
+ pkt->length - pkt->prefix);
if (ssh->logomitdata &&
(pkt->type == SSH2_MSG_CHANNEL_DATA ||
@@ -2208,7 +2196,7 @@ static int s_wrpkt_prepare(Ssh ssh, PktOut *pkt, int *offset_p)
return biglen + 4; /* len(length+padding+type+data+CRC) */
}
-static int s_write(Ssh ssh, void *data, int len)
+static int s_write(Ssh ssh, const void *data, int len)
{
if (len && ssh->logctx)
log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len,
@@ -2337,11 +2325,8 @@ static int ssh_versioncmp(const char *a, const char *b)
static void ssh_pkt_ensure(PktOut *pkt, int length)
{
if (pkt->maxlen < length) {
- unsigned char *body = pkt->body;
- int offset = body ? body - pkt->data : 0;
pkt->maxlen = length + 256;
pkt->data = sresize(pkt->data, pkt->maxlen, unsigned char);
- if (body) pkt->body = pkt->data + offset;
}
}
static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len)
@@ -2363,7 +2348,7 @@ static PktOut *ssh1_pkt_init(int pkt_type)
PktOut *pkt = ssh_new_packet();
pkt->length = 4 + 8; /* space for length + max padding */
put_byte(pkt, pkt_type);
- pkt->body = pkt->data + pkt->length;
+ pkt->prefix = pkt->length;
pkt->type = pkt_type;
pkt->downstream_id = 0;
pkt->additional_log_text = NULL;
@@ -2377,18 +2362,18 @@ static PktOut *ssh2_pkt_init(int pkt_type)
pkt->forcepad = 0;
pkt->type = pkt_type;
put_byte(pkt, (unsigned char) pkt_type);
- pkt->body = pkt->data + pkt->length; /* after packet type */
+ pkt->prefix = pkt->length;
pkt->downstream_id = 0;
pkt->additional_log_text = NULL;
return pkt;
}
/*
- * Construct an SSH-2 final-form packet: compress it, encrypt it,
- * put the MAC on it. Final packet, ready to be sent, is stored in
- * pkt->data. Total length is returned.
+ * Construct an SSH-2 final-form packet: compress it, encrypt it, put
+ * the MAC on it. Return a slice of pkt->data giving the final packet
+ * in ready-to-send form.
*/
-static int ssh2_pkt_construct(Ssh ssh, PktOut *pkt)
+static ptrlen ssh2_pkt_construct(Ssh ssh, PktOut *pkt)
{
int cipherblk, maclen, padding, unencrypted_prefix, i;
@@ -2400,10 +2385,12 @@ static int ssh2_pkt_construct(Ssh ssh, PktOut *pkt)
* Trivial packet construction for the bare connection
* protocol.
*/
- PUT_32BIT(pkt->data + 1, pkt->length - 5);
- pkt->body = pkt->data + 1;
+ long start = pkt->prefix - 1;
+ long length = pkt->length - start;
+ assert(start >= 4);
+ PUT_32BIT(pkt->data + start - 4, length);
ssh->v2_outgoing_sequence++; /* only for diagnostics, really */
- return pkt->length - 1;
+ return make_ptrlen(pkt->data + start - 4, length + 4);
}
/*
@@ -2477,9 +2464,7 @@ static int ssh2_pkt_construct(Ssh ssh, PktOut *pkt)
ssh->v2_outgoing_sequence++; /* whether or not we MACed */
pkt->encrypted_len = pkt->length + padding;
- /* Ready-to-send packet starts at pkt->data. We return length. */
- pkt->body = pkt->data;
- return pkt->length + padding + maclen;
+ return make_ptrlen(pkt->data, pkt->length + padding + maclen);
}
/*
@@ -2527,7 +2512,7 @@ static void ssh_pkt_defersend(Ssh);
*/
static void ssh2_pkt_send_noqueue(Ssh ssh, PktOut *pkt)
{
- int len;
+ ptrlen data;
int backlog;
if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC)) {
/* We need to send two packets, so use the deferral mechanism. */
@@ -2535,8 +2520,8 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, PktOut *pkt)
ssh_pkt_defersend(ssh);
return;
}
- len = ssh2_pkt_construct(ssh, pkt);
- backlog = s_write(ssh, pkt->body, len);
+ data = ssh2_pkt_construct(ssh, pkt);
+ backlog = s_write(ssh, data.ptr, data.len);
if (backlog > SSH_MAX_BACKLOG)
ssh_throttle_all(ssh, 1, backlog);
@@ -2558,7 +2543,7 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, PktOut *pkt)
*/
static void ssh2_pkt_defer_noqueue(Ssh ssh, PktOut *pkt, int noignore)
{
- int len;
+ ptrlen data;
if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
ssh->deferred_len == 0 && !noignore &&
!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
@@ -2570,15 +2555,15 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, PktOut *pkt, int noignore)
put_stringz(ipkt, "");
ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);
}
- len = ssh2_pkt_construct(ssh, pkt);
- if (ssh->deferred_len + len > ssh->deferred_size) {
- ssh->deferred_size = ssh->deferred_len + len + 128;
+ data = ssh2_pkt_construct(ssh, pkt);
+ if (ssh->deferred_len + data.len > ssh->deferred_size) {
+ ssh->deferred_size = ssh->deferred_len + data.len + 128;
ssh->deferred_send_data = sresize(ssh->deferred_send_data,
ssh->deferred_size,
unsigned char);
}
- memcpy(ssh->deferred_send_data + ssh->deferred_len, pkt->body, len);
- ssh->deferred_len += len;
+ memcpy(ssh->deferred_send_data + ssh->deferred_len, data.ptr, data.len);
+ ssh->deferred_len += data.len;
ssh->deferred_data_size += pkt->encrypted_len;
ssh_free_pktout(pkt);
}
From eb5bc3191151638bb800d582701a524dd095e411 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 5 Jun 2018 23:02:31 +0100
Subject: [PATCH 364/607] Make PktIn contain its own PacketQueueNode.
This saves a malloc and free every time we add or remove a packet from
a packet queue - it can now be done by pure pointer-shuffling instead
of allocating a separate list node structure.
---
ssh.c | 44 ++++++++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 20 deletions(-)
diff --git a/ssh.c b/ssh.c
index d7d77e17..3bac57d2 100644
--- a/ssh.c
+++ b/ssh.c
@@ -670,11 +670,17 @@ struct ssh_portfwd {
((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )
+typedef struct PacketQueueNode PacketQueueNode;
+struct PacketQueueNode {
+ PacketQueueNode *next, *prev;
+};
+
struct PktIn {
int refcount;
int type;
unsigned long sequence; /* SSH-2 incoming sequence number */
long encrypted_len; /* for SSH-2 total-size counting */
+ PacketQueueNode qnode; /* for linking this packet on to a queue */
BinarySource_IMPLEMENTATION;
};
@@ -720,25 +726,20 @@ static PktOut *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin);
static void ssh_unref_packet(PktIn *pkt);
-struct PacketQueueNode {
- struct PacketQueueNode *next, *prev;
- PktIn *pkt;
-};
-
struct PacketQueue {
- struct PacketQueueNode end;
+ PacketQueueNode end;
};
static void pq_init(struct PacketQueue *pq)
{
pq->end.next = pq->end.prev = &pq->end;
- pq->end.pkt = NULL;
}
static void pq_push(struct PacketQueue *pq, PktIn *pkt)
{
- struct PacketQueueNode *node = snew(struct PacketQueueNode);
- node->pkt = pkt;
+ PacketQueueNode *node = &pkt->qnode;
+ assert(!node->next);
+ assert(!node->prev);
node->next = &pq->end;
node->prev = pq->end.prev;
node->next->prev = node;
@@ -747,8 +748,9 @@ static void pq_push(struct PacketQueue *pq, PktIn *pkt)
static void pq_push_front(struct PacketQueue *pq, PktIn *pkt)
{
- struct PacketQueueNode *node = snew(struct PacketQueueNode);
- node->pkt = pkt;
+ PacketQueueNode *node = &pkt->qnode;
+ assert(!node->next);
+ assert(!node->prev);
node->prev = &pq->end;
node->next = pq->end.next;
node->next->prev = node;
@@ -757,25 +759,22 @@ static void pq_push_front(struct PacketQueue *pq, PktIn *pkt)
static PktIn *pq_peek(struct PacketQueue *pq)
{
- return pq->end.next->pkt; /* works even if next == &end, because
- * end.pkt is NULL */
+ if (pq->end.next == &pq->end)
+ return NULL;
+ return FROMFIELD(pq->end.next, PktIn, qnode);
}
static PktIn *pq_pop(struct PacketQueue *pq)
{
- PktIn *pkt;
- struct PacketQueueNode *node;
-
- node = pq->end.next;
+ PacketQueueNode *node = pq->end.next;
if (node == &pq->end)
return NULL;
- pkt = node->pkt;
node->next->prev = node->prev;
node->prev->next = node->next;
- sfree(node);
+ node->prev = node->next = NULL;
- return pkt;
+ return FROMFIELD(node, PktIn, qnode);
}
static void pq_clear(struct PacketQueue *pq)
@@ -1527,6 +1526,7 @@ static void ssh1_rdpkt(Ssh ssh)
* Allocate the packet to return, now we know its length.
*/
st->pktin = snew_plus(PktIn, st->biglen);
+ st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
st->pktin->refcount = 1;
st->pktin->type = 0;
@@ -1829,6 +1829,7 @@ static void ssh2_rdpkt(Ssh ssh)
* Now transfer the data into an output packet.
*/
st->pktin = snew_plus(PktIn, st->maxlen);
+ st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
st->pktin->refcount = 1;
st->pktin->type = 0;
st->data = snew_plus_get_aux(st->pktin);
@@ -1876,6 +1877,7 @@ static void ssh2_rdpkt(Ssh ssh)
* Allocate the packet to return, now we know its length.
*/
st->pktin = snew_plus(PktIn, OUR_V2_PACKETLIMIT + st->maclen);
+ st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
st->pktin->refcount = 1;
st->pktin->type = 0;
st->data = snew_plus_get_aux(st->pktin);
@@ -1947,6 +1949,7 @@ static void ssh2_rdpkt(Ssh ssh)
*/
st->maxlen = st->packetlen + st->maclen;
st->pktin = snew_plus(PktIn, st->maxlen);
+ st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
st->pktin->refcount = 1;
st->pktin->type = 0;
st->data = snew_plus_get_aux(st->pktin);
@@ -2098,6 +2101,7 @@ static void ssh2_bare_connection_rdpkt(Ssh ssh)
* Allocate the packet to return, now we know its length.
*/
st->pktin = snew_plus(PktIn, st->packetlen);
+ st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
st->maxlen = 0;
st->pktin->refcount = 1;
st->data = snew_plus_get_aux(st->pktin);
From 0df6303bb5f2aea6bec6bcc1454bd0971c0358e4 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 6 Jun 2018 20:05:13 +0100
Subject: [PATCH 365/607] Fix a valgrind error.
rsa_ssh1_fingerprint will look at the input key's comment field, which
I forgot to initialise to anything, even the NULL it should be.
---
ssh.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/ssh.c b/ssh.c
index 3bac57d2..bc5ba682 100644
--- a/ssh.c
+++ b/ssh.c
@@ -4141,6 +4141,8 @@ static void do_ssh1_login(void *vctx)
get_rsa_ssh1_pub(pktin, &s->servkey, RSA_SSH1_EXPONENT_FIRST);
get_rsa_ssh1_pub(pktin, &s->hostkey, RSA_SSH1_EXPONENT_FIRST);
+ s->hostkey.comment = NULL; /* avoid confusing rsa_ssh1_fingerprint */
+
/*
* Log the host key fingerprint.
*/
From be6fed13fa53601aab04338f45770318efd35940 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 8 Jun 2018 19:07:47 +0100
Subject: [PATCH 366/607] Further void * / const fixes.
Yet more of these that commits 7babe66a8 and 8d882756b didn't spot. I
bet these still aren't the last, either.
---
misc.c | 2 +-
misc.h | 2 +-
ssh.h | 6 +++---
sshccp.c | 12 ++++++++----
sshmd5.c | 8 +++++---
sshsh256.c | 13 ++++++++-----
sshsha.c | 20 ++++++++++++--------
7 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/misc.c b/misc.c
index 36f27171..92890322 100644
--- a/misc.c
+++ b/misc.c
@@ -485,7 +485,7 @@ struct strbuf_impl {
((buf)->visible.s = (ptr), \
(buf)->visible.u = (unsigned char *)(buf)->visible.s)
-char *strbuf_append(strbuf *buf_o, size_t len)
+void *strbuf_append(strbuf *buf_o, size_t len)
{
struct strbuf_impl *buf = FROMFIELD(buf_o, struct strbuf_impl, visible);
char *toret;
diff --git a/misc.h b/misc.h
index 4b830d99..35d9de6c 100644
--- a/misc.h
+++ b/misc.h
@@ -40,7 +40,7 @@ struct strbuf {
};
strbuf *strbuf_new(void);
void strbuf_free(strbuf *buf);
-char *strbuf_append(strbuf *buf, size_t len);
+void *strbuf_append(strbuf *buf, size_t len);
char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */
void strbuf_catf(strbuf *buf, const char *fmt, ...);
void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap);
diff --git a/ssh.h b/ssh.h
index 1c0f276c..0eaea9f2 100644
--- a/ssh.h
+++ b/ssh.h
@@ -350,10 +350,10 @@ struct ssh_mac {
/* Passes in the cipher context */
void *(*make_context)(void *);
void (*free_context)(void *);
- void (*setkey) (void *, unsigned char *key);
+ void (*setkey) (void *, const void *key);
/* whole-packet operations */
- void (*generate) (void *, unsigned char *blk, int len, unsigned long seq);
- int (*verify) (void *, unsigned char *blk, int len, unsigned long seq);
+ void (*generate) (void *, void *blk, int len, unsigned long seq);
+ int (*verify) (void *, const void *blk, int len, unsigned long seq);
/* partial-packet operations */
void (*start) (void *);
BinarySink *(*sink) (void *);
diff --git a/sshccp.c b/sshccp.c
index 9345c6fa..04f1cc16 100644
--- a/sshccp.c
+++ b/sshccp.c
@@ -878,7 +878,7 @@ static void poly_free_context(void *ctx)
/* Not allocated, just forwarded, no need to free */
}
-static void poly_setkey(void *ctx, unsigned char *key)
+static void poly_setkey(void *ctx, const void *key)
{
/* Uses the same context as ChaCha20, so ignore */
}
@@ -948,7 +948,8 @@ static int poly_verresult(void *handle, unsigned char const *blk)
}
/* The generic poly operation used before generate and verify */
-static void poly_op(void *handle, unsigned char *blk, int len, unsigned long seq)
+static void poly_op(void *handle, const unsigned char *blk, int len,
+ unsigned long seq)
{
struct ccp_context *ctx = (struct ccp_context *)handle;
poly_start(ctx);
@@ -957,14 +958,17 @@ static void poly_op(void *handle, unsigned char *blk, int len, unsigned long seq
put_data(ctx, blk, len);
}
-static void poly_generate(void *handle, unsigned char *blk, int len, unsigned long seq)
+static void poly_generate(void *handle, void *vblk, int len, unsigned long seq)
{
+ unsigned char *blk = (unsigned char *)vblk;
poly_op(handle, blk, len, seq);
poly_genresult(handle, blk+len);
}
-static int poly_verify(void *handle, unsigned char *blk, int len, unsigned long seq)
+static int poly_verify(void *handle, const void *vblk, int len,
+ unsigned long seq)
{
+ const unsigned char *blk = (const unsigned char *)vblk;
poly_op(handle, blk, len, seq);
return poly_verresult(handle, blk+len);
}
diff --git a/sshmd5.c b/sshmd5.c
index 5bd0ccb4..05f00d0b 100644
--- a/sshmd5.c
+++ b/sshmd5.c
@@ -261,7 +261,7 @@ void hmacmd5_key(void *handle, void const *keyv, int len)
smemclr(foo, 64); /* burn the evidence */
}
-static void hmacmd5_key_16(void *handle, unsigned char *key)
+static void hmacmd5_key_16(void *handle, const void *key)
{
hmacmd5_key(handle, key, 16);
}
@@ -329,15 +329,17 @@ static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len,
hmacmd5_do_hmac_internal(handle, seqbuf, 4, blk, len, hmac);
}
-static void hmacmd5_generate(void *handle, unsigned char *blk, int len,
+static void hmacmd5_generate(void *handle, void *vblk, int len,
unsigned long seq)
{
+ unsigned char *blk = (unsigned char *)vblk;
hmacmd5_do_hmac_ssh(handle, blk, len, seq, blk + len);
}
-static int hmacmd5_verify(void *handle, unsigned char *blk, int len,
+static int hmacmd5_verify(void *handle, const void *vblk, int len,
unsigned long seq)
{
+ const unsigned char *blk = (const unsigned char *)vblk;
unsigned char correct[16];
hmacmd5_do_hmac_ssh(handle, blk, len, seq, correct);
return smemeq(correct, blk + len, 16);
diff --git a/sshsh256.c b/sshsh256.c
index 0a0a9546..66752326 100644
--- a/sshsh256.c
+++ b/sshsh256.c
@@ -267,7 +267,8 @@ static void sha256_free_context(void *handle)
sfree(handle);
}
-static void sha256_key_internal(void *handle, unsigned char *key, int len)
+static void sha256_key_internal(void *handle,
+ const unsigned char *key, int len)
{
SHA256_State *keys = (SHA256_State *)handle;
unsigned char foo[64];
@@ -288,7 +289,7 @@ static void sha256_key_internal(void *handle, unsigned char *key, int len)
smemclr(foo, 64); /* burn the evidence */
}
-static void sha256_key(void *handle, unsigned char *key)
+static void sha256_key(void *handle, const void *key)
{
sha256_key_internal(handle, key, 32);
}
@@ -322,7 +323,7 @@ static void hmacsha256_genresult(void *handle, unsigned char *hmac)
SHA256_Final(&s, hmac);
}
-static void sha256_do_hmac(void *handle, unsigned char *blk, int len,
+static void sha256_do_hmac(void *handle, const unsigned char *blk, int len,
unsigned long seq, unsigned char *hmac)
{
BinarySink *bs = hmacsha256_sink(handle);
@@ -332,9 +333,10 @@ static void sha256_do_hmac(void *handle, unsigned char *blk, int len,
hmacsha256_genresult(handle, hmac);
}
-static void sha256_generate(void *handle, unsigned char *blk, int len,
+static void sha256_generate(void *handle, void *vblk, int len,
unsigned long seq)
{
+ unsigned char *blk = (unsigned char *)vblk;
sha256_do_hmac(handle, blk, len, seq, blk + len);
}
@@ -345,9 +347,10 @@ static int hmacsha256_verresult(void *handle, unsigned char const *hmac)
return smemeq(correct, hmac, 32);
}
-static int sha256_verify(void *handle, unsigned char *blk, int len,
+static int sha256_verify(void *handle, const void *vblk, int len,
unsigned long seq)
{
+ const unsigned char *blk = (const unsigned char *)vblk;
unsigned char correct[32];
sha256_do_hmac(handle, blk, len, seq, correct);
return smemeq(correct, blk + len, 32);
diff --git a/sshsha.c b/sshsha.c
index ddcab81e..4b9d002f 100644
--- a/sshsha.c
+++ b/sshsha.c
@@ -294,7 +294,7 @@ static void sha1_free_context(void *handle)
sfree(handle);
}
-static void sha1_key_internal(void *handle, unsigned char *key, int len)
+static void sha1_key_internal(void *handle, const unsigned char *key, int len)
{
SHA_State *keys = (SHA_State *)handle;
unsigned char foo[64];
@@ -315,12 +315,12 @@ static void sha1_key_internal(void *handle, unsigned char *key, int len)
smemclr(foo, 64); /* burn the evidence */
}
-static void sha1_key(void *handle, unsigned char *key)
+static void sha1_key(void *handle, const void *key)
{
sha1_key_internal(handle, key, 20);
}
-static void sha1_key_buggy(void *handle, unsigned char *key)
+static void sha1_key_buggy(void *handle, const void *key)
{
sha1_key_internal(handle, key, 16);
}
@@ -354,7 +354,7 @@ static void hmacsha1_genresult(void *handle, unsigned char *hmac)
SHA_Final(&s, hmac);
}
-static void sha1_do_hmac(void *handle, unsigned char *blk, int len,
+static void sha1_do_hmac(void *handle, const unsigned char *blk, int len,
unsigned long seq, unsigned char *hmac)
{
BinarySink *bs = hmacsha1_sink(handle);
@@ -364,9 +364,10 @@ static void sha1_do_hmac(void *handle, unsigned char *blk, int len,
hmacsha1_genresult(handle, hmac);
}
-static void sha1_generate(void *handle, unsigned char *blk, int len,
+static void sha1_generate(void *handle, void *vblk, int len,
unsigned long seq)
{
+ unsigned char *blk = (unsigned char *)vblk;
sha1_do_hmac(handle, blk, len, seq, blk + len);
}
@@ -377,9 +378,10 @@ static int hmacsha1_verresult(void *handle, unsigned char const *hmac)
return smemeq(correct, hmac, 20);
}
-static int sha1_verify(void *handle, unsigned char *blk, int len,
+static int sha1_verify(void *handle, const void *vblk, int len,
unsigned long seq)
{
+ const unsigned char *blk = (const unsigned char *)vblk;
unsigned char correct[20];
sha1_do_hmac(handle, blk, len, seq, correct);
return smemeq(correct, blk + len, 20);
@@ -392,9 +394,10 @@ static void hmacsha1_96_genresult(void *handle, unsigned char *hmac)
memcpy(hmac, full, 12);
}
-static void sha1_96_generate(void *handle, unsigned char *blk, int len,
+static void sha1_96_generate(void *handle, void *vblk, int len,
unsigned long seq)
{
+ unsigned char *blk = (unsigned char *)vblk;
unsigned char full[20];
sha1_do_hmac(handle, blk, len, seq, full);
memcpy(blk + len, full, 12);
@@ -407,9 +410,10 @@ static int hmacsha1_96_verresult(void *handle, unsigned char const *hmac)
return smemeq(correct, hmac, 12);
}
-static int sha1_96_verify(void *handle, unsigned char *blk, int len,
+static int sha1_96_verify(void *handle, const void *vblk, int len,
unsigned long seq)
{
+ const unsigned char *blk = (const unsigned char *)vblk;
unsigned char correct[20];
sha1_do_hmac(handle, blk, len, seq, correct);
return smemeq(correct, blk + len, 12);
From 734ada9b573393022888bb4338f8772af198515e Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 9 Jun 2018 07:52:28 +0100
Subject: [PATCH 367/607] gdb.py: add a 'memdump' command.
This makes it easier for me to examine the contents of binary memory
buffers, while debugging through code that does crypto or packet
marshalling.
---
contrib/gdb.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/contrib/gdb.py b/contrib/gdb.py
index 14574bb2..a993cc89 100644
--- a/contrib/gdb.py
+++ b/contrib/gdb.py
@@ -35,3 +35,62 @@ def to_string(self):
PuTTYBignumPrettyPrinter)
gdb.printing.register_pretty_printer(None, rcpp)
+
+class MemDumpCommand(gdb.Command):
+ """Print a hex+ASCII dump of object EXP.
+
+EXP must be an expression whose value resides in memory. The
+contents of the memory it occupies are printed in a standard hex
+dump format, with each line showing an offset relative to the
+address of EXP, then the hex byte values of the memory at that
+offset, and then a translation into ASCII of the same bytes (with
+values outside the printable ASCII range translated as '.').
+
+To dump a number of bytes from a particular address, it's useful
+to use the gdb expression extensions {TYPE} and @LENGTH. For
+example, if 'ptr' and 'len' are variables giving an address and a
+length in bytes, then the command
+
+ memdump {char} ptr @ len
+
+will dump the range of memory described by those two variables."""
+
+ def __init__(self):
+ super(MemDumpCommand, self).__init__(
+ "memdump", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION)
+
+ def invoke(self, cmdline, from_tty):
+ expr = gdb.parse_and_eval(cmdline)
+ try:
+ start, size = int(expr.address), expr.type.sizeof
+ except gdb.error as e:
+ sys.stderr.write(str(e))
+ return
+ except (TypeError, AttributeError):
+ sys.stderr.write("expression must identify an object in memory")
+ return
+
+ width = 16
+ line_ptr_type = gdb.lookup_type(
+ "unsigned char").const().array(width).pointer()
+
+ dumpaddr = 0
+ while size > 0:
+ line = gdb.Value(start).cast(line_ptr_type).dereference()
+ thislinelen = min(size, width)
+ start += thislinelen
+ size -= thislinelen
+
+ dumpline = [None, " "] + [" "] * width + [" "] + [""] * width
+
+ dumpline[0] = "{:08x}".format(dumpaddr)
+ dumpaddr += thislinelen
+
+ for i in range(thislinelen):
+ ch = int(line[i]) & 0xFF
+ dumpline[2+i] = " {:02x}".format(ch)
+ dumpline[3+width+i] = chr(ch) if 0x20 <= ch < 0x7F else "."
+
+ sys.stdout.write("".join(dumpline) + "\n")
+
+MemDumpCommand()
From 72c2b70736de1df2affd5ded566d38939d6564a6 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 9 Jun 2018 09:00:11 +0100
Subject: [PATCH 368/607] Make logblank_t a typedef.
It seems especially silly for a structure whose name ends in
_t to have to have the 'struct' prefix!
---
defs.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/defs.h b/defs.h
index 55468241..bfdf345d 100644
--- a/defs.h
+++ b/defs.h
@@ -62,6 +62,8 @@ typedef struct ptrlen {
size_t len;
} ptrlen;
+typedef struct logblank_t logblank_t;
+
/* Do a compile-time type-check of 'to_check' (without evaluating it),
* as a side effect of returning the value 'to_return'. Note that
* although this macro double-*expands* to_return, it always
From 8b98fea4ae2996df7185bed9128dcebc18929a1d Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 9 Jun 2018 09:01:07 +0100
Subject: [PATCH 369/607] New BinarySink function 'put_padding'.
It is to put_data what memset is to memcpy. Several places
in the code wanted it already, but not _quite_ enough for me to
have written it with the rest of the BinarySink infrastructure
originally.
---
import.c | 3 +--
marshal.c | 11 +++++++++++
marshal.h | 5 +++++
ssh.c | 3 +--
sshpubk.c | 3 +--
5 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/import.c b/import.c
index 218670ee..e95883a0 100644
--- a/import.c
+++ b/import.c
@@ -1010,8 +1010,7 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
origlen = outblob->len;
outlen = (origlen + 8) &~ 7;
pad = outlen - origlen;
- for (i = 0; i < pad; i++)
- put_byte(outblob, pad);
+ put_padding(outblob, pad, pad);
/*
* Invent an iv. Then derive encryption key from passphrase
diff --git a/marshal.c b/marshal.c
index 76c7c05f..a8371b66 100644
--- a/marshal.c
+++ b/marshal.c
@@ -11,6 +11,17 @@ void BinarySink_put_data(BinarySink *bs, const void *data, size_t len)
bs->write(bs, data, len);
}
+void BinarySink_put_padding(BinarySink *bs, unsigned char padbyte, size_t len)
+{
+ char buf[16];
+ memset(buf, padbyte, sizeof(buf));
+ while (len > 0) {
+ size_t thislen = len < sizeof(buf) ? len : sizeof(buf);
+ bs->write(bs, buf, thislen);
+ len -= thislen;
+ }
+}
+
void BinarySink_put_byte(BinarySink *bs, unsigned char val)
{
bs->write(bs, &val, 1);
diff --git a/marshal.h b/marshal.h
index 90bb94ff..0debe82f 100644
--- a/marshal.h
+++ b/marshal.h
@@ -110,6 +110,10 @@ struct BinarySink {
#define put_mp_ssh2(bs, val) \
BinarySink_put_mp_ssh2(BinarySink_UPCAST(bs), val)
+/* Padding with a specified byte. */
+#define put_padding(bs, padbyte, len) \
+ BinarySink_put_padding(BinarySink_UPCAST(bs), padbyte, len)
+
/* Fallback: just emit raw data bytes, using a syntax that matches the
* rest of these macros. */
#define put_data(bs, val, len) \
@@ -126,6 +130,7 @@ struct BinarySink {
* declaration(s) of their other parameter type(s) are in scope.
*/
void BinarySink_put_data(BinarySink *, const void *data, size_t len);
+void BinarySink_put_padding(BinarySink *, unsigned char padbyte, size_t len);
void BinarySink_put_byte(BinarySink *, unsigned char);
void BinarySink_put_bool(BinarySink *, int);
void BinarySink_put_uint16(BinarySink *, unsigned long);
diff --git a/ssh.c b/ssh.c
index bc5ba682..3980aa89 100644
--- a/ssh.c
+++ b/ssh.c
@@ -2803,8 +2803,7 @@ static void ssh2_add_sigblob(Ssh ssh, PktOut *pkt,
strbuf *substr = strbuf_new();
put_data(substr, sigblob, sig_prefix_len);
put_uint32(substr, mod_mp.len);
- while (mod_mp.len-- > sig_mp.len)
- put_byte(substr, 0);
+ put_padding(substr, mod_mp.len - sig_mp.len, 0);
put_data(substr, sig_mp.ptr, sig_mp.len);
put_stringsb(pkt, substr);
return;
diff --git a/sshpubk.c b/sshpubk.c
index 3732a6f2..2144bc2c 100644
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -356,8 +356,7 @@ int rsa_ssh1_savekey(const Filename *filename, struct RSAKey *key,
* Now write zeros until the encrypted portion is a multiple of
* 8 bytes.
*/
- while ((buf->len - estart) % 8)
- put_byte(buf, 0);
+ put_padding(buf, (estart - buf->len) & 7, 0);
/*
* Now encrypt the encrypted portion.
From ba7571291ab4552a1d587f39e295f8886fa9685b Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 9 Jun 2018 09:07:18 +0100
Subject: [PATCH 370/607] Move some ssh.c declarations into header files.
ssh.c has been an unmanageably huge monolith of a source file for too
long, and it's finally time I started breaking it up into smaller
pieces. The first step is to move some declarations - basic types like
packets and packet queues, standard constants, enums, and the
coroutine system - into headers where other files can see them.
---
ssh.c | 180 +++++---------------------------------------------------
ssh.h | 115 ++++++++++++++++++++++++++++++++++++
sshcr.h | 54 +++++++++++++++++
3 files changed, 183 insertions(+), 166 deletions(-)
create mode 100644 sshcr.h
diff --git a/ssh.c b/ssh.c
index 3980aa89..c01f55e6 100644
--- a/ssh.c
+++ b/ssh.c
@@ -15,6 +15,7 @@
#include "storage.h"
#include "marshal.h"
#include "ssh.h"
+#include "sshcr.h"
#ifndef NO_GSSAPI
#include "sshgssc.h"
#include "sshgss.h"
@@ -25,26 +26,6 @@
#define GSS_CTXT_MAYFAIL (1<<3) /* Context may expire during handshake */
#endif
-/*
- * Packet type contexts, so that ssh2_pkt_type can correctly decode
- * the ambiguous type numbers back into the correct type strings.
- */
-typedef enum {
- SSH2_PKTCTX_NOKEX,
- SSH2_PKTCTX_DHGROUP,
- SSH2_PKTCTX_DHGEX,
- SSH2_PKTCTX_ECDHKEX,
- SSH2_PKTCTX_GSSKEX,
- SSH2_PKTCTX_RSAKEX
-} Pkt_KCtx;
-typedef enum {
- SSH2_PKTCTX_NOAUTH,
- SSH2_PKTCTX_PUBLICKEY,
- SSH2_PKTCTX_PASSWORD,
- SSH2_PKTCTX_GSSAPI,
- SSH2_PKTCTX_KBDINTER
-} Pkt_ACtx;
-
static const char *const ssh2_disconnect_reasons[] = {
NULL,
"host not allowed to connect",
@@ -200,7 +181,7 @@ static unsigned long rekey_mins(int rekey_time, unsigned long def)
#define translate(x) if (type == x) return #x
#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
-static const char *ssh1_pkt_type(int type)
+const char *ssh1_pkt_type(int type)
{
translate(SSH1_MSG_DISCONNECT);
translate(SSH1_SMSG_PUBLIC_KEY);
@@ -245,8 +226,7 @@ static const char *ssh1_pkt_type(int type)
translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
return "unknown";
}
-static const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx,
- int type)
+const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
{
translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
@@ -313,57 +293,6 @@ enum {
PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM,
};
-/*
- * Coroutine mechanics for the sillier bits of the code. If these
- * macros look impenetrable to you, you might find it helpful to
- * read
- *
- * https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
- *
- * which explains the theory behind these macros.
- *
- * In particular, if you are getting `case expression not constant'
- * errors when building with MS Visual Studio, this is because MS's
- * Edit and Continue debugging feature causes their compiler to
- * violate ANSI C. To disable Edit and Continue debugging:
- *
- * - right-click ssh.c in the FileView
- * - click Settings
- * - select the C/C++ tab and the General category
- * - under `Debug info:', select anything _other_ than `Program
- * Database for Edit and Continue'.
- */
-#define crBegin(v) { int *crLine = &v; switch(v) { case 0:;
-#define crBeginState crBegin(s->crLine)
-#define crStateP(t, v) \
- struct t *s; \
- if (!(v)) { s = (v) = snew(struct t); s->crLine = 0; } \
- s = (v);
-#define crState(t) crStateP(t, ssh->t)
-#define crFinish(z) } *crLine = 0; return (z); }
-#define crFinishV } *crLine = 0; return; }
-#define crFinishFree(z) } sfree(s); return (z); }
-#define crFinishFreeV } sfree(s); return; }
-#define crReturn(z) \
- do {\
- *crLine =__LINE__; return (z); case __LINE__:;\
- } while (0)
-#define crReturnV \
- do {\
- *crLine=__LINE__; return; case __LINE__:;\
- } while (0)
-#define crStop(z) do{ *crLine = 0; return (z); }while(0)
-#define crStopV do{ *crLine = 0; return; }while(0)
-#define crWaitUntil(c) do { crReturn(0); } while (!(c))
-#define crWaitUntilV(c) do { crReturnV; } while (!(c))
-#define crMaybeWaitUntil(c) do { while (!(c)) crReturn(0); } while (0)
-#define crMaybeWaitUntilV(c) do { while (!(c)) crReturnV; } while (0)
-
-typedef struct PktIn PktIn;
-typedef struct PktOut PktOut;
-
-static struct PktOut *ssh1_pkt_init(int pkt_type);
-static struct PktOut *ssh2_pkt_init(int pkt_type);
static void ssh_pkt_ensure(struct PktOut *, int length);
static void ssh_pkt_adddata(struct PktOut *, const void *data, int len);
static ptrlen ssh2_pkt_construct(Ssh, struct PktOut *);
@@ -386,47 +315,6 @@ static void ssh1_login_input(Ssh ssh);
static void ssh2_userauth_input(Ssh ssh);
static void ssh2_connection_input(Ssh ssh);
-/*
- * Buffer management constants. There are several of these for
- * various different purposes:
- *
- * - SSH1_BUFFER_LIMIT is the amount of backlog that must build up
- * on a local data stream before we throttle the whole SSH
- * connection (in SSH-1 only). Throttling the whole connection is
- * pretty drastic so we set this high in the hope it won't
- * happen very often.
- *
- * - SSH_MAX_BACKLOG is the amount of backlog that must build up
- * on the SSH connection itself before we defensively throttle
- * _all_ local data streams. This is pretty drastic too (though
- * thankfully unlikely in SSH-2 since the window mechanism should
- * ensure that the server never has any need to throttle its end
- * of the connection), so we set this high as well.
- *
- * - OUR_V2_WINSIZE is the default window size we present on SSH-2
- * channels.
- *
- * - OUR_V2_BIGWIN is the window size we advertise for the only
- * channel in a simple connection. It must be <= INT_MAX.
- *
- * - OUR_V2_MAXPKT is the official "maximum packet size" we send
- * to the remote side. This actually has nothing to do with the
- * size of the _packet_, but is instead a limit on the amount
- * of data we're willing to receive in a single SSH2 channel
- * data message.
- *
- * - OUR_V2_PACKETLIMIT is actually the maximum size of SSH
- * _packet_ we're prepared to cope with. It must be a multiple
- * of the cipher block size, and must be at least 35000.
- */
-
-#define SSH1_BUFFER_LIMIT 32768
-#define SSH_MAX_BACKLOG 32768
-#define OUR_V2_WINSIZE 16384
-#define OUR_V2_BIGWIN 0x7fffffff
-#define OUR_V2_MAXPKT 0x4000UL
-#define OUR_V2_PACKETLIMIT 0x9000UL
-
struct ssh_signkey_with_user_pref_id {
const ssh_keyalg *alg;
int id;
@@ -670,40 +558,6 @@ struct ssh_portfwd {
((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )
-typedef struct PacketQueueNode PacketQueueNode;
-struct PacketQueueNode {
- PacketQueueNode *next, *prev;
-};
-
-struct PktIn {
- int refcount;
- int type;
- unsigned long sequence; /* SSH-2 incoming sequence number */
- long encrypted_len; /* for SSH-2 total-size counting */
- PacketQueueNode qnode; /* for linking this packet on to a queue */
- BinarySource_IMPLEMENTATION;
-};
-
-struct PktOut {
- long prefix; /* bytes up to and including type field */
- long length; /* total bytes, including prefix */
- int type;
- long forcepad; /* SSH-2: force padding to at least this length */
- unsigned char *data; /* allocated storage */
- long maxlen; /* amount of storage allocated for `data' */
- long encrypted_len; /* for SSH-2 total-size counting */
-
- /* Extra metadata used in SSH packet logging mode, allowing us to
- * log in the packet header line that the packet came from a
- * connection-sharing downstream and what if anything unusual was
- * done to it. The additional_log_text field is expected to be a
- * static string - it will not be freed. */
- unsigned downstream_id;
- const char *additional_log_text;
-
- BinarySink_IMPLEMENTATION;
-};
-
static void ssh1_protocol_setup(Ssh ssh);
static void ssh2_protocol_setup(Ssh ssh);
static void ssh2_bare_connection_protocol_setup(Ssh ssh);
@@ -724,18 +578,13 @@ static PktOut *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
const char *authtype);
#endif
static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin);
-static void ssh_unref_packet(PktIn *pkt);
-
-struct PacketQueue {
- PacketQueueNode end;
-};
-static void pq_init(struct PacketQueue *pq)
+void pq_init(struct PacketQueue *pq)
{
pq->end.next = pq->end.prev = &pq->end;
}
-static void pq_push(struct PacketQueue *pq, PktIn *pkt)
+void pq_push(struct PacketQueue *pq, PktIn *pkt)
{
PacketQueueNode *node = &pkt->qnode;
assert(!node->next);
@@ -746,7 +595,7 @@ static void pq_push(struct PacketQueue *pq, PktIn *pkt)
node->prev->next = node;
}
-static void pq_push_front(struct PacketQueue *pq, PktIn *pkt)
+void pq_push_front(struct PacketQueue *pq, PktIn *pkt)
{
PacketQueueNode *node = &pkt->qnode;
assert(!node->next);
@@ -757,14 +606,14 @@ static void pq_push_front(struct PacketQueue *pq, PktIn *pkt)
node->prev->next = node;
}
-static PktIn *pq_peek(struct PacketQueue *pq)
+PktIn *pq_peek(struct PacketQueue *pq)
{
if (pq->end.next == &pq->end)
return NULL;
return FROMFIELD(pq->end.next, PktIn, qnode);
}
-static PktIn *pq_pop(struct PacketQueue *pq)
+PktIn *pq_pop(struct PacketQueue *pq)
{
PacketQueueNode *node = pq->end.next;
if (node == &pq->end)
@@ -777,15 +626,14 @@ static PktIn *pq_pop(struct PacketQueue *pq)
return FROMFIELD(node, PktIn, qnode);
}
-static void pq_clear(struct PacketQueue *pq)
+void pq_clear(struct PacketQueue *pq)
{
PktIn *pkt;
while ((pkt = pq_pop(pq)) != NULL)
ssh_unref_packet(pkt);
}
-static int pq_empty_on_to_front_of(struct PacketQueue *src,
- struct PacketQueue *dest)
+int pq_empty_on_to_front_of(struct PacketQueue *src, struct PacketQueue *dest)
{
struct PacketQueueNode *srcfirst, *srclast;
@@ -1373,13 +1221,13 @@ static void c_write_str(Ssh ssh, const char *buf)
c_write(ssh, buf, strlen(buf));
}
-static void ssh_unref_packet(PktIn *pkt)
+void ssh_unref_packet(PktIn *pkt)
{
if (--pkt->refcount <= 0)
sfree(pkt);
}
-static void ssh_free_pktout(PktOut *pkt)
+void ssh_free_pktout(PktOut *pkt)
{
sfree(pkt->data);
sfree(pkt);
@@ -2347,7 +2195,7 @@ static void ssh_pkt_BinarySink_write(BinarySink *bs,
ssh_pkt_adddata(pkt, data, len);
}
-static PktOut *ssh1_pkt_init(int pkt_type)
+PktOut *ssh1_pkt_init(int pkt_type)
{
PktOut *pkt = ssh_new_packet();
pkt->length = 4 + 8; /* space for length + max padding */
@@ -2359,7 +2207,7 @@ static PktOut *ssh1_pkt_init(int pkt_type)
return pkt;
}
-static PktOut *ssh2_pkt_init(int pkt_type)
+PktOut *ssh2_pkt_init(int pkt_type)
{
PktOut *pkt = ssh_new_packet();
pkt->length = 5; /* space for packet length + padding length */
diff --git a/ssh.h b/ssh.h
index 0eaea9f2..5e257324 100644
--- a/ssh.h
+++ b/ssh.h
@@ -22,6 +22,118 @@ void sshfwd_x11_sharing_handover(struct ssh_channel *c,
const void *initial_data, int initial_len);
void sshfwd_x11_is_local(struct ssh_channel *c);
+/*
+ * Buffer management constants. There are several of these for
+ * various different purposes:
+ *
+ * - SSH1_BUFFER_LIMIT is the amount of backlog that must build up
+ * on a local data stream before we throttle the whole SSH
+ * connection (in SSH-1 only). Throttling the whole connection is
+ * pretty drastic so we set this high in the hope it won't
+ * happen very often.
+ *
+ * - SSH_MAX_BACKLOG is the amount of backlog that must build up
+ * on the SSH connection itself before we defensively throttle
+ * _all_ local data streams. This is pretty drastic too (though
+ * thankfully unlikely in SSH-2 since the window mechanism should
+ * ensure that the server never has any need to throttle its end
+ * of the connection), so we set this high as well.
+ *
+ * - OUR_V2_WINSIZE is the default window size we present on SSH-2
+ * channels.
+ *
+ * - OUR_V2_BIGWIN is the window size we advertise for the only
+ * channel in a simple connection. It must be <= INT_MAX.
+ *
+ * - OUR_V2_MAXPKT is the official "maximum packet size" we send
+ * to the remote side. This actually has nothing to do with the
+ * size of the _packet_, but is instead a limit on the amount
+ * of data we're willing to receive in a single SSH2 channel
+ * data message.
+ *
+ * - OUR_V2_PACKETLIMIT is actually the maximum size of SSH
+ * _packet_ we're prepared to cope with. It must be a multiple
+ * of the cipher block size, and must be at least 35000.
+ */
+
+#define SSH1_BUFFER_LIMIT 32768
+#define SSH_MAX_BACKLOG 32768
+#define OUR_V2_WINSIZE 16384
+#define OUR_V2_BIGWIN 0x7fffffff
+#define OUR_V2_MAXPKT 0x4000UL
+#define OUR_V2_PACKETLIMIT 0x9000UL
+
+typedef struct PacketQueueNode PacketQueueNode;
+struct PacketQueueNode {
+ PacketQueueNode *next, *prev;
+};
+
+typedef struct PktIn {
+ int refcount;
+ int type;
+ unsigned long sequence; /* SSH-2 incoming sequence number */
+ long encrypted_len; /* for SSH-2 total-size counting */
+ PacketQueueNode qnode; /* for linking this packet on to a queue */
+ BinarySource_IMPLEMENTATION;
+} PktIn;
+
+typedef struct PktOut {
+ long prefix; /* bytes up to and including type field */
+ long length; /* total bytes, including prefix */
+ int type;
+ long forcepad; /* SSH-2: force padding to at least this length */
+ unsigned char *data; /* allocated storage */
+ long maxlen; /* amount of storage allocated for `data' */
+ long encrypted_len; /* for SSH-2 total-size counting */
+
+ /* Extra metadata used in SSH packet logging mode, allowing us to
+ * log in the packet header line that the packet came from a
+ * connection-sharing downstream and what if anything unusual was
+ * done to it. The additional_log_text field is expected to be a
+ * static string - it will not be freed. */
+ unsigned downstream_id;
+ const char *additional_log_text;
+
+ BinarySink_IMPLEMENTATION;
+} PktOut;
+
+typedef struct PacketQueue {
+ PacketQueueNode end;
+} PacketQueue;
+
+void pq_init(struct PacketQueue *pq);
+void pq_push(struct PacketQueue *pq, PktIn *pkt);
+void pq_push_front(struct PacketQueue *pq, PktIn *pkt);
+PktIn *pq_peek(struct PacketQueue *pq);
+PktIn *pq_pop(struct PacketQueue *pq);
+void pq_clear(struct PacketQueue *pq);
+int pq_empty_on_to_front_of(struct PacketQueue *src, struct PacketQueue *dest);
+
+/*
+ * Packet type contexts, so that ssh2_pkt_type can correctly decode
+ * the ambiguous type numbers back into the correct type strings.
+ */
+typedef enum {
+ SSH2_PKTCTX_NOKEX,
+ SSH2_PKTCTX_DHGROUP,
+ SSH2_PKTCTX_DHGEX,
+ SSH2_PKTCTX_ECDHKEX,
+ SSH2_PKTCTX_GSSKEX,
+ SSH2_PKTCTX_RSAKEX
+} Pkt_KCtx;
+typedef enum {
+ SSH2_PKTCTX_NOAUTH,
+ SSH2_PKTCTX_PUBLICKEY,
+ SSH2_PKTCTX_PASSWORD,
+ SSH2_PKTCTX_GSSAPI,
+ SSH2_PKTCTX_KBDINTER
+} Pkt_ACtx;
+
+PktOut *ssh1_pkt_init(int pkt_type);
+PktOut *ssh2_pkt_init(int pkt_type);
+void ssh_unref_packet(PktIn *pkt);
+void ssh_free_pktout(PktOut *pkt);
+
extern Socket ssh_connection_sharing_init(
const char *host, int port, Conf *conf, Ssh ssh, Plug sshplug,
void **state);
@@ -1048,6 +1160,9 @@ void platform_ssh_share_cleanup(const char *name);
#define SSH2_EXTENDED_DATA_STDERR 1 /* 0x1 */
+const char *ssh1_pkt_type(int type);
+const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type);
+
/*
* Need this to warn about support for the original SSH-2 keyfile
* format.
diff --git a/sshcr.h b/sshcr.h
new file mode 100644
index 00000000..18a31ff5
--- /dev/null
+++ b/sshcr.h
@@ -0,0 +1,54 @@
+/*
+ * Coroutine mechanics used in PuTTY's SSH code.
+ */
+
+#ifndef PUTTY_SSHCR_H
+#define PUTTY_SSHCR_H
+
+/*
+ * If these macros look impenetrable to you, you might find it helpful
+ * to read
+ *
+ * https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+ *
+ * which explains the theory behind these macros.
+ *
+ * In particular, if you are getting `case expression not constant'
+ * errors when building with MS Visual Studio, this is because MS's
+ * Edit and Continue debugging feature causes their compiler to
+ * violate ANSI C. To disable Edit and Continue debugging:
+ *
+ * - right-click ssh.c in the FileView
+ * - click Settings
+ * - select the C/C++ tab and the General category
+ * - under `Debug info:', select anything _other_ than `Program
+ * Database for Edit and Continue'.
+ */
+
+#define crBegin(v) { int *crLine = &v; switch(v) { case 0:;
+#define crBeginState crBegin(s->crLine)
+#define crStateP(t, v) \
+ struct t *s; \
+ if (!(v)) { s = (v) = snew(struct t); s->crLine = 0; } \
+ s = (v);
+#define crState(t) crStateP(t, ssh->t)
+#define crFinish(z) } *crLine = 0; return (z); }
+#define crFinishV } *crLine = 0; return; }
+#define crFinishFree(z) } sfree(s); return (z); }
+#define crFinishFreeV } sfree(s); return; }
+#define crReturn(z) \
+ do {\
+ *crLine =__LINE__; return (z); case __LINE__:;\
+ } while (0)
+#define crReturnV \
+ do {\
+ *crLine=__LINE__; return; case __LINE__:;\
+ } while (0)
+#define crStop(z) do{ *crLine = 0; return (z); }while(0)
+#define crStopV do{ *crLine = 0; return; }while(0)
+#define crWaitUntil(c) do { crReturn(0); } while (!(c))
+#define crWaitUntilV(c) do { crReturnV; } while (!(c))
+#define crMaybeWaitUntil(c) do { while (!(c)) crReturn(0); } while (0)
+#define crMaybeWaitUntilV(c) do { while (!(c)) crReturnV; } while (0)
+
+#endif /* PUTTY_SSHCR_H */
From 9e3522a97130ec4aec35a024482d694b3e935a05 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 9 Jun 2018 09:09:10 +0100
Subject: [PATCH 371/607] Use a bufchain for outgoing SSH wire data.
This mirrors the use of one for incoming wire data: now when we send
raw data (be it the initial greeting, or the output of binary packet
construction), we put it on ssh->outgoing_data, and schedule a
callback to transfer that into the socket.
Partly this is in preparation for delegating the task of appending to
that bufchain to a separate self-contained module that won't have
direct access to the connection's Socket. But also, it has the very
nice feature that I get to throw away the ssh_pkt_defer system
completely! That was there so that we could construct more than one
packet in rapid succession, concatenate them into a single blob, and
pass that blob to the socket in one go so that the TCP headers
couldn't contain any trace of where the boundary between them was. But
now we don't need a separate function to do that: calling the ordinary
packet-send routine twice in the same function before returning to the
main event loop will have that effect _anyway_.
---
ssh.c | 257 ++++++++++++++++------------------------------------------
1 file changed, 72 insertions(+), 185 deletions(-)
diff --git a/ssh.c b/ssh.c
index c01f55e6..bdc91005 100644
--- a/ssh.c
+++ b/ssh.c
@@ -793,8 +793,6 @@ struct ssh_tag {
PktOut **queue;
int queuelen, queuesize;
int queueing;
- unsigned char *deferred_send_data;
- int deferred_len, deferred_size;
/*
* Gross hack: pscp will try to start SFTP but fall back to
@@ -858,6 +856,9 @@ struct ssh_tag {
bufchain user_input;
struct IdempotentCallback user_input_consumer;
+ bufchain outgoing_data;
+ struct IdempotentCallback outgoing_data_sender;
+
const char *rekey_reason;
enum RekeyClass rekey_class;
@@ -926,7 +927,7 @@ struct ssh_tag {
* Track incoming and outgoing data sizes and time, for
* size-based rekeys.
*/
- unsigned long incoming_data_size, outgoing_data_size, deferred_data_size;
+ unsigned long incoming_data_size, outgoing_data_size;
unsigned long max_data_size;
int kex_in_progress;
unsigned long next_rekey, last_rekey;
@@ -2059,28 +2060,11 @@ static int s_write(Ssh ssh, const void *data, int len)
}
static void s_wrpkt(Ssh ssh, PktOut *pkt)
-{
- int len, backlog, offset;
- len = s_wrpkt_prepare(ssh, pkt, &offset);
- backlog = s_write(ssh, pkt->data + offset, len);
- if (backlog > SSH_MAX_BACKLOG)
- ssh_throttle_all(ssh, 1, backlog);
- ssh_free_pktout(pkt);
-}
-
-static void s_wrpkt_defer(Ssh ssh, PktOut *pkt)
{
int len, offset;
len = s_wrpkt_prepare(ssh, pkt, &offset);
- if (ssh->deferred_len + len > ssh->deferred_size) {
- ssh->deferred_size = ssh->deferred_len + len + 128;
- ssh->deferred_send_data = sresize(ssh->deferred_send_data,
- ssh->deferred_size,
- unsigned char);
- }
- memcpy(ssh->deferred_send_data + ssh->deferred_len,
- pkt->data + offset, len);
- ssh->deferred_len += len;
+ bufchain_add(&ssh->outgoing_data, pkt->data + offset, len);
+ queue_idempotent_callback(&ssh->outgoing_data_sender);
ssh_free_pktout(pkt);
}
@@ -2141,16 +2125,6 @@ static void send_packet(Ssh ssh, int pkttype, ...)
s_wrpkt(ssh, pkt);
}
-static void defer_packet(Ssh ssh, int pkttype, ...)
-{
- PktOut *pkt;
- va_list ap;
- va_start(ap, pkttype);
- pkt = construct_packet(ssh, pkttype, ap);
- va_end(ap);
- s_wrpkt_defer(ssh, pkt);
-}
-
static int ssh_versioncmp(const char *a, const char *b)
{
char *ae, *be;
@@ -2320,62 +2294,29 @@ static ptrlen ssh2_pkt_construct(Ssh ssh, PktOut *pkt)
}
/*
- * Routines called from the main SSH code to send packets. There
- * are quite a few of these, because we have two separate
- * mechanisms for delaying the sending of packets:
- *
- * - In order to send an IGNORE message and a password message in
- * a single fixed-length blob, we require the ability to
- * concatenate the encrypted forms of those two packets _into_ a
- * single blob and then pass it to our transport
- * layer in one go. Hence, there's a deferment mechanism which
- * works after packet encryption.
- *
- * - In order to avoid sending any connection-layer messages
- * during repeat key exchange, we have to queue up any such
- * outgoing messages _before_ they are encrypted (and in
- * particular before they're allocated sequence numbers), and
- * then send them once we've finished.
- *
- * I call these mechanisms `defer' and `queue' respectively, so as
- * to distinguish them reasonably easily.
- *
- * The functions send_noqueue() and defer_noqueue() free the packet
- * structure they are passed. Every outgoing packet goes through
- * precisely one of these functions in its life; packets passed to
- * ssh2_pkt_send() or ssh2_pkt_defer() either go straight to one of
- * these or get queued, and then when the queue is later emptied
- * the packets are all passed to defer_noqueue().
- *
- * When using a CBC-mode cipher, it's necessary to ensure that an
- * attacker can't provide data to be encrypted using an IV that they
- * know. We ensure this by prefixing each packet that might contain
- * user data with an SSH_MSG_IGNORE. This is done using the deferral
- * mechanism, so in this case send_noqueue() ends up redirecting to
- * defer_noqueue(). If you don't like this inefficiency, don't use
- * CBC.
- */
-
-static void ssh2_pkt_defer_noqueue(Ssh, PktOut *, int);
-static void ssh_pkt_defersend(Ssh);
-
-/*
- * Send an SSH-2 packet immediately, without queuing or deferring.
+ * Send an SSH-2 packet immediately, without queuing.
*/
static void ssh2_pkt_send_noqueue(Ssh ssh, PktOut *pkt)
{
ptrlen data;
- int backlog;
- if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC)) {
- /* We need to send two packets, so use the deferral mechanism. */
- ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
- ssh_pkt_defersend(ssh);
- return;
+ if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
+ bufchain_size(&ssh->outgoing_data) != 0 &&
+ !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
+ /*
+ * When using a CBC-mode cipher in SSH-2, it's necessary to
+ * ensure that an attacker can't provide data to be encrypted
+ * using an IV that they know. We ensure this by prefixing
+ * each packet that might contain user data with an
+ * SSH_MSG_IGNORE.
+ */
+ PktOut *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ put_stringz(ipkt, "");
+ data = ssh2_pkt_construct(ssh, ipkt);
+ bufchain_add(&ssh->outgoing_data, data.ptr, data.len);
}
data = ssh2_pkt_construct(ssh, pkt);
- backlog = s_write(ssh, data.ptr, data.len);
- if (backlog > SSH_MAX_BACKLOG)
- ssh_throttle_all(ssh, 1, backlog);
+ bufchain_add(&ssh->outgoing_data, data.ptr, data.len);
+ queue_idempotent_callback(&ssh->outgoing_data_sender);
ssh->outgoing_data_size += pkt->encrypted_len;
if (!ssh->kex_in_progress &&
@@ -2390,36 +2331,6 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, PktOut *pkt)
ssh_free_pktout(pkt);
}
-/*
- * Defer an SSH-2 packet.
- */
-static void ssh2_pkt_defer_noqueue(Ssh ssh, PktOut *pkt, int noignore)
-{
- ptrlen data;
- if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
- ssh->deferred_len == 0 && !noignore &&
- !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
- /*
- * Interpose an SSH_MSG_IGNORE to ensure that user data don't
- * get encrypted with a known IV.
- */
- PktOut *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
- put_stringz(ipkt, "");
- ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);
- }
- data = ssh2_pkt_construct(ssh, pkt);
- if (ssh->deferred_len + data.len > ssh->deferred_size) {
- ssh->deferred_size = ssh->deferred_len + data.len + 128;
- ssh->deferred_send_data = sresize(ssh->deferred_send_data,
- ssh->deferred_size,
- unsigned char);
- }
- memcpy(ssh->deferred_send_data + ssh->deferred_len, data.ptr, data.len);
- ssh->deferred_len += data.len;
- ssh->deferred_data_size += pkt->encrypted_len;
- ssh_free_pktout(pkt);
-}
-
/*
* Queue an SSH-2 packet.
*/
@@ -2441,58 +2352,38 @@ static void ssh2_pkt_queue(Ssh ssh, PktOut *pkt)
*/
static void ssh2_pkt_send(Ssh ssh, PktOut *pkt)
{
- if (ssh->queueing)
+ if (ssh->queueing) {
ssh2_pkt_queue(ssh, pkt);
- else
+ } else {
ssh2_pkt_send_noqueue(ssh, pkt);
+ }
}
-/*
- * Either queue or defer a packet, depending on whether queueing is
- * set.
- */
-static void ssh2_pkt_defer(Ssh ssh, PktOut *pkt)
+static void ssh_send_outgoing_data(void *ctx)
{
- if (ssh->queueing)
- ssh2_pkt_queue(ssh, pkt);
- else
- ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
-}
+ Ssh ssh = (Ssh)ctx;
-/*
- * Send the whole deferred data block constructed by
- * ssh2_pkt_defer() or SSH-1's defer_packet().
- *
- * The expected use of the defer mechanism is that you call
- * ssh2_pkt_defer() a few times, then call ssh_pkt_defersend(). If
- * not currently queueing, this simply sets up deferred_send_data
- * and then sends it. If we _are_ currently queueing, the calls to
- * ssh2_pkt_defer() put the deferred packets on to the queue
- * instead, and therefore ssh_pkt_defersend() has no deferred data
- * to send. Hence, there's no need to make it conditional on
- * ssh->queueing.
- */
-static void ssh_pkt_defersend(Ssh ssh)
-{
- int backlog;
- backlog = s_write(ssh, ssh->deferred_send_data, ssh->deferred_len);
- ssh->deferred_len = ssh->deferred_size = 0;
- sfree(ssh->deferred_send_data);
- ssh->deferred_send_data = NULL;
- if (backlog > SSH_MAX_BACKLOG)
- ssh_throttle_all(ssh, 1, backlog);
+ while (bufchain_size(&ssh->outgoing_data) > 0) {
+ void *data;
+ int len, backlog;
- if (ssh->version == 2) {
- ssh->outgoing_data_size += ssh->deferred_data_size;
- ssh->deferred_data_size = 0;
- if (!ssh->kex_in_progress &&
- !ssh->bare_connection &&
- ssh->max_data_size != 0 &&
+ bufchain_prefix(&ssh->outgoing_data, &data, &len);
+ backlog = s_write(ssh, data, len);
+ bufchain_consume(&ssh->outgoing_data, len);
+
+ ssh->outgoing_data_size += len;
+ if (ssh->version == 2 && !ssh->kex_in_progress &&
+ !ssh->bare_connection && ssh->max_data_size != 0 &&
ssh->outgoing_data_size > ssh->max_data_size) {
ssh->rekey_reason = "too much data sent";
ssh->rekey_class = RK_NORMAL;
queue_idempotent_callback(&ssh->ssh2_transport_icb);
}
+
+ if (backlog > SSH_MAX_BACKLOG) {
+ ssh_throttle_all(ssh, 1, backlog);
+ return;
+ }
}
}
@@ -2518,26 +2409,27 @@ static void ssh2_pkt_send_with_padding(Ssh ssh, PktOut *pkt, int padsize)
#endif
{
/*
- * If we can't do that, however, an alternative approach is
- * to use the pkt_defer mechanism to bundle the packet
- * tightly together with an SSH_MSG_IGNORE such that their
- * combined length is a constant. So first we construct the
- * final form of this packet and defer its sending.
+ * If we can't do that, however, an alternative approach is to
+ * bundle the packet tightly together with an SSH_MSG_IGNORE
+ * such that their combined length is a constant. So first we
+ * construct the final form of this packet and append it to
+ * the outgoing_data bufchain...
*/
- ssh2_pkt_defer(ssh, pkt);
+ ssh2_pkt_send(ssh, pkt);
/*
- * Now construct an SSH_MSG_IGNORE which includes a string
- * that's an exact multiple of the cipher block size. (If
- * the cipher is NULL so that the block size is
- * unavailable, we don't do this trick at all, because we
- * gain nothing by it.)
+ * ... but before we return from this function (triggering a
+ * call to the outgoing_data_sender), we also construct an
+ * SSH_MSG_IGNORE which includes a string that's an exact
+ * multiple of the cipher block size. (If the cipher is NULL
+ * so that the block size is unavailable, we don't do this
+ * trick at all, because we gain nothing by it.)
*/
if (ssh->cscipher &&
!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
int stringlen, i;
- stringlen = (256 - ssh->deferred_len);
+ stringlen = (256 - bufchain_size(&ssh->outgoing_data));
stringlen += ssh->cscipher->blksize - 1;
stringlen -= (stringlen % ssh->cscipher->blksize);
if (ssh->cscomp) {
@@ -2559,16 +2451,13 @@ static void ssh2_pkt_send_with_padding(Ssh ssh, PktOut *pkt, int padsize)
put_byte(substr, random_byte());
put_stringsb(pkt, substr);
}
- ssh2_pkt_defer(ssh, pkt);
+ ssh2_pkt_send(ssh, pkt);
}
- ssh_pkt_defersend(ssh);
}
}
/*
- * Send all queued SSH-2 packets. We send them by means of
- * ssh2_pkt_defer_noqueue(), in case they included a pair of
- * packets that needed to be lumped together.
+ * Send all queued SSH-2 packets.
*/
static void ssh2_pkt_queuesend(Ssh ssh)
{
@@ -2577,10 +2466,8 @@ static void ssh2_pkt_queuesend(Ssh ssh)
assert(!ssh->queueing);
for (i = 0; i < ssh->queuelen; i++)
- ssh2_pkt_defer_noqueue(ssh, ssh->queue[i], FALSE);
+ ssh2_pkt_send_noqueue(ssh, ssh->queue[i]);
ssh->queuelen = 0;
-
- ssh_pkt_defersend(ssh);
}
#if 0
@@ -2913,7 +2800,8 @@ static void ssh_send_verstring(Ssh ssh, const char *protoname, char *svers)
logeventf(ssh, "We claim version: %.*s",
strcspn(verstring, "\015\012"), verstring);
- s_write(ssh, verstring, strlen(verstring));
+ bufchain_add(&ssh->outgoing_data, verstring, strlen(verstring));
+ queue_idempotent_callback(&ssh->outgoing_data_sender);
sfree(verstring);
}
@@ -4826,9 +4714,9 @@ static void do_ssh1_login(void *vctx)
for (i = bottom; i <= top; i++) {
if (i == pwlen) {
- defer_packet(ssh, s->pwpkt_type,
- PKT_STR,s->cur_prompt->prompts[0]->result,
- PKT_END);
+ send_packet(ssh, s->pwpkt_type,
+ PKT_STR, s->cur_prompt->prompts[0]->result,
+ PKT_END);
} else {
for (j = 0; j < i; j++) {
do {
@@ -4836,12 +4724,11 @@ static void do_ssh1_login(void *vctx)
} while (randomstr[j] == '\0');
}
randomstr[i] = '\0';
- defer_packet(ssh, SSH1_MSG_IGNORE,
- PKT_STR, randomstr, PKT_END);
+ send_packet(ssh, SSH1_MSG_IGNORE,
+ PKT_STR, randomstr, PKT_END);
}
}
logevent("Sending password with camouflage packets");
- ssh_pkt_defersend(ssh);
sfree(randomstr);
}
else if (!(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
@@ -11898,9 +11785,6 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->eof_needed = FALSE;
ssh->ldisc = NULL;
ssh->logctx = NULL;
- ssh->deferred_send_data = NULL;
- ssh->deferred_len = 0;
- ssh->deferred_size = 0;
ssh->fallback_cmd = 0;
ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
@@ -11954,6 +11838,10 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->user_input_consumer.fn = ssh_process_user_input;
ssh->user_input_consumer.ctx = ssh;
ssh->user_input_consumer.queued = FALSE;
+ bufchain_init(&ssh->outgoing_data);
+ ssh->outgoing_data_sender.fn = ssh_send_outgoing_data;
+ ssh->outgoing_data_sender.ctx = ssh;
+ ssh->outgoing_data_sender.queued = FALSE;
ssh->current_user_input_fn = NULL;
ssh->pending_newkeys = FALSE;
ssh->rekey_reason = NULL;
@@ -12010,8 +11898,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->pinger = NULL;
- ssh->incoming_data_size = ssh->outgoing_data_size =
- ssh->deferred_data_size = 0L;
+ ssh->incoming_data_size = ssh->outgoing_data_size = 0L;
ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf,
CONF_ssh_rekey_data));
ssh->kex_in_progress = FALSE;
@@ -12120,7 +12007,6 @@ static void ssh_free(void *handle)
freetree234(ssh->rportfwds);
ssh->rportfwds = NULL;
}
- sfree(ssh->deferred_send_data);
if (ssh->x11disp)
x11_free_display(ssh->x11disp);
while ((auth = delpos234(ssh->x11authtree, 0)) != NULL)
@@ -12132,6 +12018,7 @@ static void ssh_free(void *handle)
sfree(ssh->do_ssh2_userauth_state);
sfree(ssh->do_ssh2_connection_state);
bufchain_clear(&ssh->incoming_data);
+ bufchain_clear(&ssh->outgoing_data);
sfree(ssh->incoming_data_eof_message);
pq_clear(&ssh->pq_full);
pq_clear(&ssh->pq_ssh1_login);
From 679fa90dfef62153f95816d703a48495546410c7 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 9 Jun 2018 09:09:10 +0100
Subject: [PATCH 372/607] Move binary packet protocols and censoring out of
ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
---
Recipe | 3 +-
ssh.c | 1721 ++++++++++--------------------------------------
ssh.h | 24 +-
ssh1bpp.c | 280 ++++++++
ssh1censor.c | 76 +++
ssh2bpp-bare.c | 160 +++++
ssh2bpp.c | 613 +++++++++++++++++
ssh2censor.c | 107 +++
sshbpp.h | 54 ++
9 files changed, 1659 insertions(+), 1379 deletions(-)
create mode 100644 ssh1bpp.c
create mode 100644 ssh1censor.c
create mode 100644 ssh2bpp-bare.c
create mode 100644 ssh2bpp.c
create mode 100644 ssh2censor.c
create mode 100644 sshbpp.h
diff --git a/Recipe b/Recipe
index c76b14f7..1b84885b 100644
--- a/Recipe
+++ b/Recipe
@@ -250,7 +250,8 @@ GTKMAIN = gtkmain cmdline
NONSSH = telnet raw rlogin ldisc pinger
# SSH back end (putty, plink, pscp, psftp).
-SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+SSH = ssh ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
+ + sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf
+ sshgssc pgssapi sshshare sshecc aqsync marshal nullplug
diff --git a/ssh.c b/ssh.c
index bdc91005..919b2321 100644
--- a/ssh.c
+++ b/ssh.c
@@ -16,6 +16,7 @@
#include "marshal.h"
#include "ssh.h"
#include "sshcr.h"
+#include "sshbpp.h"
#ifndef NO_GSSAPI
#include "sshgssc.h"
#include "sshgss.h"
@@ -295,9 +296,7 @@ enum {
static void ssh_pkt_ensure(struct PktOut *, int length);
static void ssh_pkt_adddata(struct PktOut *, const void *data, int len);
-static ptrlen ssh2_pkt_construct(Ssh, struct PktOut *);
static void ssh2_pkt_send(Ssh, struct PktOut *);
-static void ssh2_pkt_send_noqueue(Ssh, struct PktOut *);
static void do_ssh1_login(void *vctx);
static void do_ssh2_userauth(void *vctx);
static void ssh2_connection_setup(Ssh ssh);
@@ -651,31 +650,6 @@ int pq_empty_on_to_front_of(struct PacketQueue *src, struct PacketQueue *dest)
return TRUE;
}
-struct rdpkt1_state_tag {
- long len, pad, biglen, length, maxlen;
- unsigned char *data;
- unsigned long realcrc, gotcrc;
- int chunk;
- PktIn *pktin;
-};
-
-struct rdpkt2_state_tag {
- long len, pad, payload, packetlen, maclen, length, maxlen;
- unsigned char *buf;
- size_t bufsize;
- unsigned char *data;
- int cipherblk;
- unsigned long incoming_sequence;
- PktIn *pktin;
-};
-
-struct rdpkt2_bare_state_tag {
- long packetlen, maxlen;
- unsigned char *data;
- unsigned long incoming_sequence;
- PktIn *pktin;
-};
-
struct queued_handler;
struct queued_handler {
int msg1, msg2;
@@ -729,28 +703,18 @@ struct ssh_tag {
void *logctx;
unsigned char session_key[32];
- int v1_compressing;
int v1_remote_protoflags;
int v1_local_protoflags;
int agentfwd_enabled;
int X11_fwd_enabled;
int remote_bugs;
- const struct ssh_cipher *cipher;
- void *v1_cipher_ctx;
- void *crcda_ctx;
- const struct ssh2_cipher *cscipher, *sccipher;
- void *cs_cipher_ctx, *sc_cipher_ctx;
- const struct ssh_mac *csmac, *scmac;
- int csmac_etm, scmac_etm;
- void *cs_mac_ctx, *sc_mac_ctx;
- BinarySink *sc_mac_bs;
- const struct ssh_compress *cscomp, *sccomp;
- void *cs_comp_ctx, *sc_comp_ctx;
const struct ssh_kex *kex;
const ssh_keyalg *hostkey_alg;
char *hostkey_str; /* string representation, for easy checking in rekeys */
unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN];
int v2_session_id_len;
+ int v2_cbc_ignore_workaround;
+ int v2_out_cipherblksize;
void *kex_ctx;
int bare_connection;
@@ -804,9 +768,6 @@ struct ssh_tag {
bufchain banner; /* accumulates banners during do_ssh2_userauth */
- Pkt_KCtx pkt_kctx;
- Pkt_ACtx pkt_actx;
-
struct X11Display *x11disp;
struct X11FakeAuth *x11auth;
tree234 *x11authtree;
@@ -816,11 +777,7 @@ struct ssh_tag {
int overall_bufsize;
int throttled_all;
int v1_stdout_throttling;
- unsigned long v2_outgoing_sequence;
- int ssh1_rdpkt_crstate;
- int ssh2_rdpkt_crstate;
- int ssh2_bare_rdpkt_crstate;
int do_ssh1_connection_crstate;
void *do_ssh_init_state;
@@ -862,9 +819,8 @@ struct ssh_tag {
const char *rekey_reason;
enum RekeyClass rekey_class;
- struct rdpkt1_state_tag rdpkt1_state;
- struct rdpkt2_state_tag rdpkt2_state;
- struct rdpkt2_bare_state_tag rdpkt2_bare_state;
+ PacketLogSettings pls;
+ BinaryPacketProtocol *bpp;
void (*general_packet_processing)(Ssh ssh, PktIn *pkt);
void (*current_incoming_data_fn) (Ssh ssh);
@@ -878,12 +834,6 @@ struct ssh_tag {
*/
Conf *conf;
- /*
- * Values cached out of conf so as to avoid the tree234 lookup
- * cost every time they're used.
- */
- int logomitdata;
-
/*
* Dynamically allocated username string created during SSH
* login. Stored in here rather than in the coroutine state so
@@ -909,7 +859,7 @@ struct ssh_tag {
* Dispatch table for packet types that we may have to deal
* with at any time.
*/
- handler_fn_t packet_dispatch[256];
+ handler_fn_t packet_dispatch[SSH_MAX_MSG];
/*
* Queues of one-off handler functions for success/failure
@@ -933,13 +883,6 @@ struct ssh_tag {
unsigned long next_rekey, last_rekey;
const char *deferred_rekey_reason;
- /*
- * Inhibit processing of incoming raw data into packets while
- * we're still waiting for a NEWKEYS message to complete and fill
- * in the new details of how that should be done.
- */
- int pending_newkeys;
-
/*
* Fully qualified host name, which we need if doing GSSAPI.
*/
@@ -997,7 +940,7 @@ static const char *ssh_pkt_type(Ssh ssh, int type)
if (ssh->version == 1)
return ssh1_pkt_type(type);
else
- return ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, type);
+ return ssh2_pkt_type(ssh->pls.kctx, ssh->pls.actx, type);
}
#define logevent(s) logevent(ssh->frontend, s)
@@ -1236,819 +1179,20 @@ void ssh_free_pktout(PktOut *pkt)
static void ssh_pkt_BinarySink_write(BinarySink *bs,
const void *data, size_t len);
-static PktOut *ssh_new_packet(void)
+PktOut *ssh_new_packet(void)
{
PktOut *pkt = snew(PktOut);
BinarySink_INIT(pkt, ssh_pkt_BinarySink_write);
pkt->data = NULL;
+ pkt->length = 0;
pkt->maxlen = 0;
+ pkt->downstream_id = 0;
+ pkt->additional_log_text = NULL;
return pkt;
}
-static void ssh1_log_incoming_packet(Ssh ssh, const PktIn *pkt)
-{
- int nblanks = 0;
- struct logblank_t blanks[4];
- ptrlen str;
- BinarySource src[1];
-
- BinarySource_BARE_INIT(src, BinarySource_UPCAST(pkt)->data,
- BinarySource_UPCAST(pkt)->len);
-
- if (ssh->logomitdata &&
- (pkt->type == SSH1_SMSG_STDOUT_DATA ||
- pkt->type == SSH1_SMSG_STDERR_DATA ||
- pkt->type == SSH1_MSG_CHANNEL_DATA)) {
- /* "Session data" packets - omit the data string. */
- if (pkt->type == SSH1_MSG_CHANNEL_DATA)
- get_uint32(src); /* skip channel id */
- str = get_string(src);
- if (!get_err(src)) {
- blanks[nblanks].offset = src->pos - str.len;
- blanks[nblanks].type = PKTLOG_OMIT;
- blanks[nblanks].len = str.len;
- nblanks++;
- }
- }
- log_packet(ssh->logctx, PKT_INCOMING, pkt->type, ssh1_pkt_type(pkt->type),
- src->data, src->len, nblanks, blanks, NULL, 0, NULL);
-}
-
-static void ssh1_log_outgoing_packet(Ssh ssh, const PktOut *pkt)
-{
- int nblanks = 0;
- struct logblank_t blanks[4];
- ptrlen str;
- BinarySource src[1];
-
- BinarySource_BARE_INIT(src, pkt->data + pkt->prefix,
- pkt->length - pkt->prefix);
-
- if (ssh->logomitdata &&
- (pkt->type == SSH1_CMSG_STDIN_DATA ||
- pkt->type == SSH1_MSG_CHANNEL_DATA)) {
- /* "Session data" packets - omit the data string. */
- if (pkt->type == SSH1_MSG_CHANNEL_DATA)
- get_uint32(src); /* skip channel id */
- str = get_string(src);
- if (!get_err(src)) {
- blanks[nblanks].offset = src->pos - str.len;
- blanks[nblanks].type = PKTLOG_OMIT;
- blanks[nblanks].len = str.len;
- nblanks++;
- }
- }
-
- if ((pkt->type == SSH1_CMSG_AUTH_PASSWORD ||
- pkt->type == SSH1_CMSG_AUTH_TIS_RESPONSE ||
- pkt->type == SSH1_CMSG_AUTH_CCARD_RESPONSE) &&
- conf_get_int(ssh->conf, CONF_logomitpass)) {
- /* If this is a password or similar packet, blank the password(s). */
- blanks[nblanks].offset = 0;
- blanks[nblanks].len = pkt->length;
- blanks[nblanks].type = PKTLOG_BLANK;
- nblanks++;
- } else if (pkt->type == SSH1_CMSG_X11_REQUEST_FORWARDING &&
- conf_get_int(ssh->conf, CONF_logomitpass)) {
- /*
- * If this is an X forwarding request packet, blank the fake
- * auth data.
- *
- * Note that while we blank the X authentication data here, we
- * don't take any special action to blank the start of an X11
- * channel, so using MIT-MAGIC-COOKIE-1 and actually opening
- * an X connection without having session blanking enabled is
- * likely to leak your cookie into the log.
- */
- get_string(src); /* skip protocol name */
- str = get_string(src);
- if (!get_err(src)) {
- blanks[nblanks].offset = src->pos - str.len;
- blanks[nblanks].type = PKTLOG_BLANK;
- blanks[nblanks].len = str.len;
- nblanks++;
- }
- }
-
- log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12],
- ssh1_pkt_type(pkt->data[12]),
- src->data, src->len, nblanks, blanks, NULL, 0, NULL);
-}
-
-/*
- * Collect incoming data in the incoming packet buffer.
- * Decipher and verify the packet when it is completely read.
- * Update the *data and *datalen variables.
- * Return a Packet structure when a packet is completed.
- */
-static void ssh1_rdpkt(Ssh ssh)
-{
- struct rdpkt1_state_tag *st = &ssh->rdpkt1_state;
-
- crBegin(ssh->ssh1_rdpkt_crstate);
-
- while (1) {
- st->maxlen = 0;
- st->length = 0;
-
- {
- unsigned char lenbuf[4];
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data, lenbuf, 4));
- st->len = toint(GET_32BIT_MSB_FIRST(lenbuf));
- }
-
- st->pad = 8 - (st->len % 8);
- st->biglen = st->len + st->pad;
- st->length = st->len - 5;
-
- if (st->biglen < 0) {
- bombout(("Extremely large packet length from server suggests"
- " data stream corruption"));
- ssh_unref_packet(st->pktin);
- crStopV;
- }
-
- /*
- * Allocate the packet to return, now we know its length.
- */
- st->pktin = snew_plus(PktIn, st->biglen);
- st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
- st->pktin->refcount = 1;
- st->pktin->type = 0;
-
- st->maxlen = st->biglen;
- st->data = snew_plus_get_aux(st->pktin);
-
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data, st->data, st->biglen));
-
- if (ssh->cipher && detect_attack(ssh->crcda_ctx, st->data,
- st->biglen, NULL)) {
- bombout(("Network attack (CRC compensation) detected!"));
- ssh_unref_packet(st->pktin);
- crStopV;
- }
-
- if (ssh->cipher)
- ssh->cipher->decrypt(ssh->v1_cipher_ctx, st->data, st->biglen);
-
- st->realcrc = crc32_compute(st->data, st->biglen - 4);
- st->gotcrc = GET_32BIT(st->data + st->biglen - 4);
- if (st->gotcrc != st->realcrc) {
- bombout(("Incorrect CRC received on packet"));
- ssh_unref_packet(st->pktin);
- crStopV;
- }
-
- if (ssh->v1_compressing) {
- unsigned char *decompblk;
- int decomplen;
- if (!zlib_decompress_block(ssh->sc_comp_ctx,
- st->data + st->pad, st->length + 1,
- &decompblk, &decomplen)) {
- bombout(("Zlib decompression encountered invalid data"));
- ssh_unref_packet(st->pktin);
- crStopV;
- }
-
- if (st->maxlen < st->pad + decomplen) {
- PktIn *old_pktin = st->pktin;
-
- st->maxlen = st->pad + decomplen;
- st->pktin = snew_plus(PktIn, st->maxlen);
- *st->pktin = *old_pktin; /* structure copy */
- st->data = snew_plus_get_aux(st->pktin);
-
- smemclr(old_pktin, st->biglen);
- sfree(old_pktin);
- }
-
- memcpy(st->data + st->pad, decompblk, decomplen);
- sfree(decompblk);
- st->length = decomplen - 1;
- }
-
- /*
- * Now we can find the bounds of the semantic content of the
- * packet, and the initial type byte.
- */
- st->pktin->type = st->data[st->pad];
- BinarySource_INIT(st->pktin, st->data + st->pad + 1, st->length);
-
- if (ssh->logctx)
- ssh1_log_incoming_packet(ssh, st->pktin);
-
- /*
- * Mild layer violation: if the message is a DISCONNECT, we
- * should unset the close_expected flag, because now we _do_
- * expect the server to close the network connection
- * afterwards. That way, the more informative connection_fatal
- * message for the disconnect itself won't fight with 'Server
- * unexpectedly closed network connection'.
- */
- if (st->pktin->type == SSH1_MSG_DISCONNECT) {
- ssh->clean_exit = FALSE;
- ssh->close_expected = TRUE;
- ssh->disconnect_message_seen = TRUE;
- }
-
- pq_push(&ssh->pq_full, st->pktin);
- queue_idempotent_callback(&ssh->pq_full_consumer);
- }
- crFinishV;
-}
-
-static void ssh2_log_incoming_packet(Ssh ssh, const PktIn *pkt)
-{
- int nblanks = 0;
- struct logblank_t blanks[4];
- ptrlen str;
- BinarySource src[1];
-
- BinarySource_BARE_INIT(src, BinarySource_UPCAST(pkt)->data,
- BinarySource_UPCAST(pkt)->len);
-
- if (ssh->logomitdata &&
- (pkt->type == SSH2_MSG_CHANNEL_DATA ||
- pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) {
- /* "Session data" packets - omit the data string. */
- get_uint32(src); /* skip channel id */
- if (pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
- get_uint32(src); /* skip extended data type */
- str = get_string(src);
- if (!get_err(src)) {
- blanks[nblanks].offset = src->pos - str.len;
- blanks[nblanks].type = PKTLOG_OMIT;
- blanks[nblanks].len = str.len;
- nblanks++;
- }
- }
-
- log_packet(ssh->logctx, PKT_INCOMING, pkt->type,
- ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->type),
- src->data, src->len, nblanks, blanks, &pkt->sequence,
- 0, NULL);
-}
-
-static void ssh2_log_outgoing_packet(Ssh ssh, const PktOut *pkt)
-{
- int nblanks = 0;
- struct logblank_t blanks[4];
- ptrlen str;
- BinarySource src[1];
-
- BinarySource_BARE_INIT(src, pkt->data + pkt->prefix,
- pkt->length - pkt->prefix);
-
- if (ssh->logomitdata &&
- (pkt->type == SSH2_MSG_CHANNEL_DATA ||
- pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) {
- /* "Session data" packets - omit the data string. */
- get_uint32(src); /* skip channel id */
- if (pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
- get_uint32(src); /* skip extended data type */
- str = get_string(src);
- if (!get_err(src)) {
- blanks[nblanks].offset = src->pos - str.len;
- blanks[nblanks].type = PKTLOG_OMIT;
- blanks[nblanks].len = str.len;
- nblanks++;
- }
- }
-
- if (pkt->type == SSH2_MSG_USERAUTH_REQUEST &&
- conf_get_int(ssh->conf, CONF_logomitpass)) {
- /* If this is a password packet, blank the password(s). */
- get_string(src); /* username */
- get_string(src); /* service name */
- str = get_string(src); /* auth method */
- if (ptrlen_eq_string(str, "password")) {
- get_bool(src);
- /* Blank the password field. */
- str = get_string(src);
- if (!get_err(src)) {
- blanks[nblanks].offset = src->pos - str.len;
- blanks[nblanks].type = PKTLOG_BLANK;
- blanks[nblanks].len = str.len;
- nblanks++;
- /* If there's another password field beyond it (change of
- * password), blank that too. */
- str = get_string(src);
- if (!get_err(src))
- blanks[nblanks-1].len = src->pos - blanks[nblanks].offset;
- }
- }
- } else if (ssh->pkt_actx == SSH2_PKTCTX_KBDINTER &&
- pkt->type == SSH2_MSG_USERAUTH_INFO_RESPONSE &&
- conf_get_int(ssh->conf, CONF_logomitpass)) {
- /* If this is a keyboard-interactive response packet, blank
- * the responses. */
- get_uint32(src);
- blanks[nblanks].offset = src->pos;
- blanks[nblanks].type = PKTLOG_BLANK;
- do {
- str = get_string(src);
- } while (!get_err(src));
- blanks[nblanks].len = src->pos - blanks[nblanks].offset;
- nblanks++;
- } else if (pkt->type == SSH2_MSG_CHANNEL_REQUEST &&
- conf_get_int(ssh->conf, CONF_logomitpass)) {
- /*
- * If this is an X forwarding request packet, blank the fake
- * auth data.
- *
- * Note that while we blank the X authentication data here, we
- * don't take any special action to blank the start of an X11
- * channel, so using MIT-MAGIC-COOKIE-1 and actually opening
- * an X connection without having session blanking enabled is
- * likely to leak your cookie into the log.
- */
- get_uint32(src);
- str = get_string(src);
- if (ptrlen_eq_string(str, "x11-req")) {
- get_bool(src);
- get_bool(src);
- get_string(src);
- str = get_string(src);
- if (!get_err(src)) {
- blanks[nblanks].offset = src->pos - str.len;
- blanks[nblanks].type = PKTLOG_BLANK;
- blanks[nblanks].len = str.len;
- nblanks++;
- }
- }
- }
-
- log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5],
- ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]),
- src->data, src->len, nblanks, blanks,
- &ssh->v2_outgoing_sequence,
- pkt->downstream_id, pkt->additional_log_text);
-}
-
-static void ssh2_rdpkt(Ssh ssh)
-{
- struct rdpkt2_state_tag *st = &ssh->rdpkt2_state;
-
- crBegin(ssh->ssh2_rdpkt_crstate);
-
- st->buf = NULL;
- st->bufsize = 0;
-
- while (1) {
- st->maxlen = 0;
- st->length = 0;
- if (ssh->sccipher)
- st->cipherblk = ssh->sccipher->blksize;
- else
- st->cipherblk = 8;
- if (st->cipherblk < 8)
- st->cipherblk = 8;
- st->maclen = ssh->scmac ? ssh->scmac->len : 0;
-
- if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_IS_CBC) &&
- ssh->scmac && !ssh->scmac_etm) {
- /*
- * When dealing with a CBC-mode cipher, we want to avoid the
- * possibility of an attacker's tweaking the ciphertext stream
- * so as to cause us to feed the same block to the block
- * cipher more than once and thus leak information
- * (VU#958563). The way we do this is not to take any
- * decisions on the basis of anything we've decrypted until
- * we've verified it with a MAC. That includes the packet
- * length, so we just read data and check the MAC repeatedly,
- * and when the MAC passes, see if the length we've got is
- * plausible.
- *
- * This defence is unnecessary in OpenSSH ETM mode, because
- * the whole point of ETM mode is that the attacker can't
- * tweak the ciphertext stream at all without the MAC
- * detecting it before we decrypt anything.
- */
-
- /*
- * Make sure we have buffer space for a maximum-size packet.
- */
- int buflimit = OUR_V2_PACKETLIMIT + st->maclen;
- if (st->bufsize < buflimit) {
- st->bufsize = buflimit;
- st->buf = sresize(st->buf, st->bufsize, unsigned char);
- }
-
- /* Read an amount corresponding to the MAC. */
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data, st->buf, st->maclen));
-
- st->packetlen = 0;
- ssh->scmac->start(ssh->sc_mac_ctx);
- ssh->sc_mac_bs = ssh->scmac->sink(ssh->sc_mac_ctx);
- put_uint32(ssh->sc_mac_bs, st->incoming_sequence);
-
- for (;;) { /* Once around this loop per cipher block. */
- /* Read another cipher-block's worth, and tack it onto the end. */
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data,
- st->buf + (st->packetlen + st->maclen),
- st->cipherblk));
- /* Decrypt one more block (a little further back in the stream). */
- ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->buf + st->packetlen,
- st->cipherblk);
- /* Feed that block to the MAC. */
- put_data(ssh->sc_mac_bs,
- st->buf + st->packetlen, st->cipherblk);
- st->packetlen += st->cipherblk;
- /* See if that gives us a valid packet. */
- if (ssh->scmac->verresult(ssh->sc_mac_ctx,
- st->buf + st->packetlen) &&
- ((st->len = toint(GET_32BIT(st->buf))) ==
- st->packetlen-4))
- break;
- if (st->packetlen >= OUR_V2_PACKETLIMIT) {
- bombout(("No valid incoming packet found"));
- crStopV;
- }
- }
- st->maxlen = st->packetlen + st->maclen;
-
- /*
- * Now transfer the data into an output packet.
- */
- st->pktin = snew_plus(PktIn, st->maxlen);
- st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
- st->pktin->refcount = 1;
- st->pktin->type = 0;
- st->data = snew_plus_get_aux(st->pktin);
- memcpy(st->data, st->buf, st->maxlen);
- } else if (ssh->scmac && ssh->scmac_etm) {
- if (st->bufsize < 4) {
- st->bufsize = 4;
- st->buf = sresize(st->buf, st->bufsize, unsigned char);
- }
-
- /*
- * OpenSSH encrypt-then-MAC mode: the packet length is
- * unencrypted, unless the cipher supports length encryption.
- */
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data, st->buf, 4));
-
- /* Cipher supports length decryption, so do it */
- if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
- /* Keep the packet the same though, so the MAC passes */
- unsigned char len[4];
- memcpy(len, st->buf, 4);
- ssh->sccipher->decrypt_length(ssh->sc_cipher_ctx, len, 4, st->incoming_sequence);
- st->len = toint(GET_32BIT(len));
- } else {
- st->len = toint(GET_32BIT(st->buf));
- }
-
- /*
- * _Completely_ silly lengths should be stomped on before they
- * do us any more damage.
- */
- if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT ||
- st->len % st->cipherblk != 0) {
- bombout(("Incoming packet length field was garbled"));
- crStopV;
- }
-
- /*
- * So now we can work out the total packet length.
- */
- st->packetlen = st->len + 4;
-
- /*
- * Allocate the packet to return, now we know its length.
- */
- st->pktin = snew_plus(PktIn, OUR_V2_PACKETLIMIT + st->maclen);
- st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
- st->pktin->refcount = 1;
- st->pktin->type = 0;
- st->data = snew_plus_get_aux(st->pktin);
- memcpy(st->data, st->buf, 4);
-
- /*
- * Read the remainder of the packet.
- */
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data, st->data + 4,
- st->packetlen + st->maclen - 4));
-
- /*
- * Check the MAC.
- */
- if (ssh->scmac
- && !ssh->scmac->verify(ssh->sc_mac_ctx, st->data,
- st->len + 4, st->incoming_sequence)) {
- bombout(("Incorrect MAC received on packet"));
- ssh_unref_packet(st->pktin);
- crStopV;
- }
-
- /* Decrypt everything between the length field and the MAC. */
- if (ssh->sccipher)
- ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->data + 4, st->packetlen - 4);
- } else {
- if (st->bufsize < st->cipherblk) {
- st->bufsize = st->cipherblk;
- st->buf = sresize(st->buf, st->bufsize, unsigned char);
- }
-
- /*
- * Acquire and decrypt the first block of the packet. This will
- * contain the length and padding details.
- */
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data,
- st->buf, st->cipherblk));
-
- if (ssh->sccipher)
- ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->buf, st->cipherblk);
-
- /*
- * Now get the length figure.
- */
- st->len = toint(GET_32BIT(st->buf));
-
- /*
- * _Completely_ silly lengths should be stomped on before they
- * do us any more damage.
- */
- if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT ||
- (st->len + 4) % st->cipherblk != 0) {
- bombout(("Incoming packet was garbled on decryption"));
- ssh_unref_packet(st->pktin);
- crStopV;
- }
-
- /*
- * So now we can work out the total packet length.
- */
- st->packetlen = st->len + 4;
-
- /*
- * Allocate the packet to return, now we know its length.
- */
- st->maxlen = st->packetlen + st->maclen;
- st->pktin = snew_plus(PktIn, st->maxlen);
- st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
- st->pktin->refcount = 1;
- st->pktin->type = 0;
- st->data = snew_plus_get_aux(st->pktin);
- memcpy(st->data, st->buf, st->cipherblk);
-
- /*
- * Read and decrypt the remainder of the packet.
- */
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data,
- st->data + st->cipherblk,
- st->packetlen + st->maclen - st->cipherblk));
-
- /* Decrypt everything _except_ the MAC. */
- if (ssh->sccipher)
- ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->data + st->cipherblk,
- st->packetlen - st->cipherblk);
-
- /*
- * Check the MAC.
- */
- if (ssh->scmac
- && !ssh->scmac->verify(ssh->sc_mac_ctx, st->data,
- st->len + 4, st->incoming_sequence)) {
- bombout(("Incorrect MAC received on packet"));
- ssh_unref_packet(st->pktin);
- crStopV;
- }
- }
- /* Get and sanity-check the amount of random padding. */
- st->pad = st->data[4];
- if (st->pad < 4 || st->len - st->pad < 1) {
- bombout(("Invalid padding length on received packet"));
- ssh_unref_packet(st->pktin);
- crStopV;
- }
- /*
- * This enables us to deduce the payload length.
- */
- st->payload = st->len - st->pad - 1;
-
- st->length = st->payload + 5;
- st->pktin->encrypted_len = st->packetlen;
-
- st->pktin->sequence = st->incoming_sequence++;
-
- st->length = st->packetlen - st->pad;
- assert(st->length >= 0);
-
- /*
- * Decompress packet payload.
- */
- {
- unsigned char *newpayload;
- int newlen;
- if (ssh->sccomp &&
- ssh->sccomp->decompress(ssh->sc_comp_ctx,
- st->data + 5, st->length - 5,
- &newpayload, &newlen)) {
- if (st->maxlen < newlen + 5) {
- PktIn *old_pktin = st->pktin;
-
- st->maxlen = newlen + 5;
- st->pktin = snew_plus(PktIn, st->maxlen);
- *st->pktin = *old_pktin; /* structure copy */
- st->data = snew_plus_get_aux(st->pktin);
-
- smemclr(old_pktin, st->packetlen + st->maclen);
- sfree(old_pktin);
- }
- st->length = 5 + newlen;
- memcpy(st->data + 5, newpayload, newlen);
- sfree(newpayload);
- }
- }
-
- /*
- * RFC 4253 doesn't explicitly say that completely empty packets
- * with no type byte are forbidden, so treat them as deserving
- * an SSH_MSG_UNIMPLEMENTED.
- */
- if (st->length <= 5) { /* == 5 we hope, but robustness */
- ssh2_msg_something_unimplemented(ssh, st->pktin);
- crStopV;
- }
-
- /*
- * Now we can identify the semantic content of the packet,
- * and also the initial type byte.
- */
- st->pktin->type = st->data[5];
- st->length -= 6;
- assert(st->length >= 0); /* one last double-check */
- BinarySource_INIT(st->pktin, st->data + 6, st->length);
-
- if (ssh->logctx)
- ssh2_log_incoming_packet(ssh, st->pktin);
-
- /*
- * Mild layer violation: if the message is a DISCONNECT, we
- * should unset the close_expected flag, because now we _do_
- * expect the server to close the network connection
- * afterwards. That way, the more informative connection_fatal
- * message for the disconnect itself won't fight with 'Server
- * unexpectedly closed network connection'.
- */
- if (st->pktin->type == SSH2_MSG_DISCONNECT) {
- ssh->clean_exit = FALSE;
- ssh->close_expected = TRUE;
- ssh->disconnect_message_seen = TRUE;
- }
-
- pq_push(&ssh->pq_full, st->pktin);
- queue_idempotent_callback(&ssh->pq_full_consumer);
- if (st->pktin->type == SSH2_MSG_NEWKEYS) {
- /* Mild layer violation: in this situation we must suspend
- * processing of the input byte stream in order to ensure
- * that the transport code has processed NEWKEYS and
- * installed the new cipher. */
- ssh->pending_newkeys = TRUE;
- crReturnV;
- }
- }
- crFinishV;
-}
-
-static void ssh2_bare_connection_rdpkt(Ssh ssh)
-{
- struct rdpkt2_bare_state_tag *st = &ssh->rdpkt2_bare_state;
-
- crBegin(ssh->ssh2_bare_rdpkt_crstate);
-
- while (1) {
- /* Read the length field. */
- {
- unsigned char lenbuf[4];
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data, lenbuf, 4));
- st->packetlen = toint(GET_32BIT_MSB_FIRST(lenbuf));
- }
-
- if (st->packetlen <= 0 || st->packetlen >= OUR_V2_PACKETLIMIT) {
- bombout(("Invalid packet length received"));
- crStopV;
- }
-
- /*
- * Allocate the packet to return, now we know its length.
- */
- st->pktin = snew_plus(PktIn, st->packetlen);
- st->pktin->qnode.prev = st->pktin->qnode.next = NULL;
- st->maxlen = 0;
- st->pktin->refcount = 1;
- st->data = snew_plus_get_aux(st->pktin);
-
- st->pktin->encrypted_len = st->packetlen;
-
- st->pktin->sequence = st->incoming_sequence++;
-
- /*
- * Read the remainder of the packet.
- */
- crMaybeWaitUntilV(bufchain_try_fetch_consume(
- &ssh->incoming_data, st->data, st->packetlen));
-
- /*
- * The data we just read is precisely the initial type byte
- * followed by the packet payload.
- */
- st->pktin->type = st->data[0];
- BinarySource_INIT(st->pktin, st->data + 1, st->packetlen - 1);
-
- /*
- * Log incoming packet, possibly omitting sensitive fields.
- */
- if (ssh->logctx)
- ssh2_log_incoming_packet(ssh, st->pktin);
-
- /*
- * Mild layer violation: if the message is a DISCONNECT, we
- * should unset the close_expected flag, because now we _do_
- * expect the server to close the network connection
- * afterwards. That way, the more informative connection_fatal
- * message for the disconnect itself won't fight with 'Server
- * unexpectedly closed network connection'.
- */
- if (st->pktin->type == SSH2_MSG_DISCONNECT) {
- ssh->clean_exit = FALSE;
- ssh->close_expected = TRUE;
- ssh->disconnect_message_seen = TRUE;
- }
-
- pq_push(&ssh->pq_full, st->pktin);
- queue_idempotent_callback(&ssh->pq_full_consumer);
- }
- crFinishV;
-}
-
-static int s_wrpkt_prepare(Ssh ssh, PktOut *pkt, int *offset_p)
-{
- int pad, biglen, i, pktoffs;
- unsigned long crc;
-#ifdef __SC__
- /*
- * XXX various versions of SC (including 8.8.4) screw up the
- * register allocation in this function and use the same register
- * (D6) for len and as a temporary, with predictable results. The
- * following sledgehammer prevents this.
- */
- volatile
-#endif
- int len;
-
- if (ssh->logctx)
- ssh1_log_outgoing_packet(ssh, pkt);
-
- if (ssh->v1_compressing) {
- unsigned char *compblk;
- int complen;
- zlib_compress_block(ssh->cs_comp_ctx,
- pkt->data + 12, pkt->length - 12,
- &compblk, &complen);
- ssh_pkt_ensure(pkt, complen + 2); /* just in case it's got bigger */
- memcpy(pkt->data + 12, compblk, complen);
- sfree(compblk);
- pkt->length = complen + 12;
- }
-
- ssh_pkt_ensure(pkt, pkt->length + 4); /* space for CRC */
- pkt->length += 4;
- len = pkt->length - 4 - 8; /* len(type+data+CRC) */
- pad = 8 - (len % 8);
- pktoffs = 8 - pad;
- biglen = len + pad; /* len(padding+type+data+CRC) */
-
- for (i = pktoffs; i < 4+8; i++)
- pkt->data[i] = random_byte();
- crc = crc32_compute(pkt->data + pktoffs + 4, biglen - 4); /* all ex len */
- PUT_32BIT(pkt->data + pktoffs + 4 + biglen - 4, crc);
- PUT_32BIT(pkt->data + pktoffs, len);
-
- if (ssh->cipher)
- ssh->cipher->encrypt(ssh->v1_cipher_ctx,
- pkt->data + pktoffs + 4, biglen);
-
- if (offset_p) *offset_p = pktoffs;
- return biglen + 4; /* len(length+padding+type+data+CRC) */
-}
-
static int s_write(Ssh ssh, const void *data, int len)
{
if (len && ssh->logctx)
@@ -2059,15 +1203,6 @@ static int s_write(Ssh ssh, const void *data, int len)
return sk_write(ssh->s, data, len);
}
-static void s_wrpkt(Ssh ssh, PktOut *pkt)
-{
- int len, offset;
- len = s_wrpkt_prepare(ssh, pkt, &offset);
- bufchain_add(&ssh->outgoing_data, pkt->data + offset, len);
- queue_idempotent_callback(&ssh->outgoing_data_sender);
- ssh_free_pktout(pkt);
-}
-
/*
* Construct a SSH-1 packet with the specified contents.
* (This all-at-once interface used to be the only one, but now SSH-1
@@ -2079,7 +1214,7 @@ static PktOut *construct_packet(Ssh ssh, int pkttype, va_list ap)
Bignum bn;
PktOut *pkt;
- pkt = ssh1_pkt_init(pkttype);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, pkttype);
while ((argtype = va_arg(ap, int)) != PKT_END) {
unsigned char *argp, argchar;
@@ -2115,6 +1250,26 @@ static PktOut *construct_packet(Ssh ssh, int pkttype, va_list ap)
return pkt;
}
+static void ssh_pkt_write(Ssh ssh, PktOut *pkt)
+{
+ if (ssh->version == 2 && ssh->v2_cbc_ignore_workaround &&
+ bufchain_size(&ssh->outgoing_data) != 0) {
+ /*
+ * When using a CBC-mode cipher in SSH-2, it's necessary to
+ * ensure that an attacker can't provide data to be encrypted
+ * using an IV that they know. We ensure this by prefixing
+ * each packet that might contain user data with an
+ * SSH_MSG_IGNORE.
+ */
+ PktOut *ipkt = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
+ put_stringz(ipkt, "");
+ ssh_bpp_format_packet(ssh->bpp, pkt);
+ }
+
+ ssh_bpp_format_packet(ssh->bpp, pkt);
+ queue_idempotent_callback(&ssh->outgoing_data_sender);
+}
+
static void send_packet(Ssh ssh, int pkttype, ...)
{
PktOut *pkt;
@@ -2122,7 +1277,7 @@ static void send_packet(Ssh ssh, int pkttype, ...)
va_start(ap, pkttype);
pkt = construct_packet(ssh, pkttype, ap);
va_end(ap);
- s_wrpkt(ssh, pkt);
+ ssh_pkt_write(ssh, pkt);
}
static int ssh_versioncmp(const char *a, const char *b)
@@ -2169,168 +1324,6 @@ static void ssh_pkt_BinarySink_write(BinarySink *bs,
ssh_pkt_adddata(pkt, data, len);
}
-PktOut *ssh1_pkt_init(int pkt_type)
-{
- PktOut *pkt = ssh_new_packet();
- pkt->length = 4 + 8; /* space for length + max padding */
- put_byte(pkt, pkt_type);
- pkt->prefix = pkt->length;
- pkt->type = pkt_type;
- pkt->downstream_id = 0;
- pkt->additional_log_text = NULL;
- return pkt;
-}
-
-PktOut *ssh2_pkt_init(int pkt_type)
-{
- PktOut *pkt = ssh_new_packet();
- pkt->length = 5; /* space for packet length + padding length */
- pkt->forcepad = 0;
- pkt->type = pkt_type;
- put_byte(pkt, (unsigned char) pkt_type);
- pkt->prefix = pkt->length;
- pkt->downstream_id = 0;
- pkt->additional_log_text = NULL;
- return pkt;
-}
-
-/*
- * Construct an SSH-2 final-form packet: compress it, encrypt it, put
- * the MAC on it. Return a slice of pkt->data giving the final packet
- * in ready-to-send form.
- */
-static ptrlen ssh2_pkt_construct(Ssh ssh, PktOut *pkt)
-{
- int cipherblk, maclen, padding, unencrypted_prefix, i;
-
- if (ssh->logctx)
- ssh2_log_outgoing_packet(ssh, pkt);
-
- if (ssh->bare_connection) {
- /*
- * Trivial packet construction for the bare connection
- * protocol.
- */
- long start = pkt->prefix - 1;
- long length = pkt->length - start;
- assert(start >= 4);
- PUT_32BIT(pkt->data + start - 4, length);
- ssh->v2_outgoing_sequence++; /* only for diagnostics, really */
- return make_ptrlen(pkt->data + start - 4, length + 4);
- }
-
- /*
- * Compress packet payload.
- */
- {
- unsigned char *newpayload;
- int newlen;
- if (ssh->cscomp &&
- ssh->cscomp->compress(ssh->cs_comp_ctx, pkt->data + 5,
- pkt->length - 5,
- &newpayload, &newlen)) {
- pkt->length = 5;
- put_data(pkt, newpayload, newlen);
- sfree(newpayload);
- }
- }
-
- /*
- * Add padding. At least four bytes, and must also bring total
- * length (minus MAC) up to a multiple of the block size.
- * If pkt->forcepad is set, make sure the packet is at least that size
- * after padding.
- */
- cipherblk = ssh->cscipher ? ssh->cscipher->blksize : 8; /* block size */
- cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
- padding = 4;
- unencrypted_prefix = (ssh->csmac && ssh->csmac_etm) ? 4 : 0;
- if (pkt->length + padding < pkt->forcepad)
- padding = pkt->forcepad - pkt->length;
- padding +=
- (cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
- % cipherblk;
- assert(padding <= 255);
- maclen = ssh->csmac ? ssh->csmac->len : 0;
- ssh_pkt_ensure(pkt, pkt->length + padding + maclen);
- pkt->data[4] = padding;
- for (i = 0; i < padding; i++)
- pkt->data[pkt->length + i] = random_byte();
- PUT_32BIT(pkt->data, pkt->length + padding - 4);
-
- /* Encrypt length if the scheme requires it */
- if (ssh->cscipher && (ssh->cscipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
- ssh->cscipher->encrypt_length(ssh->cs_cipher_ctx, pkt->data, 4,
- ssh->v2_outgoing_sequence);
- }
-
- if (ssh->csmac && ssh->csmac_etm) {
- /*
- * OpenSSH-defined encrypt-then-MAC protocol.
- */
- if (ssh->cscipher)
- ssh->cscipher->encrypt(ssh->cs_cipher_ctx,
- pkt->data + 4, pkt->length + padding - 4);
- ssh->csmac->generate(ssh->cs_mac_ctx, pkt->data,
- pkt->length + padding,
- ssh->v2_outgoing_sequence);
- } else {
- /*
- * SSH-2 standard protocol.
- */
- if (ssh->csmac)
- ssh->csmac->generate(ssh->cs_mac_ctx, pkt->data,
- pkt->length + padding,
- ssh->v2_outgoing_sequence);
- if (ssh->cscipher)
- ssh->cscipher->encrypt(ssh->cs_cipher_ctx,
- pkt->data, pkt->length + padding);
- }
-
- ssh->v2_outgoing_sequence++; /* whether or not we MACed */
- pkt->encrypted_len = pkt->length + padding;
-
- return make_ptrlen(pkt->data, pkt->length + padding + maclen);
-}
-
-/*
- * Send an SSH-2 packet immediately, without queuing.
- */
-static void ssh2_pkt_send_noqueue(Ssh ssh, PktOut *pkt)
-{
- ptrlen data;
- if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
- bufchain_size(&ssh->outgoing_data) != 0 &&
- !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
- /*
- * When using a CBC-mode cipher in SSH-2, it's necessary to
- * ensure that an attacker can't provide data to be encrypted
- * using an IV that they know. We ensure this by prefixing
- * each packet that might contain user data with an
- * SSH_MSG_IGNORE.
- */
- PktOut *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
- put_stringz(ipkt, "");
- data = ssh2_pkt_construct(ssh, ipkt);
- bufchain_add(&ssh->outgoing_data, data.ptr, data.len);
- }
- data = ssh2_pkt_construct(ssh, pkt);
- bufchain_add(&ssh->outgoing_data, data.ptr, data.len);
- queue_idempotent_callback(&ssh->outgoing_data_sender);
-
- ssh->outgoing_data_size += pkt->encrypted_len;
- if (!ssh->kex_in_progress &&
- !ssh->bare_connection &&
- ssh->max_data_size != 0 &&
- ssh->outgoing_data_size > ssh->max_data_size) {
- ssh->rekey_reason = "too much data sent";
- ssh->rekey_class = RK_NORMAL;
- queue_idempotent_callback(&ssh->ssh2_transport_icb);
- }
-
- ssh_free_pktout(pkt);
-}
-
/*
* Queue an SSH-2 packet.
*/
@@ -2355,7 +1348,7 @@ static void ssh2_pkt_send(Ssh ssh, PktOut *pkt)
if (ssh->queueing) {
ssh2_pkt_queue(ssh, pkt);
} else {
- ssh2_pkt_send_noqueue(ssh, pkt);
+ ssh_pkt_write(ssh, pkt);
}
}
@@ -2415,7 +1408,7 @@ static void ssh2_pkt_send_with_padding(Ssh ssh, PktOut *pkt, int padsize)
* construct the final form of this packet and append it to
* the outgoing_data bufchain...
*/
- ssh2_pkt_send(ssh, pkt);
+ ssh_pkt_write(ssh, pkt);
/*
* ... but before we return from this function (triggering a
@@ -2425,33 +1418,30 @@ static void ssh2_pkt_send_with_padding(Ssh ssh, PktOut *pkt, int padsize)
* so that the block size is unavailable, we don't do this
* trick at all, because we gain nothing by it.)
*/
- if (ssh->cscipher &&
- !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
int stringlen, i;
stringlen = (256 - bufchain_size(&ssh->outgoing_data));
- stringlen += ssh->cscipher->blksize - 1;
- stringlen -= (stringlen % ssh->cscipher->blksize);
- if (ssh->cscomp) {
- /*
- * Temporarily disable actual compression, so we
- * can guarantee to get this string exactly the
- * length we want it. The compression-disabling
- * routine should return an integer indicating how
- * many bytes we should adjust our string length
- * by.
- */
- stringlen -=
- ssh->cscomp->disable_compression(ssh->cs_comp_ctx);
- }
- pkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ stringlen += ssh->v2_out_cipherblksize - 1;
+ stringlen -= (stringlen % ssh->v2_out_cipherblksize);
+
+ /*
+ * Temporarily disable actual compression, so we can
+ * guarantee to get this string exactly the length we want
+ * it. The compression-disabling routine should return an
+ * integer indicating how many bytes we should adjust our
+ * string length by.
+ */
+ stringlen -= ssh2_bpp_temporarily_disable_compression(ssh->bpp);
+
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
{
strbuf *substr = strbuf_new();
for (i = 0; i < stringlen; i++)
put_byte(substr, random_byte());
put_stringsb(pkt, substr);
}
- ssh2_pkt_send(ssh, pkt);
+ ssh_pkt_write(ssh, pkt);
}
}
}
@@ -2466,7 +1456,7 @@ static void ssh2_pkt_queuesend(Ssh ssh)
assert(!ssh->queueing);
for (i = 0; i < ssh->queuelen; i++)
- ssh2_pkt_send_noqueue(ssh, ssh->queue[i]);
+ ssh_pkt_write(ssh, ssh->queue[i]);
ssh->queuelen = 0;
}
@@ -2805,6 +1795,36 @@ static void ssh_send_verstring(Ssh ssh, const char *protoname, char *svers)
sfree(verstring);
}
+static void ssh_feed_to_bpp(Ssh ssh)
+{
+ PacketQueueNode *prev_tail = ssh->pq_full.end.prev;
+
+ assert(ssh->bpp);
+ ssh_bpp_handle_input(ssh->bpp);
+
+ if (ssh->bpp->error) {
+ bomb_out(ssh, ssh->bpp->error); /* also frees the error string */
+ return;
+ }
+
+ if (ssh->bpp->seen_disconnect) {
+ /*
+ * If we've seen a DISCONNECT message, we should unset the
+ * close_expected flag, because now we _do_ expect the server
+ * to close the network connection afterwards. That way, the
+ * more informative connection_fatal message for the
+ * disconnect itself won't fight with 'Server unexpectedly
+ * closed network connection'.
+ */
+ ssh->clean_exit = FALSE;
+ ssh->close_expected = TRUE;
+ ssh->disconnect_message_seen = TRUE;
+ }
+
+ if (ssh->pq_full.end.prev != prev_tail)
+ queue_idempotent_callback(&ssh->pq_full_consumer);
+}
+
static void do_ssh_init(Ssh ssh)
{
static const char protoname[] = "SSH-";
@@ -2863,7 +1883,6 @@ static void do_ssh_init(Ssh ssh)
ssh->session_started = TRUE;
ssh->agentfwd_enabled = FALSE;
- ssh->rdpkt2_state.incoming_sequence = 0;
/*
* Now read the rest of the greeting line.
@@ -2958,16 +1977,21 @@ static void do_ssh_init(Ssh ssh)
*/
ssh2_protocol_setup(ssh);
ssh->general_packet_processing = ssh2_general_packet_processing;
- ssh->current_incoming_data_fn = ssh2_rdpkt;
ssh->current_user_input_fn = NULL;
} else {
/*
* Initialise SSH-1 protocol.
*/
ssh1_protocol_setup(ssh);
- ssh->current_incoming_data_fn = ssh1_rdpkt;
ssh->current_user_input_fn = ssh1_login_input;
}
+ ssh->bpp->out_raw = &ssh->outgoing_data;
+ ssh->bpp->in_raw = &ssh->incoming_data;
+ ssh->bpp->in_pq = &ssh->pq_full;
+ ssh->bpp->pls = &ssh->pls;
+ ssh->bpp->logctx = ssh->logctx;
+ ssh->current_incoming_data_fn = ssh_feed_to_bpp;
+
queue_idempotent_callback(&ssh->incoming_data_consumer);
queue_idempotent_callback(&ssh->user_input_consumer);
if (ssh->version == 2)
@@ -3081,7 +2105,6 @@ static void do_ssh_connection_init(Ssh ssh)
s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */
ssh->agentfwd_enabled = FALSE;
- ssh->rdpkt2_bare_state.incoming_sequence = 0;
logeventf(ssh, "Server version: %s", s->vstring);
ssh_detect_bugs(ssh, s->vstring);
@@ -3116,7 +2139,12 @@ static void do_ssh_connection_init(Ssh ssh)
* Initialise bare connection protocol.
*/
ssh2_bare_connection_protocol_setup(ssh);
- ssh->current_incoming_data_fn = ssh2_bare_connection_rdpkt;
+ ssh->bpp->out_raw = &ssh->outgoing_data;
+ ssh->bpp->in_raw = &ssh->incoming_data;
+ ssh->bpp->in_pq = &ssh->pq_full;
+ ssh->bpp->pls = &ssh->pls;
+ ssh->bpp->logctx = ssh->logctx;
+ ssh->current_incoming_data_fn = ssh_feed_to_bpp;
queue_idempotent_callback(&ssh->incoming_data_consumer);
ssh->current_user_input_fn = ssh2_connection_input;
queue_idempotent_callback(&ssh->user_input_consumer);
@@ -3149,7 +2177,7 @@ static void ssh_process_incoming_data(void *ctx)
if (ssh->state == SSH_STATE_CLOSED)
return;
- if (!ssh->frozen && !ssh->pending_newkeys)
+ if (!ssh->frozen)
ssh->current_incoming_data_fn(ssh);
if (ssh->state == SSH_STATE_CLOSED) /* yes, check _again_ */
@@ -3748,11 +2776,11 @@ static void ssh_disconnect(Ssh ssh, const char *client_reason,
send_packet(ssh, SSH1_MSG_DISCONNECT, PKT_STR, wire_reason,
PKT_END);
} else if (ssh->version == 2) {
- PktOut *pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+ PktOut *pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_DISCONNECT);
put_uint32(pktout, code);
put_stringz(pktout, wire_reason);
put_stringz(pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, pktout);
+ ssh_pkt_write(ssh, pktout);
}
}
ssh->close_expected = TRUE;
@@ -4079,15 +3107,14 @@ static void do_ssh1_login(void *vctx)
sfree(s->rsabuf);
- ssh->cipher = (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
- s->cipher_type == SSH_CIPHER_DES ? &ssh_des :
- &ssh_3des);
- ssh->v1_cipher_ctx = ssh->cipher->make_context();
- ssh->cipher->sesskey(ssh->v1_cipher_ctx, ssh->session_key);
- logeventf(ssh, "Initialised %s encryption", ssh->cipher->text_name);
-
- ssh->crcda_ctx = crcda_make_context();
- logevent("Installing CRC compensation attack detector");
+ {
+ const struct ssh_cipher *cipher =
+ (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
+ s->cipher_type == SSH_CIPHER_DES ? &ssh_des :
+ &ssh_3des);
+ ssh1_bpp_new_cipher(ssh->bpp, cipher, ssh->session_key);
+ logeventf(ssh, "Initialised %s encryption", cipher->text_name);
+ }
if (s->servkey.modulus) {
sfree(s->servkey.modulus);
@@ -4798,7 +3825,7 @@ static void do_ssh1_login(void *vctx)
/* Set up for the next phase */
{
int i;
- for (i = 0; i < 256; i++)
+ for (i = 0; i < SSH_MAX_MSG; i++)
if (ssh->packet_dispatch[i] == ssh1_coro_wrapper_initial)
ssh->packet_dispatch[i] = ssh1_coro_wrapper_session;
ssh->current_user_input_fn = ssh1_connection_input;
@@ -4824,7 +3851,7 @@ static void ssh_channel_try_eof(struct ssh_channel *c)
c->closes |= CLOSES_SENT_EOF;
} else {
PktOut *pktout;
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_EOF);
put_uint32(pktout, c->remoteid);
ssh2_pkt_send(ssh, pktout);
c->closes |= CLOSES_SENT_EOF;
@@ -5203,7 +4230,8 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
* to make on it are rejected.
*/
} else {
- pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
+ pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
put_stringz(pktout, "cancel-tcpip-forward");
put_bool(pktout, 0);/* _don't_ want reply */
if (epf->saddr) {
@@ -5330,7 +4358,8 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
ssh_rportfwd_succfail, pf);
} else {
PktOut *pktout;
- pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
+ pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
put_stringz(pktout, "tcpip-forward");
put_bool(pktout, 1);/* want reply */
put_stringz(pktout, pf->shost);
@@ -5730,7 +4759,7 @@ static void do_ssh1_connection(void *vctx)
ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed);
/* Send the pty request. */
- pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_REQUEST_PTY);
put_stringz(pkt, conf_get_str(ssh->conf, CONF_termtype));
put_uint32(pkt, ssh->term_height);
put_uint32(pkt, ssh->term_width);
@@ -5742,7 +4771,7 @@ static void do_ssh1_connection(void *vctx)
put_byte(pkt, SSH1_TTY_OP_OSPEED);
put_uint32(pkt, ssh->ospeed);
put_byte(pkt, SSH_TTY_OP_END);
- s_wrpkt(ssh, pkt);
+ ssh_pkt_write(ssh, pkt);
ssh->state = SSH_STATE_INTERMED;
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection)) != NULL);
if (pktin->type != SSH1_SMSG_SUCCESS
@@ -5771,12 +4800,8 @@ static void do_ssh1_connection(void *vctx)
} else if (pktin->type == SSH1_SMSG_FAILURE) {
c_write_str(ssh, "Server refused to compress\r\n");
}
- logevent("Started compression");
- ssh->v1_compressing = TRUE;
- ssh->cs_comp_ctx = zlib_compress_init();
- logevent("Initialised zlib (RFC1950) compression");
- ssh->sc_comp_ctx = zlib_decompress_init();
- logevent("Initialised zlib (RFC1950) decompression");
+ logevent("Started zlib (RFC1950) compression");
+ ssh1_bpp_start_compression(ssh->bpp);
}
/*
@@ -5895,10 +4920,12 @@ static void ssh1_protocol_setup(Ssh ssh)
{
int i;
+ ssh->bpp = ssh1_bpp_new();
+
/*
* Most messages are handled by the main protocol routine.
*/
- for (i = 0; i < 256; i++)
+ for (i = 0; i < SSH_MAX_MSG; i++)
ssh->packet_dispatch[i] = ssh1_coro_wrapper_initial;
/*
@@ -5965,8 +4992,8 @@ static void add_to_commasep(strbuf *buf, const char *data)
/*
* SSH-2 key derivation (RFC 4253 section 7.2).
*/
-static unsigned char *ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H,
- char chr, int keylen)
+static void ssh2_mkkey(Ssh ssh, strbuf *out, Bignum K, unsigned char *H,
+ char chr, int keylen)
{
const struct ssh_hash *h = ssh->kex->hash;
int keylen_padded;
@@ -5975,12 +5002,22 @@ static unsigned char *ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H,
BinarySink *bs;
if (keylen == 0)
- return NULL;
+ return;
- /* Round up to the next multiple of hash length. */
+ /*
+ * Round the requested amount of key material up to a multiple of
+ * the length of the hash we're using to make it. This makes life
+ * simpler because then we can just write each hash output block
+ * straight into the output buffer without fiddling about
+ * truncating the last one. Since it's going into a strbuf, and
+ * strbufs are always smemclr()ed on free, there's no need to
+ * worry about leaving extra potentially-sensitive data in memory
+ * that the caller didn't ask for.
+ */
keylen_padded = ((keylen + h->hlen - 1) / h->hlen) * h->hlen;
- key = snewn(keylen_padded, unsigned char);
+ out->len = 0;
+ key = strbuf_append(out, keylen_padded);
/* First hlen bytes. */
s = h->init();
@@ -6010,14 +5047,6 @@ static unsigned char *ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H,
h->free(s);
}
-
- /* Now clear any extra bytes of key material beyond the length
- * we're officially returning, because the caller won't know to
- * smemclr those. */
- if (keylen_padded > keylen)
- smemclr(key + keylen, keylen_padded - keylen);
-
- return key;
}
/*
@@ -6211,13 +5240,12 @@ static void do_ssh2_transport(void *vctx)
int kex_init_value, kex_reply_value;
const struct ssh_mac *const *maclist;
int nmacs;
- const struct ssh2_cipher *cscipher_tobe;
- const struct ssh2_cipher *sccipher_tobe;
- const struct ssh_mac *csmac_tobe;
- const struct ssh_mac *scmac_tobe;
- int csmac_etm_tobe, scmac_etm_tobe;
- const struct ssh_compress *cscomp_tobe;
- const struct ssh_compress *sccomp_tobe;
+ struct {
+ const struct ssh2_cipher *cipher;
+ const struct ssh_mac *mac;
+ int etm_mode;
+ const struct ssh_compress *comp;
+ } in, out;
ptrlen hostkeydata, sigdata;
char *keystr, *fingerprint;
ssh_key *hkey; /* actual host key */
@@ -6261,9 +5289,9 @@ static void do_ssh2_transport(void *vctx)
crBeginState;
- s->cscipher_tobe = s->sccipher_tobe = NULL;
- s->csmac_tobe = s->scmac_tobe = NULL;
- s->cscomp_tobe = s->sccomp_tobe = NULL;
+ s->in.cipher = s->out.cipher = NULL;
+ s->in.mac = s->out.mac = NULL;
+ s->in.comp = s->out.comp = NULL;
s->got_session_id = FALSE;
s->userauth_succeeded = FALSE;
@@ -6328,7 +5356,7 @@ static void do_ssh2_transport(void *vctx)
}
#endif
- ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
+ ssh->pls.kctx = SSH2_PKTCTX_NOKEX;
{
int i, j, k, warn;
struct kexinit_algorithm *alg;
@@ -6616,7 +5644,7 @@ static void do_ssh2_transport(void *vctx)
/*
* Construct and send our key exchange packet.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEXINIT);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEXINIT);
for (i = 0; i < 16; i++)
put_byte(s->pktout, (unsigned char) random_byte());
for (i = 0; i < NKEXLIST; i++) {
@@ -6641,7 +5669,7 @@ static void do_ssh2_transport(void *vctx)
s->our_kexinit = snewn(s->our_kexinitlen, unsigned char);
memcpy(s->our_kexinit, s->pktout->data + 5, s->our_kexinitlen);
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh_pkt_write(ssh, s->pktout);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
@@ -6659,12 +5687,9 @@ static void do_ssh2_transport(void *vctx)
}
ssh->kex = NULL;
ssh->hostkey_alg = NULL;
- s->cscipher_tobe = NULL;
- s->sccipher_tobe = NULL;
- s->csmac_tobe = NULL;
- s->scmac_tobe = NULL;
- s->cscomp_tobe = NULL;
- s->sccomp_tobe = NULL;
+ s->in.cipher = s->out.cipher = NULL;
+ s->in.mac = s->out.mac = NULL;
+ s->in.comp = s->out.comp = NULL;
s->warn_kex = s->warn_hk = FALSE;
s->warn_cscipher = s->warn_sccipher = FALSE;
@@ -6682,16 +5707,16 @@ static void do_ssh2_transport(void *vctx)
* particular MAC, then just select that, and don't even
* bother looking through the server's KEXINIT string for
* MACs. */
- if (i == KEXLIST_CSMAC && s->cscipher_tobe &&
- s->cscipher_tobe->required_mac) {
- s->csmac_tobe = s->cscipher_tobe->required_mac;
- s->csmac_etm_tobe = !!(s->csmac_tobe->etm_name);
+ if (i == KEXLIST_CSMAC && s->out.cipher &&
+ s->out.cipher->required_mac) {
+ s->out.mac = s->out.cipher->required_mac;
+ s->out.etm_mode = !!(s->out.mac->etm_name);
goto matched;
}
- if (i == KEXLIST_SCMAC && s->sccipher_tobe &&
- s->sccipher_tobe->required_mac) {
- s->scmac_tobe = s->sccipher_tobe->required_mac;
- s->scmac_etm_tobe = !!(s->scmac_tobe->etm_name);
+ if (i == KEXLIST_SCMAC && s->in.cipher &&
+ s->in.cipher->required_mac) {
+ s->in.mac = s->in.cipher->required_mac;
+ s->in.etm_mode = !!(s->in.mac->etm_name);
goto matched;
}
@@ -6723,21 +5748,21 @@ static void do_ssh2_transport(void *vctx)
ssh->hostkey_alg = alg->u.hk.hostkey;
s->warn_hk = alg->u.hk.warn;
} else if (i == KEXLIST_CSCIPHER) {
- s->cscipher_tobe = alg->u.cipher.cipher;
+ s->out.cipher = alg->u.cipher.cipher;
s->warn_cscipher = alg->u.cipher.warn;
} else if (i == KEXLIST_SCCIPHER) {
- s->sccipher_tobe = alg->u.cipher.cipher;
+ s->in.cipher = alg->u.cipher.cipher;
s->warn_sccipher = alg->u.cipher.warn;
} else if (i == KEXLIST_CSMAC) {
- s->csmac_tobe = alg->u.mac.mac;
- s->csmac_etm_tobe = alg->u.mac.etm;
+ s->out.mac = alg->u.mac.mac;
+ s->out.etm_mode = alg->u.mac.etm;
} else if (i == KEXLIST_SCMAC) {
- s->scmac_tobe = alg->u.mac.mac;
- s->scmac_etm_tobe = alg->u.mac.etm;
+ s->in.mac = alg->u.mac.mac;
+ s->in.etm_mode = alg->u.mac.etm;
} else if (i == KEXLIST_CSCOMP) {
- s->cscomp_tobe = alg->u.comp;
+ s->out.comp = alg->u.comp;
} else if (i == KEXLIST_SCCOMP) {
- s->sccomp_tobe = alg->u.comp;
+ s->in.comp = alg->u.comp;
}
goto matched;
}
@@ -6883,7 +5908,7 @@ static void do_ssh2_transport(void *vctx)
ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend,
"client-to-server cipher",
- s->cscipher_tobe->name,
+ s->out.cipher->name,
ssh_dialog_callback, ssh);
if (s->dlgret < 0) {
ssh->user_response = -1;
@@ -6902,7 +5927,7 @@ static void do_ssh2_transport(void *vctx)
ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend,
"server-to-client cipher",
- s->sccipher_tobe->name,
+ s->in.cipher->name,
ssh_dialog_callback, ssh);
if (s->dlgret < 0) {
ssh->user_response = -1;
@@ -6930,8 +5955,8 @@ static void do_ssh2_transport(void *vctx)
{
int csbits, scbits;
- csbits = s->cscipher_tobe ? s->cscipher_tobe->real_keybits : 0;
- scbits = s->sccipher_tobe ? s->sccipher_tobe->real_keybits : 0;
+ csbits = s->out.cipher ? s->out.cipher->real_keybits : 0;
+ scbits = s->in.cipher ? s->in.cipher->real_keybits : 0;
s->nbits = (csbits > scbits ? csbits : scbits);
}
/* The keys only have hlen-bit entropy, since they're based on
@@ -6945,7 +5970,7 @@ static void do_ssh2_transport(void *vctx)
*/
if (dh_is_gex(ssh->kex)) {
logevent("Doing Diffie-Hellman group exchange");
- ssh->pkt_kctx = SSH2_PKTCTX_DHGEX;
+ ssh->pls.kctx = SSH2_PKTCTX_DHGEX;
/*
* Work out how big a DH group we will need to allow that
* much data.
@@ -6956,15 +5981,17 @@ static void do_ssh2_transport(void *vctx)
if (s->pbits > DH_MAX_SIZE)
s->pbits = DH_MAX_SIZE;
if ((ssh->remote_bugs & BUG_SSH2_OLDGEX)) {
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD);
put_uint32(s->pktout, s->pbits);
} else {
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_KEX_DH_GEX_REQUEST);
put_uint32(s->pktout, DH_MIN_SIZE);
put_uint32(s->pktout, s->pbits);
put_uint32(s->pktout, DH_MAX_SIZE);
}
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh_pkt_write(ssh, s->pktout);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
@@ -6983,7 +6010,7 @@ static void do_ssh2_transport(void *vctx)
s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
} else {
- ssh->pkt_kctx = SSH2_PKTCTX_DHGROUP;
+ ssh->pls.kctx = SSH2_PKTCTX_DHGROUP;
ssh->kex_ctx = dh_setup_group(ssh->kex);
s->kex_init_value = SSH2_MSG_KEXDH_INIT;
s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
@@ -6998,9 +6025,9 @@ static void do_ssh2_transport(void *vctx)
*/
set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
- s->pktout = ssh2_pkt_init(s->kex_init_value);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, s->kex_init_value);
put_mp_ssh2(s->pktout, s->e);
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh_pkt_write(ssh, s->pktout);
set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
@@ -7055,7 +6082,7 @@ static void do_ssh2_transport(void *vctx)
logeventf(ssh, "Doing ECDH key exchange with curve %s and hash %s",
ssh_ecdhkex_curve_textname(ssh->kex),
ssh->kex->hash->text_name);
- ssh->pkt_kctx = SSH2_PKTCTX_ECDHKEX;
+ ssh->pls.kctx = SSH2_PKTCTX_ECDHKEX;
s->eckey = ssh_ecdhkex_newkey(ssh->kex);
if (!s->eckey) {
@@ -7063,14 +6090,14 @@ static void do_ssh2_transport(void *vctx)
crStopV;
}
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_ECDH_INIT);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEX_ECDH_INIT);
{
strbuf *pubpoint = strbuf_new();
ssh_ecdhkex_getpublic(s->eckey, BinarySink_UPCAST(pubpoint));
put_stringsb(s->pktout, pubpoint);
}
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh_pkt_write(ssh, s->pktout);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
if (pktin->type != SSH2_MSG_KEX_ECDH_REPLY) {
@@ -7112,7 +6139,7 @@ static void do_ssh2_transport(void *vctx)
} else if (ssh->kex->main_type == KEXTYPE_GSS) {
ptrlen data;
- ssh->pkt_kctx = SSH2_PKTCTX_GSSKEX;
+ ssh->pls.kctx = SSH2_PKTCTX_GSSKEX;
s->init_token_sent = 0;
s->complete_rcvd = 0;
s->hkey = NULL;
@@ -7129,8 +6156,8 @@ static void do_ssh2_transport(void *vctx)
{
int csbits, scbits;
- csbits = s->cscipher_tobe->real_keybits;
- scbits = s->sccipher_tobe->real_keybits;
+ csbits = s->out.cipher->real_keybits;
+ scbits = s->in.cipher->real_keybits;
s->nbits = (csbits > scbits ? csbits : scbits);
}
/* The keys only have hlen-bit entropy, since they're based on
@@ -7146,11 +6173,11 @@ static void do_ssh2_transport(void *vctx)
s->pbits = 512 << ((s->nbits - 1) / 64);
logeventf(ssh, "Doing GSSAPI (with Kerberos V5) Diffie-Hellman "
"group exchange, with minimum %d bits", s->pbits);
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEXGSS_GROUPREQ);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEXGSS_GROUPREQ);
put_uint32(s->pktout, s->pbits); /* min */
put_uint32(s->pktout, s->pbits); /* preferred */
put_uint32(s->pktout, s->pbits * 2); /* max */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh_pkt_write(ssh, s->pktout);
crMaybeWaitUntilV(
(pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
@@ -7232,7 +6259,7 @@ static void do_ssh2_transport(void *vctx)
if (!s->init_token_sent) {
s->init_token_sent = 1;
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEXGSS_INIT);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEXGSS_INIT);
if (s->gss_sndtok.length == 0) {
bombout(("GSSAPI key exchange failed:"
" no initial context token"));
@@ -7241,14 +6268,15 @@ static void do_ssh2_transport(void *vctx)
put_string(s->pktout,
s->gss_sndtok.value, s->gss_sndtok.length);
put_mp_ssh2(s->pktout, s->e);
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh_pkt_write(ssh, s->pktout);
ssh->gsslib->free_tok(ssh->gsslib, &s->gss_sndtok);
logevent("GSSAPI key exchange initialised");
} else if (s->gss_sndtok.length != 0) {
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEXGSS_CONTINUE);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_KEXGSS_CONTINUE);
put_string(s->pktout,
s->gss_sndtok.value, s->gss_sndtok.length);
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh_pkt_write(ssh, s->pktout);
ssh->gsslib->free_tok(ssh->gsslib, &s->gss_sndtok);
}
@@ -7366,7 +6394,7 @@ static void do_ssh2_transport(void *vctx)
assert(ssh->kex->main_type == KEXTYPE_RSA);
logeventf(ssh, "Doing RSA key exchange with hash %s",
ssh->kex->hash->text_name);
- ssh->pkt_kctx = SSH2_PKTCTX_RSAKEX;
+ ssh->pls.kctx = SSH2_PKTCTX_RSAKEX;
/*
* RSA key exchange. First expect a KEXRSA_PUBKEY packet
* from the server.
@@ -7431,9 +6459,9 @@ static void do_ssh2_transport(void *vctx)
/*
* And send it off in a return packet.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEXRSA_SECRET);
put_string(s->pktout, outstr, outstrlen);
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh_pkt_write(ssh, s->pktout);
put_string(ssh->exhash_bs, outstr, outstrlen);
@@ -7750,71 +6778,65 @@ static void do_ssh2_transport(void *vctx)
/*
* Send SSH2_MSG_NEWKEYS.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_NEWKEYS);
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_NEWKEYS);
+ ssh_pkt_write(ssh, s->pktout);
ssh->outgoing_data_size = 0; /* start counting from here */
/*
* We've sent client NEWKEYS, so create and initialise
* client-to-server session keys.
*/
- if (ssh->cs_cipher_ctx)
- ssh->cscipher->free_context(ssh->cs_cipher_ctx);
- ssh->cscipher = s->cscipher_tobe;
- if (ssh->cscipher) ssh->cs_cipher_ctx = ssh->cscipher->make_context();
-
- if (ssh->cs_mac_ctx)
- ssh->csmac->free_context(ssh->cs_mac_ctx);
- ssh->csmac = s->csmac_tobe;
- ssh->csmac_etm = s->csmac_etm_tobe;
- if (ssh->csmac)
- ssh->cs_mac_ctx = ssh->csmac->make_context(ssh->cs_cipher_ctx);
-
- if (ssh->cs_comp_ctx)
- ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx);
- ssh->cscomp = s->cscomp_tobe;
- ssh->cs_comp_ctx = ssh->cscomp->compress_init();
+ {
+ strbuf *cipher_key = strbuf_new();
+ strbuf *cipher_iv = strbuf_new();
+ strbuf *mac_key = strbuf_new();
+
+ if (s->out.cipher) {
+ ssh2_mkkey(ssh, cipher_iv, s->K, s->exchange_hash, 'A',
+ s->out.cipher->blksize);
+ ssh2_mkkey(ssh, cipher_key, s->K, s->exchange_hash, 'C',
+ s->out.cipher->padded_keybytes);
+ }
+ if (s->out.mac) {
+ ssh2_mkkey(ssh, mac_key, s->K, s->exchange_hash, 'E',
+ s->out.mac->keylen);
+ }
- /*
- * Set IVs on client-to-server keys. Here we use the exchange
- * hash from the _first_ key exchange.
- */
- if (ssh->cscipher) {
- unsigned char *key;
-
- key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'C',
- ssh->cscipher->padded_keybytes);
- ssh->cscipher->setkey(ssh->cs_cipher_ctx, key);
- smemclr(key, ssh->cscipher->padded_keybytes);
- sfree(key);
-
- key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'A',
- ssh->cscipher->blksize);
- ssh->cscipher->setiv(ssh->cs_cipher_ctx, key);
- smemclr(key, ssh->cscipher->blksize);
- sfree(key);
- }
- if (ssh->csmac) {
- unsigned char *key;
-
- key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'E',
- ssh->csmac->keylen);
- ssh->csmac->setkey(ssh->cs_mac_ctx, key);
- smemclr(key, ssh->csmac->keylen);
- sfree(key);
- }
-
- if (ssh->cscipher)
- logeventf(ssh, "Initialised %.200s client->server encryption",
- ssh->cscipher->text_name);
- if (ssh->csmac)
- logeventf(ssh, "Initialised %.200s client->server MAC algorithm%s%s",
- ssh->csmac->text_name,
- ssh->csmac_etm ? " (in ETM mode)" : "",
- ssh->cscipher->required_mac ? " (required by cipher)" : "");
- if (ssh->cscomp->text_name)
- logeventf(ssh, "Initialised %s compression",
- ssh->cscomp->text_name);
+ ssh2_bpp_new_outgoing_crypto(
+ ssh->bpp,
+ s->out.cipher, cipher_key->u, cipher_iv->u,
+ s->out.mac, s->out.etm_mode, mac_key->u,
+ s->out.comp);
+
+ /*
+ * Remember some details we'll need later for making other
+ * policy decisions based on the crypto we've just
+ * initialised.
+ */
+ ssh->v2_cbc_ignore_workaround = (
+ s->out.cipher &&
+ (s->out.cipher->flags & SSH_CIPHER_IS_CBC) &&
+ !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE));
+ ssh->v2_out_cipherblksize = s->out.cipher->blksize;
+
+ strbuf_free(cipher_key);
+ strbuf_free(cipher_iv);
+ strbuf_free(mac_key);
+ }
+
+ if (s->out.cipher)
+ logeventf(ssh, "Initialised %.200s client->server encryption",
+ s->out.cipher->text_name);
+ if (s->out.mac)
+ logeventf(ssh, "Initialised %.200s client->server"
+ " MAC algorithm%s%s",
+ s->out.mac->text_name,
+ s->out.etm_mode ? " (in ETM mode)" : "",
+ (s->out.cipher->required_mac ?
+ " (required by cipher)" : ""));
+ if (s->out.comp->text_name)
+ logeventf(ssh, "Initialised %s compression",
+ s->out.comp->text_name);
/*
* Now our end of the key exchange is complete, we can send all
@@ -7831,72 +6853,52 @@ static void do_ssh2_transport(void *vctx)
bombout(("expected new-keys packet from server"));
crStopV;
}
- ssh->pending_newkeys = FALSE; /* resume processing incoming data */
ssh->incoming_data_size = 0; /* start counting from here */
/*
* We've seen server NEWKEYS, so create and initialise
* server-to-client session keys.
*/
- if (ssh->sc_cipher_ctx)
- ssh->sccipher->free_context(ssh->sc_cipher_ctx);
- if (s->sccipher_tobe) {
- ssh->sccipher = s->sccipher_tobe;
- ssh->sc_cipher_ctx = ssh->sccipher->make_context();
- }
+ {
+ strbuf *cipher_key = strbuf_new();
+ strbuf *cipher_iv = strbuf_new();
+ strbuf *mac_key = strbuf_new();
+
+ if (s->in.cipher) {
+ ssh2_mkkey(ssh, cipher_iv, s->K, s->exchange_hash, 'B',
+ s->in.cipher->blksize);
+ ssh2_mkkey(ssh, cipher_key, s->K, s->exchange_hash, 'D',
+ s->in.cipher->padded_keybytes);
+ }
+ if (s->in.mac) {
+ ssh2_mkkey(ssh, mac_key, s->K, s->exchange_hash, 'F',
+ s->in.mac->keylen);
+ }
- if (ssh->sc_mac_ctx)
- ssh->scmac->free_context(ssh->sc_mac_ctx);
- if (s->scmac_tobe) {
- ssh->scmac = s->scmac_tobe;
- ssh->scmac_etm = s->scmac_etm_tobe;
- ssh->sc_mac_ctx = ssh->scmac->make_context(ssh->sc_cipher_ctx);
- }
+ ssh2_bpp_new_incoming_crypto(
+ ssh->bpp,
+ s->in.cipher, cipher_key->u, cipher_iv->u,
+ s->in.mac, s->in.etm_mode, mac_key->u,
+ s->in.comp);
- if (ssh->sc_comp_ctx)
- ssh->sccomp->decompress_cleanup(ssh->sc_comp_ctx);
- ssh->sccomp = s->sccomp_tobe;
- ssh->sc_comp_ctx = ssh->sccomp->decompress_init();
+ strbuf_free(cipher_key);
+ strbuf_free(cipher_iv);
+ strbuf_free(mac_key);
+ }
- /*
- * Set IVs on server-to-client keys. Here we use the exchange
- * hash from the _first_ key exchange.
- */
- if (ssh->sccipher) {
- unsigned char *key;
-
- key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'D',
- ssh->sccipher->padded_keybytes);
- ssh->sccipher->setkey(ssh->sc_cipher_ctx, key);
- smemclr(key, ssh->sccipher->padded_keybytes);
- sfree(key);
-
- key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'B',
- ssh->sccipher->blksize);
- ssh->sccipher->setiv(ssh->sc_cipher_ctx, key);
- smemclr(key, ssh->sccipher->blksize);
- sfree(key);
- }
- if (ssh->scmac) {
- unsigned char *key;
-
- key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'F',
- ssh->scmac->keylen);
- ssh->scmac->setkey(ssh->sc_mac_ctx, key);
- smemclr(key, ssh->scmac->keylen);
- sfree(key);
- }
- if (ssh->sccipher)
- logeventf(ssh, "Initialised %.200s server->client encryption",
- ssh->sccipher->text_name);
- if (ssh->scmac)
- logeventf(ssh, "Initialised %.200s server->client MAC algorithm%s%s",
- ssh->scmac->text_name,
- ssh->scmac_etm ? " (in ETM mode)" : "",
- ssh->sccipher->required_mac ? " (required by cipher)" : "");
- if (ssh->sccomp->text_name)
- logeventf(ssh, "Initialised %s decompression",
- ssh->sccomp->text_name);
+ if (s->in.cipher)
+ logeventf(ssh, "Initialised %.200s server->client encryption",
+ s->in.cipher->text_name);
+ if (s->in.mac)
+ logeventf(ssh, "Initialised %.200s server->client"
+ " MAC algorithm%s%s",
+ s->in.mac->text_name,
+ s->in.etm_mode ? " (in ETM mode)" : "",
+ (s->in.cipher->required_mac ?
+ " (required by cipher)" : ""));
+ if (s->in.comp->text_name)
+ logeventf(ssh, "Initialised %s decompression",
+ s->in.comp->text_name);
/*
* Free shared secret.
@@ -8068,7 +7070,7 @@ static int ssh2_try_send(struct ssh_channel *c)
len = c->v.v2.remwindow;
if ((unsigned)len > c->v.v2.remmaxpkt)
len = c->v.v2.remmaxpkt;
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_DATA);
put_uint32(pktout, c->remoteid);
put_string(pktout, data, len);
ssh2_pkt_send(ssh, pktout);
@@ -8162,7 +7164,7 @@ static PktOut *ssh2_chanopen_init(struct ssh_channel *c,
{
PktOut *pktout;
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
+ pktout = ssh_bpp_new_pktout(c->ssh->bpp, SSH2_MSG_CHANNEL_OPEN);
put_stringz(pktout, type);
put_uint32(pktout, c->localid);
put_uint32(pktout, c->v.v2.locwindow);/* our window size */
@@ -8212,7 +7214,7 @@ static PktOut *ssh2_chanreq_init(struct ssh_channel *c,
PktOut *pktout;
assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE)));
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+ pktout = ssh_bpp_new_pktout(c->ssh->bpp, SSH2_MSG_CHANNEL_REQUEST);
put_uint32(pktout, c->remoteid);
put_stringz(pktout, type);
put_bool(pktout, handler != NULL);
@@ -8312,7 +7314,7 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin)
c->v.v2.remlocwin = newwin;
c->v.v2.throttle_state = THROTTLED;
}
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_WINDOW_ADJUST);
put_uint32(pktout, c->remoteid);
put_uint32(pktout, newwin - c->v.v2.locwindow);
ssh2_pkt_send(ssh, pktout);
@@ -8603,7 +7605,7 @@ static void ssh2_channel_check_close(struct ssh_channel *c)
* means the channel is in final wind-up. But we haven't sent
* CLOSE, so let's do so now.
*/
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_CLOSE);
put_uint32(pktout, c->remoteid);
ssh2_pkt_send(ssh, pktout);
c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE;
@@ -9012,7 +8014,7 @@ static void ssh2_msg_channel_request(Ssh ssh, PktIn *pktin)
reply = SSH2_MSG_CHANNEL_FAILURE;
}
if (want_reply) {
- pktout = ssh2_pkt_init(reply);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, reply);
put_uint32(pktout, c->remoteid);
ssh2_pkt_send(ssh, pktout);
}
@@ -9033,7 +8035,7 @@ static void ssh2_msg_global_request(Ssh ssh, PktIn *pktin)
* want_reply.
*/
if (want_reply) {
- pktout = ssh2_pkt_init(SSH2_MSG_REQUEST_FAILURE);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_REQUEST_FAILURE);
ssh2_pkt_send(ssh, pktout);
}
}
@@ -9171,7 +8173,7 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
c->remoteid = remid;
c->halfopen = FALSE;
if (error) {
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_FAILURE);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_OPEN_FAILURE);
put_uint32(pktout, c->remoteid);
put_uint32(pktout, SSH2_OPEN_CONNECT_FAILED);
put_stringz(pktout, error);
@@ -9187,7 +8189,8 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
our_winsize_override;
}
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+ pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
put_uint32(pktout, c->remoteid);
put_uint32(pktout, c->localid);
put_uint32(pktout, c->v.v2.locwindow);
@@ -9561,7 +8564,7 @@ static void do_ssh2_userauth(void *vctx)
/*
* Request userauth protocol, and await a response to it.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_SERVICE_REQUEST);
put_stringz(s->pktout, "ssh-userauth");
ssh2_pkt_send(ssh, s->pktout);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
@@ -9572,7 +8575,7 @@ static void do_ssh2_userauth(void *vctx)
/*
* Request connection protocol directly, without authentication.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_SERVICE_REQUEST);
put_stringz(s->pktout, "ssh-connection");
ssh2_pkt_send(ssh, s->pktout);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
@@ -9809,9 +8812,9 @@ static void do_ssh2_userauth(void *vctx)
* just in case it succeeds, and (b) so that we know what
* authentication methods we can usefully try next.
*/
- ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
+ ssh->pls.actx = SSH2_PKTCTX_NOAUTH;
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(s->pktout, ssh->username);
put_stringz(s->pktout, "ssh-connection");/* service requested */
put_stringz(s->pktout, "none"); /* method */
@@ -9976,7 +8979,7 @@ static void do_ssh2_userauth(void *vctx)
#endif
}
- ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
+ ssh->pls.actx = SSH2_PKTCTX_NOAUTH;
#ifndef NO_GSSAPI
if (s->can_gssapi_keyex_auth && !s->tried_gssapi_keyex_auth) {
@@ -9985,7 +8988,7 @@ static void do_ssh2_userauth(void *vctx)
s->type = AUTH_TYPE_GSSAPI;
s->tried_gssapi_keyex_auth = TRUE;
- ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;
+ ssh->pls.actx = SSH2_PKTCTX_GSSAPI;
if (ssh->gsslib->gsslogmsg)
logevent(ssh->gsslib->gsslogmsg);
@@ -10007,7 +9010,7 @@ static void do_ssh2_userauth(void *vctx)
* Attempt public-key authentication using a key from Pageant.
*/
- ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;
+ ssh->pls.actx = SSH2_PKTCTX_PUBLICKEY;
logeventf(ssh, "Trying Pageant key #%d", s->keyi);
@@ -10021,7 +9024,8 @@ static void do_ssh2_userauth(void *vctx)
}
/* See if server will accept it */
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(s->pktout, ssh->username);
put_stringz(s->pktout, "ssh-connection");
/* service requested */
@@ -10056,7 +9060,8 @@ static void do_ssh2_userauth(void *vctx)
* Server is willing to accept the key.
* Construct a SIGN_REQUEST.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(s->pktout, ssh->username);
put_stringz(s->pktout, "ssh-connection");
/* service requested */
@@ -10131,7 +9136,7 @@ static void do_ssh2_userauth(void *vctx)
struct ssh2_userkey *key; /* not live over crReturn */
char *passphrase; /* not live over crReturn */
- ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;
+ ssh->pls.actx = SSH2_PKTCTX_PUBLICKEY;
s->tried_pubkey_config = TRUE;
@@ -10141,7 +9146,8 @@ static void do_ssh2_userauth(void *vctx)
* First, offer the public blob to see if the server is
* willing to accept it.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(s->pktout, ssh->username);
put_stringz(s->pktout, "ssh-connection");
/* service requested */
@@ -10251,7 +9257,8 @@ static void do_ssh2_userauth(void *vctx)
* has announced that it's willing to accept it.
* Hallelujah. Generate a signature and send it.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(s->pktout, ssh->username);
put_stringz(s->pktout, "ssh-connection");
/* service requested */
@@ -10306,14 +9313,15 @@ static void do_ssh2_userauth(void *vctx)
s->type = AUTH_TYPE_GSSAPI;
s->tried_gssapi = TRUE;
- ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;
+ ssh->pls.actx = SSH2_PKTCTX_GSSAPI;
if (ssh->gsslib->gsslogmsg)
logevent(ssh->gsslib->gsslogmsg);
/* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
logeventf(ssh, "Trying gssapi-with-mic...");
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(s->pktout, ssh->username);
put_stringz(s->pktout, "ssh-connection");
put_stringz(s->pktout, "gssapi-with-mic");
@@ -10419,7 +9427,8 @@ static void do_ssh2_userauth(void *vctx)
*/
if (s->gss_sndtok.length != 0) {
s->pktout =
- ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+ ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
put_string(s->pktout,
s->gss_sndtok.value, s->gss_sndtok.length);
ssh2_pkt_send(ssh, s->pktout);
@@ -10464,9 +9473,10 @@ static void do_ssh2_userauth(void *vctx)
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
- ssh->pkt_actx = SSH2_PKTCTX_KBDINTER;
+ ssh->pls.actx = SSH2_PKTCTX_KBDINTER;
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(s->pktout, ssh->username);
put_stringz(s->pktout, "ssh-connection");
/* service requested */
@@ -10588,7 +9598,8 @@ static void do_ssh2_userauth(void *vctx)
/*
* Send the response(s) to the server.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE);
put_uint32(s->pktout, s->num_prompts);
for (i=0; i < s->num_prompts; i++) {
put_stringz(s->pktout,
@@ -10623,7 +9634,7 @@ static void do_ssh2_userauth(void *vctx)
*/
int changereq_first_time; /* not live over crReturn */
- ssh->pkt_actx = SSH2_PKTCTX_PASSWORD;
+ ssh->pls.actx = SSH2_PKTCTX_PASSWORD;
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
@@ -10675,7 +9686,8 @@ static void do_ssh2_userauth(void *vctx)
* probably doesn't have much to worry about from
* people who find out how long their password is!
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(s->pktout, ssh->username);
put_stringz(s->pktout, "ssh-connection");
/* service requested */
@@ -10807,7 +9819,8 @@ static void do_ssh2_userauth(void *vctx)
* Send the new password (along with the old one).
* (see above for padding rationale)
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(s->pktout, ssh->username);
put_stringz(s->pktout, "ssh-connection");
/* service requested */
@@ -11297,7 +10310,7 @@ static void ssh2_msg_transport(Ssh ssh, PktIn *pktin)
static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin)
{
char *buf = dupprintf("Server protocol violation: unexpected %s packet",
- ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,
+ ssh2_pkt_type(ssh->pls.kctx, ssh->pls.actx,
pktin->type));
ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
sfree(buf);
@@ -11306,13 +10319,13 @@ static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin)
static void ssh2_msg_something_unimplemented(Ssh ssh, PktIn *pktin)
{
PktOut *pktout;
- pktout = ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_UNIMPLEMENTED);
put_uint32(pktout, pktin->sequence);
/*
* UNIMPLEMENTED messages MUST appear in the same order as the
* messages they respond to. Hence, never queue them.
*/
- ssh2_pkt_send_noqueue(ssh, pktout);
+ ssh_pkt_write(ssh, pktout);
}
/*
@@ -11322,6 +10335,8 @@ static void ssh2_protocol_setup(Ssh ssh)
{
int i;
+ ssh->bpp = ssh2_bpp_new();
+
#ifndef NO_GSSAPI
/* Load and pick the highest GSS library on the preference list. */
if (!ssh->gsslibs)
@@ -11353,7 +10368,7 @@ static void ssh2_protocol_setup(Ssh ssh)
/*
* Most messages cause SSH2_MSG_UNIMPLEMENTED.
*/
- for (i = 0; i < 256; i++)
+ for (i = 0; i < SSH_MAX_MSG; i++)
ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented;
/*
@@ -11409,10 +10424,12 @@ static void ssh2_bare_connection_protocol_setup(Ssh ssh)
{
int i;
+ ssh->bpp = ssh2_bare_bpp_new();
+
/*
* Most messages cause SSH2_MSG_UNIMPLEMENTED.
*/
- for (i = 0; i < 256; i++)
+ for (i = 0; i < SSH_MAX_MSG; i++)
ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented;
/*
@@ -11475,9 +10492,9 @@ static PktOut *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
/* Now we can build the real packet */
if (strcmp(authtype, "gssapi-with-mic") == 0) {
- p = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC);
+ p = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_USERAUTH_GSSAPI_MIC);
} else {
- p = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ p = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
put_stringz(p, ssh->username);
put_stringz(p, "ssh-connection");
put_stringz(p, authtype);
@@ -11736,7 +10753,8 @@ static void ssh2_general_packet_processing(Ssh ssh, PktIn *pktin)
static void ssh_cache_conf_values(Ssh ssh)
{
- ssh->logomitdata = conf_get_int(ssh->conf, CONF_logomitdata);
+ ssh->pls.omit_passwords = conf_get_int(ssh->conf, CONF_logomitpass);
+ ssh->pls.omit_data = conf_get_int(ssh->conf, CONF_logomitdata);
}
/*
@@ -11757,21 +10775,6 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh_cache_conf_values(ssh);
ssh->version = 0; /* when not ready yet */
ssh->s = NULL;
- ssh->cipher = NULL;
- ssh->v1_cipher_ctx = NULL;
- ssh->crcda_ctx = NULL;
- ssh->cscipher = NULL;
- ssh->cs_cipher_ctx = NULL;
- ssh->sccipher = NULL;
- ssh->sc_cipher_ctx = NULL;
- ssh->csmac = NULL;
- ssh->cs_mac_ctx = NULL;
- ssh->scmac = NULL;
- ssh->sc_mac_ctx = NULL;
- ssh->cscomp = NULL;
- ssh->cs_comp_ctx = NULL;
- ssh->sccomp = NULL;
- ssh->sc_comp_ctx = NULL;
ssh->kex = NULL;
ssh->kex_ctx = NULL;
ssh->hostkey_alg = NULL;
@@ -11786,17 +10789,13 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->ldisc = NULL;
ssh->logctx = NULL;
ssh->fallback_cmd = 0;
- ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
- ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
+ ssh->pls.kctx = SSH2_PKTCTX_NOKEX;
+ ssh->pls.actx = SSH2_PKTCTX_NOAUTH;
ssh->x11disp = NULL;
ssh->x11auth = NULL;
ssh->x11authtree = newtree234(x11_authcmp);
- ssh->v1_compressing = FALSE;
- ssh->v2_outgoing_sequence = 0;
- ssh->ssh1_rdpkt_crstate = 0;
- ssh->ssh2_rdpkt_crstate = 0;
- ssh->ssh2_bare_rdpkt_crstate = 0;
- ssh->rdpkt2_state.buf = NULL;
+ ssh->v2_cbc_ignore_workaround = FALSE;
+ ssh->bpp = NULL;
ssh->do_ssh1_connection_crstate = 0;
ssh->do_ssh_init_state = NULL;
ssh->do_ssh_connection_init_state = NULL;
@@ -11843,7 +10842,6 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->outgoing_data_sender.ctx = ssh;
ssh->outgoing_data_sender.queued = FALSE;
ssh->current_user_input_fn = NULL;
- ssh->pending_newkeys = FALSE;
ssh->rekey_reason = NULL;
ssh->rekey_class = RK_INITIAL;
ssh->v_c = NULL;
@@ -11934,37 +10932,10 @@ static void ssh_free(void *handle)
struct X11FakeAuth *auth;
int need_random_unref;
- if (ssh->v1_cipher_ctx)
- ssh->cipher->free_context(ssh->v1_cipher_ctx);
- if (ssh->cs_cipher_ctx)
- ssh->cscipher->free_context(ssh->cs_cipher_ctx);
- if (ssh->sc_cipher_ctx)
- ssh->sccipher->free_context(ssh->sc_cipher_ctx);
- if (ssh->cs_mac_ctx)
- ssh->csmac->free_context(ssh->cs_mac_ctx);
- if (ssh->sc_mac_ctx)
- ssh->scmac->free_context(ssh->sc_mac_ctx);
- if (ssh->cs_comp_ctx) {
- if (ssh->cscomp)
- ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx);
- else
- zlib_compress_cleanup(ssh->cs_comp_ctx);
- }
- if (ssh->sc_comp_ctx) {
- if (ssh->sccomp)
- ssh->sccomp->decompress_cleanup(ssh->sc_comp_ctx);
- else
- zlib_decompress_cleanup(ssh->sc_comp_ctx);
- }
if (ssh->kex_ctx)
dh_cleanup(ssh->kex_ctx);
sfree(ssh->savedhost);
- if (ssh->rdpkt2_state.buf) {
- smemclr(ssh->rdpkt2_state.buf, ssh->rdpkt2_state.bufsize);
- sfree(ssh->rdpkt2_state.buf);
- }
-
while (ssh->queuelen-- > 0)
ssh_free_pktout(ssh->queue[ssh->queuelen]);
sfree(ssh->queue);
@@ -12011,6 +10982,8 @@ static void ssh_free(void *handle)
x11_free_display(ssh->x11disp);
while ((auth = delpos234(ssh->x11authtree, 0)) != NULL)
x11_free_fake_auth(auth);
+ if (ssh->bpp)
+ ssh_bpp_free(ssh->bpp);
freetree234(ssh->x11authtree);
sfree(ssh->do_ssh_init_state);
sfree(ssh->do_ssh1_login_state);
@@ -12032,10 +11005,6 @@ static void ssh_free(void *handle)
sfree(ssh->fullhostname);
sfree(ssh->hostkey_str);
sfree(ssh->specials);
- if (ssh->crcda_ctx) {
- crcda_free_context(ssh->crcda_ctx);
- ssh->crcda_ctx = NULL;
- }
if (ssh->s)
ssh_do_close(ssh, TRUE);
expire_timer_context(ssh);
@@ -12353,9 +11322,9 @@ static void ssh_special(void *handle, Telnet_Special code)
send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
} else {
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
- pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
put_stringz(pktout, "");
- ssh2_pkt_send_noqueue(ssh, pktout);
+ ssh_pkt_write(ssh, pktout);
}
}
} else if (code == TS_REKEY) {
@@ -12457,7 +11426,7 @@ void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type,
{
PktOut *pkt;
- pkt = ssh2_pkt_init(type);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, type);
pkt->downstream_id = id;
pkt->additional_log_text = additional_log_text;
put_data(pkt, data, datalen);
diff --git a/ssh.h b/ssh.h
index 5e257324..f00c7e99 100644
--- a/ssh.h
+++ b/ssh.h
@@ -129,8 +129,21 @@ typedef enum {
SSH2_PKTCTX_KBDINTER
} Pkt_ACtx;
-PktOut *ssh1_pkt_init(int pkt_type);
-PktOut *ssh2_pkt_init(int pkt_type);
+typedef struct PacketLogSettings {
+ int omit_passwords, omit_data;
+ Pkt_KCtx kctx;
+ Pkt_ACtx actx;
+} PacketLogSettings;
+
+#define MAX_BLANKS 4 /* no packet needs more censored sections than this */
+int ssh1_censor_packet(
+ const PacketLogSettings *pls, int type, int sender_is_client,
+ ptrlen pkt, logblank_t *blanks);
+int ssh2_censor_packet(
+ const PacketLogSettings *pls, int type, int sender_is_client,
+ ptrlen pkt, logblank_t *blanks);
+
+PktOut *ssh_new_packet(void);
void ssh_unref_packet(PktIn *pkt);
void ssh_free_pktout(PktOut *pkt);
@@ -1106,6 +1119,13 @@ void platform_ssh_share_cleanup(const char *name);
#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65
#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66
+/* Virtual packet type, for packets too short to even have a type */
+#define SSH_MSG_NO_TYPE_CODE 0x100
+
+/* Given that virtual packet types exist, this is how big the dispatch
+ * table has to be */
+#define SSH_MAX_MSG 0x101
+
/*
* SSH-1 agent messages.
*/
diff --git a/ssh1bpp.c b/ssh1bpp.c
new file mode 100644
index 00000000..a00c4d3c
--- /dev/null
+++ b/ssh1bpp.c
@@ -0,0 +1,280 @@
+/*
+ * Binary packet protocol for SSH-1.
+ */
+
+#include
+
+#include "putty.h"
+#include "ssh.h"
+#include "sshbpp.h"
+#include "sshcr.h"
+
+struct ssh1_bpp_state {
+ int crState;
+ long len, pad, biglen, length, maxlen;
+ unsigned char *data;
+ unsigned long realcrc, gotcrc;
+ int chunk;
+ PktIn *pktin;
+
+ const struct ssh_cipher *cipher;
+ void *cipher_ctx;
+
+ void *crcda_ctx;
+
+ void *compctx, *decompctx;
+
+ BinaryPacketProtocol bpp;
+};
+
+static void ssh1_bpp_free(BinaryPacketProtocol *bpp);
+static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp);
+static PktOut *ssh1_bpp_new_pktout(int type);
+static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt);
+
+const struct BinaryPacketProtocolVtable ssh1_bpp_vtable = {
+ ssh1_bpp_free,
+ ssh1_bpp_handle_input,
+ ssh1_bpp_new_pktout,
+ ssh1_bpp_format_packet,
+};
+
+BinaryPacketProtocol *ssh1_bpp_new(void)
+{
+ struct ssh1_bpp_state *s = snew(struct ssh1_bpp_state);
+ memset(s, 0, sizeof(*s));
+ s->bpp.vt = &ssh1_bpp_vtable;
+ return &s->bpp;
+}
+
+static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
+{
+ struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+ if (s->cipher)
+ s->cipher->free_context(s->cipher_ctx);
+ if (s->compctx)
+ zlib_compress_cleanup(s->compctx);
+ if (s->decompctx)
+ zlib_decompress_cleanup(s->decompctx);
+ if (s->crcda_ctx)
+ crcda_free_context(s->crcda_ctx);
+ if (s->pktin)
+ ssh_unref_packet(s->pktin);
+ sfree(s);
+}
+
+void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
+ const struct ssh_cipher *cipher,
+ const void *session_key)
+{
+ struct ssh1_bpp_state *s;
+ assert(bpp->vt == &ssh1_bpp_vtable);
+ s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+
+ assert(!s->cipher);
+
+ s->cipher = cipher;
+ if (s->cipher) {
+ s->cipher_ctx = cipher->make_context();
+ cipher->sesskey(s->cipher_ctx, session_key);
+
+ assert(!s->crcda_ctx);
+ s->crcda_ctx = crcda_make_context();
+ }
+}
+
+void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp)
+{
+ struct ssh1_bpp_state *s;
+ assert(bpp->vt == &ssh1_bpp_vtable);
+ s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+
+ assert(!s->compctx);
+ assert(!s->decompctx);
+
+ s->compctx = zlib_compress_init();
+ s->decompctx = zlib_decompress_init();
+}
+
+static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
+{
+ struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+
+ crBegin(s->crState);
+
+ while (1) {
+ s->maxlen = 0;
+ s->length = 0;
+
+ {
+ unsigned char lenbuf[4];
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ bpp->in_raw, lenbuf, 4));
+ s->len = toint(GET_32BIT_MSB_FIRST(lenbuf));
+ }
+
+ if (s->len < 0 || s->len > 262144) { /* SSH1.5-mandated max size */
+ s->bpp.error = dupprintf(
+ "Extremely large packet length from server suggests"
+ " data stream corruption");
+ crStopV;
+ }
+
+ s->pad = 8 - (s->len % 8);
+ s->biglen = s->len + s->pad;
+ s->length = s->len - 5;
+
+ /*
+ * Allocate the packet to return, now we know its length.
+ */
+ s->pktin = snew_plus(PktIn, s->biglen);
+ s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+ s->pktin->refcount = 1;
+ s->pktin->type = 0;
+
+ s->maxlen = s->biglen;
+ s->data = snew_plus_get_aux(s->pktin);
+
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ bpp->in_raw, s->data, s->biglen));
+
+ if (s->cipher && detect_attack(s->crcda_ctx,
+ s->data, s->biglen, NULL)) {
+ s->bpp.error = dupprintf(
+ "Network attack (CRC compensation) detected!");
+ crStopV;
+ }
+
+ if (s->cipher)
+ s->cipher->decrypt(s->cipher_ctx, s->data, s->biglen);
+
+ s->realcrc = crc32_compute(s->data, s->biglen - 4);
+ s->gotcrc = GET_32BIT(s->data + s->biglen - 4);
+ if (s->gotcrc != s->realcrc) {
+ s->bpp.error = dupprintf(
+ "Incorrect CRC received on packet");
+ crStopV;
+ }
+
+ if (s->decompctx) {
+ unsigned char *decompblk;
+ int decomplen;
+ if (!zlib_decompress_block(s->decompctx,
+ s->data + s->pad, s->length + 1,
+ &decompblk, &decomplen)) {
+ s->bpp.error = dupprintf(
+ "Zlib decompression encountered invalid data");
+ crStopV;
+ }
+
+ if (s->maxlen < s->pad + decomplen) {
+ PktIn *old_pktin = s->pktin;
+
+ s->maxlen = s->pad + decomplen;
+ s->pktin = snew_plus(PktIn, s->maxlen);
+ *s->pktin = *old_pktin; /* structure copy */
+ s->data = snew_plus_get_aux(s->pktin);
+
+ smemclr(old_pktin, s->biglen);
+ sfree(old_pktin);
+ }
+
+ memcpy(s->data + s->pad, decompblk, decomplen);
+ sfree(decompblk);
+ s->length = decomplen - 1;
+ }
+
+ /*
+ * Now we can find the bounds of the semantic content of the
+ * packet, and the initial type byte.
+ */
+ s->data += s->pad;
+ s->pktin->type = *s->data++;
+ BinarySource_INIT(s->pktin, s->data, s->length);
+
+ if (s->bpp.logctx) {
+ logblank_t blanks[MAX_BLANKS];
+ int nblanks = ssh1_censor_packet(
+ s->bpp.pls, s->pktin->type, FALSE,
+ make_ptrlen(s->data, s->length), blanks);
+ log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
+ ssh1_pkt_type(s->pktin->type),
+ get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
+ NULL, 0, NULL);
+ }
+
+ pq_push(s->bpp.in_pq, s->pktin);
+
+ {
+ int type = s->pktin->type;
+ s->pktin = NULL;
+
+ if (type == SSH1_MSG_DISCONNECT)
+ s->bpp.seen_disconnect = TRUE;
+ }
+ }
+ crFinishV;
+}
+
+static PktOut *ssh1_bpp_new_pktout(int pkt_type)
+{
+ PktOut *pkt = ssh_new_packet();
+ pkt->length = 4 + 8; /* space for length + max padding */
+ put_byte(pkt, pkt_type);
+ pkt->prefix = pkt->length;
+ pkt->type = pkt_type;
+ pkt->downstream_id = 0;
+ pkt->additional_log_text = NULL;
+ return pkt;
+}
+
+static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
+{
+ struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+ int pad, biglen, i, pktoffs;
+ unsigned long crc;
+ int len;
+
+ if (s->bpp.logctx) {
+ ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix,
+ pkt->length - pkt->prefix);
+ logblank_t blanks[MAX_BLANKS];
+ int nblanks = ssh1_censor_packet(
+ s->bpp.pls, pkt->type, TRUE, pktdata, blanks);
+ log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
+ ssh1_pkt_type(pkt->type),
+ pktdata.ptr, pktdata.len, nblanks, blanks,
+ NULL, 0, NULL);
+ }
+
+ if (s->compctx) {
+ unsigned char *compblk;
+ int complen;
+ zlib_compress_block(s->compctx, pkt->data + 12, pkt->length - 12,
+ &compblk, &complen);
+ /* Replace the uncompressed packet data with the compressed
+ * version. */
+ pkt->length = 12;
+ put_data(pkt, compblk, complen);
+ sfree(compblk);
+ }
+
+ put_uint32(pkt, 0); /* space for CRC */
+ len = pkt->length - 4 - 8; /* len(type+data+CRC) */
+ pad = 8 - (len % 8);
+ pktoffs = 8 - pad;
+ biglen = len + pad; /* len(padding+type+data+CRC) */
+
+ for (i = pktoffs; i < 4+8; i++)
+ pkt->data[i] = random_byte();
+ crc = crc32_compute(pkt->data + pktoffs + 4,
+ biglen - 4); /* all ex len */
+ PUT_32BIT(pkt->data + pktoffs + 4 + biglen - 4, crc);
+ PUT_32BIT(pkt->data + pktoffs, len);
+
+ if (s->cipher)
+ s->cipher->encrypt(s->cipher_ctx, pkt->data + pktoffs + 4, biglen);
+
+ bufchain_add(s->bpp.out_raw, pkt->data + pktoffs,
+ biglen + 4); /* len(length+padding+type+data+CRC) */
+}
diff --git a/ssh1censor.c b/ssh1censor.c
new file mode 100644
index 00000000..755ce3d8
--- /dev/null
+++ b/ssh1censor.c
@@ -0,0 +1,76 @@
+/*
+ * Packet-censoring code for SSH-1, used to identify sensitive fields
+ * like passwords so that the logging system can avoid writing them
+ * into log files.
+ */
+
+#include
+
+#include "putty.h"
+#include "ssh.h"
+
+int ssh1_censor_packet(
+ const PacketLogSettings *pls, int type, int sender_is_client,
+ ptrlen pkt, logblank_t *blanks)
+{
+ int nblanks = 0;
+ ptrlen str;
+ BinarySource src[1];
+
+ BinarySource_BARE_INIT(src, pkt.ptr, pkt.len);
+
+ if (pls->omit_data &&
+ (type == SSH1_SMSG_STDOUT_DATA ||
+ type == SSH1_SMSG_STDERR_DATA ||
+ type == SSH1_CMSG_STDIN_DATA ||
+ type == SSH1_MSG_CHANNEL_DATA)) {
+ /* "Session data" packets - omit the data string. */
+ if (type == SSH1_MSG_CHANNEL_DATA)
+ get_uint32(src); /* skip channel id */
+ str = get_string(src);
+ if (!get_err(src)) {
+ assert(nblanks < MAX_BLANKS);
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_OMIT;
+ blanks[nblanks].len = str.len;
+ nblanks++;
+ }
+ }
+
+ if (sender_is_client && pls->omit_passwords) {
+ if (type == SSH1_CMSG_AUTH_PASSWORD ||
+ type == SSH1_CMSG_AUTH_TIS_RESPONSE ||
+ type == SSH1_CMSG_AUTH_CCARD_RESPONSE) {
+ /* If this is a password or similar packet, blank the
+ * password(s). */
+ assert(nblanks < MAX_BLANKS);
+ blanks[nblanks].offset = 0;
+ blanks[nblanks].len = pkt.len;
+ blanks[nblanks].type = PKTLOG_BLANK;
+ nblanks++;
+ } else if (type == SSH1_CMSG_X11_REQUEST_FORWARDING) {
+ /*
+ * If this is an X forwarding request packet, blank the
+ * fake auth data.
+ *
+ * Note that while we blank the X authentication data
+ * here, we don't take any special action to blank the
+ * start of an X11 channel, so using MIT-MAGIC-COOKIE-1
+ * and actually opening an X connection without having
+ * session blanking enabled is likely to leak your cookie
+ * into the log.
+ */
+ get_string(src); /* skip protocol name */
+ str = get_string(src);
+ if (!get_err(src)) {
+ assert(nblanks < MAX_BLANKS);
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_BLANK;
+ blanks[nblanks].len = str.len;
+ nblanks++;
+ }
+ }
+ }
+
+ return nblanks;
+}
diff --git a/ssh2bpp-bare.c b/ssh2bpp-bare.c
new file mode 100644
index 00000000..270c0118
--- /dev/null
+++ b/ssh2bpp-bare.c
@@ -0,0 +1,160 @@
+/*
+ * Trivial binary packet protocol for the 'bare' ssh-connection
+ * protocol used in PuTTY's SSH-2 connection sharing system.
+ */
+
+#include
+
+#include "putty.h"
+#include "ssh.h"
+#include "sshbpp.h"
+#include "sshcr.h"
+
+struct ssh2_bare_bpp_state {
+ int crState;
+ long packetlen, maxlen;
+ unsigned char *data;
+ unsigned long incoming_sequence, outgoing_sequence;
+ PktIn *pktin;
+
+ BinaryPacketProtocol bpp;
+};
+
+static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp);
+static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp);
+static PktOut *ssh2_bare_bpp_new_pktout(int type);
+static void ssh2_bare_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *);
+
+const struct BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = {
+ ssh2_bare_bpp_free,
+ ssh2_bare_bpp_handle_input,
+ ssh2_bare_bpp_new_pktout,
+ ssh2_bare_bpp_format_packet,
+};
+
+BinaryPacketProtocol *ssh2_bare_bpp_new(void)
+{
+ struct ssh2_bare_bpp_state *s = snew(struct ssh2_bare_bpp_state);
+ memset(s, 0, sizeof(*s));
+ s->bpp.vt = &ssh2_bare_bpp_vtable;
+ return &s->bpp;
+}
+
+static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp)
+{
+ struct ssh2_bare_bpp_state *s =
+ FROMFIELD(bpp, struct ssh2_bare_bpp_state, bpp);
+ if (s->pktin)
+ ssh_unref_packet(s->pktin);
+ sfree(s);
+}
+
+static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
+{
+ struct ssh2_bare_bpp_state *s =
+ FROMFIELD(bpp, struct ssh2_bare_bpp_state, bpp);
+
+ crBegin(s->crState);
+
+ while (1) {
+ /* Read the length field. */
+ {
+ unsigned char lenbuf[4];
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ s->bpp.in_raw, lenbuf, 4));
+ s->packetlen = toint(GET_32BIT_MSB_FIRST(lenbuf));
+ }
+
+ if (s->packetlen <= 0 || s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
+ s->bpp.error = dupstr("Invalid packet length received");
+ crStopV;
+ }
+
+ /*
+ * Allocate the packet to return, now we know its length.
+ */
+ s->pktin = snew_plus(PktIn, s->packetlen);
+ s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+ s->maxlen = 0;
+ s->pktin->refcount = 1;
+ s->data = snew_plus_get_aux(s->pktin);
+
+ s->pktin->encrypted_len = s->packetlen;
+
+ s->pktin->sequence = s->incoming_sequence++;
+
+ /*
+ * Read the remainder of the packet.
+ */
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ s->bpp.in_raw, s->data, s->packetlen));
+
+ /*
+ * The data we just read is precisely the initial type byte
+ * followed by the packet payload.
+ */
+ s->pktin->type = s->data[0];
+ s->data++;
+ s->packetlen--;
+ BinarySource_INIT(s->pktin, s->data, s->packetlen);
+
+ /*
+ * Log incoming packet, possibly omitting sensitive fields.
+ */
+ if (s->bpp.logctx) {
+ logblank_t blanks[MAX_BLANKS];
+ int nblanks = ssh2_censor_packet(
+ s->bpp.pls, s->pktin->type, FALSE,
+ make_ptrlen(s->data, s->packetlen), blanks);
+ log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
+ ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
+ s->pktin->type),
+ get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
+ &s->pktin->sequence, 0, NULL);
+ }
+
+ pq_push(s->bpp.in_pq, s->pktin);
+
+ {
+ int type = s->pktin->type;
+ s->pktin = NULL;
+
+ if (type == SSH2_MSG_DISCONNECT)
+ s->bpp.seen_disconnect = TRUE;
+ }
+ }
+ crFinishV;
+}
+
+static PktOut *ssh2_bare_bpp_new_pktout(int pkt_type)
+{
+ PktOut *pkt = ssh_new_packet();
+ pkt->length = 4; /* space for packet length */
+ pkt->type = pkt_type;
+ put_byte(pkt, pkt_type);
+ return pkt;
+}
+
+static void ssh2_bare_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
+{
+ struct ssh2_bare_bpp_state *s =
+ FROMFIELD(bpp, struct ssh2_bare_bpp_state, bpp);
+
+ if (s->bpp.logctx) {
+ ptrlen pktdata = make_ptrlen(pkt->data + 5, pkt->length - 5);
+ logblank_t blanks[MAX_BLANKS];
+ int nblanks = ssh2_censor_packet(
+ s->bpp.pls, pkt->type, TRUE, pktdata, blanks);
+ log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
+ ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
+ pkt->type),
+ pktdata.ptr, pktdata.len, nblanks, blanks,
+ &s->outgoing_sequence,
+ pkt->downstream_id, pkt->additional_log_text);
+ }
+
+ s->outgoing_sequence++; /* only for diagnostics, really */
+
+ PUT_32BIT(pkt->data, pkt->length - 4);
+ bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
+}
diff --git a/ssh2bpp.c b/ssh2bpp.c
new file mode 100644
index 00000000..da0ac900
--- /dev/null
+++ b/ssh2bpp.c
@@ -0,0 +1,613 @@
+/*
+ * Binary packet protocol for SSH-2.
+ */
+
+#include
+
+#include "putty.h"
+#include "ssh.h"
+#include "sshbpp.h"
+#include "sshcr.h"
+
+struct ssh2_bpp_direction {
+ unsigned long sequence;
+ const struct ssh2_cipher *cipher;
+ void *cipher_ctx;
+ const struct ssh_mac *mac;
+ int etm_mode;
+ void *mac_ctx;
+ const struct ssh_compress *comp;
+ void *comp_ctx;
+};
+
+struct ssh2_bpp_state {
+ int crState;
+ long len, pad, payload, packetlen, maclen, length, maxlen;
+ unsigned char *buf;
+ size_t bufsize;
+ unsigned char *data;
+ unsigned cipherblk;
+ PktIn *pktin;
+ BinarySink *sc_mac_bs;
+
+ struct ssh2_bpp_direction in, out;
+ int pending_newkeys;
+
+ BinaryPacketProtocol bpp;
+};
+
+static void ssh2_bpp_free(BinaryPacketProtocol *bpp);
+static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp);
+static PktOut *ssh2_bpp_new_pktout(int type);
+static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt);
+
+const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = {
+ ssh2_bpp_free,
+ ssh2_bpp_handle_input,
+ ssh2_bpp_new_pktout,
+ ssh2_bpp_format_packet,
+};
+
+BinaryPacketProtocol *ssh2_bpp_new(void)
+{
+ struct ssh2_bpp_state *s = snew(struct ssh2_bpp_state);
+ memset(s, 0, sizeof(*s));
+ s->bpp.vt = &ssh2_bpp_vtable;
+ return &s->bpp;
+}
+
+static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
+{
+ struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+ sfree(s->buf);
+ if (s->out.cipher_ctx)
+ s->out.cipher->free_context(s->out.cipher_ctx);
+ if (s->out.mac_ctx)
+ s->out.mac->free_context(s->out.mac_ctx);
+ if (s->out.comp_ctx)
+ s->out.comp->compress_cleanup(s->out.comp_ctx);
+ if (s->in.cipher_ctx)
+ s->in.cipher->free_context(s->in.cipher_ctx);
+ if (s->in.mac_ctx)
+ s->in.mac->free_context(s->in.mac_ctx);
+ if (s->in.comp_ctx)
+ s->in.comp->decompress_cleanup(s->in.comp_ctx);
+ if (s->pktin)
+ ssh_unref_packet(s->pktin);
+ sfree(s);
+}
+
+void ssh2_bpp_new_outgoing_crypto(
+ BinaryPacketProtocol *bpp,
+ const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+ const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+ const struct ssh_compress *compression)
+{
+ struct ssh2_bpp_state *s;
+ assert(bpp->vt == &ssh2_bpp_vtable);
+ s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+
+ if (s->out.cipher_ctx)
+ s->out.cipher->free_context(s->out.cipher_ctx);
+ if (s->out.mac_ctx)
+ s->out.mac->free_context(s->out.mac_ctx);
+ if (s->out.comp_ctx)
+ s->out.comp->compress_cleanup(s->out.comp_ctx);
+
+ s->out.cipher = cipher;
+ if (cipher) {
+ s->out.cipher_ctx = cipher->make_context();
+ cipher->setkey(s->out.cipher_ctx, ckey);
+ cipher->setiv(s->out.cipher_ctx, iv);
+ }
+ s->out.mac = mac;
+ s->out.etm_mode = etm_mode;
+ if (mac) {
+ s->out.mac_ctx = mac->make_context(s->out.cipher_ctx);
+ mac->setkey(s->out.mac_ctx, mac_key);
+ }
+
+ s->out.comp = compression;
+ /* out_comp is always non-NULL, because no compression is
+ * indicated by ssh_comp_none. So compress_init always exists, but
+ * it may return a null out_comp_ctx. */
+ s->out.comp_ctx = compression->compress_init();
+}
+
+void ssh2_bpp_new_incoming_crypto(
+ BinaryPacketProtocol *bpp,
+ const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+ const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+ const struct ssh_compress *compression)
+{
+ struct ssh2_bpp_state *s;
+ assert(bpp->vt == &ssh2_bpp_vtable);
+ s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+
+ if (s->in.cipher_ctx)
+ s->in.cipher->free_context(s->in.cipher_ctx);
+ if (s->in.mac_ctx)
+ s->in.mac->free_context(s->in.mac_ctx);
+ if (s->in.comp_ctx)
+ s->in.comp->decompress_cleanup(s->in.comp_ctx);
+
+ s->in.cipher = cipher;
+ if (cipher) {
+ s->in.cipher_ctx = cipher->make_context();
+ cipher->setkey(s->in.cipher_ctx, ckey);
+ cipher->setiv(s->in.cipher_ctx, iv);
+ }
+ s->in.mac = mac;
+ s->in.etm_mode = etm_mode;
+ if (mac) {
+ s->in.mac_ctx = mac->make_context(s->in.cipher_ctx);
+ mac->setkey(s->in.mac_ctx, mac_key);
+ }
+
+ s->in.comp = compression;
+ /* out_comp is always non-NULL, because no compression is
+ * indicated by ssh_comp_none. So compress_init always exists, but
+ * it may return a null out_comp_ctx. */
+ s->in.comp_ctx = compression->compress_init();
+
+ /* Clear the pending_newkeys flag, so that handle_input below will
+ * start consuming the input data again. */
+ s->pending_newkeys = FALSE;
+}
+
+static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
+{
+ struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+
+ crBegin(s->crState);
+
+ while (1) {
+ s->maxlen = 0;
+ s->length = 0;
+ if (s->in.cipher)
+ s->cipherblk = s->in.cipher->blksize;
+ else
+ s->cipherblk = 8;
+ if (s->cipherblk < 8)
+ s->cipherblk = 8;
+ s->maclen = s->in.mac ? s->in.mac->len : 0;
+
+ if (s->in.cipher && (s->in.cipher->flags & SSH_CIPHER_IS_CBC) &&
+ s->in.mac && !s->in.etm_mode) {
+ /*
+ * When dealing with a CBC-mode cipher, we want to avoid the
+ * possibility of an attacker's tweaking the ciphertext stream
+ * so as to cause us to feed the same block to the block
+ * cipher more than once and thus leak information
+ * (VU#958563). The way we do this is not to take any
+ * decisions on the basis of anything we've decrypted until
+ * we've verified it with a MAC. That includes the packet
+ * length, so we just read data and check the MAC repeatedly,
+ * and when the MAC passes, see if the length we've got is
+ * plausible.
+ *
+ * This defence is unnecessary in OpenSSH ETM mode, because
+ * the whole point of ETM mode is that the attacker can't
+ * tweak the ciphertext stream at all without the MAC
+ * detecting it before we decrypt anything.
+ */
+
+ /*
+ * Make sure we have buffer space for a maximum-size packet.
+ */
+ unsigned buflimit = OUR_V2_PACKETLIMIT + s->maclen;
+ if (s->bufsize < buflimit) {
+ s->bufsize = buflimit;
+ s->buf = sresize(s->buf, s->bufsize, unsigned char);
+ }
+
+ /* Read an amount corresponding to the MAC. */
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ s->bpp.in_raw, s->buf, s->maclen));
+
+ s->packetlen = 0;
+ s->in.mac->start(s->in.mac_ctx);
+ s->sc_mac_bs = s->in.mac->sink(s->in.mac_ctx);
+ put_uint32(s->sc_mac_bs, s->in.sequence);
+
+ for (;;) { /* Once around this loop per cipher block. */
+ /* Read another cipher-block's worth, and tack it on to
+ * the end. */
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ s->bpp.in_raw,
+ s->buf + (s->packetlen + s->maclen),
+ s->cipherblk));
+ /* Decrypt one more block (a little further back in
+ * the stream). */
+ s->in.cipher->decrypt(
+ s->in.cipher_ctx,
+ s->buf + s->packetlen, s->cipherblk);
+
+ /* Feed that block to the MAC. */
+ put_data(s->sc_mac_bs,
+ s->buf + s->packetlen, s->cipherblk);
+ s->packetlen += s->cipherblk;
+
+ /* See if that gives us a valid packet. */
+ if (s->in.mac->verresult(
+ s->in.mac_ctx, s->buf + s->packetlen) &&
+ ((s->len = toint(GET_32BIT(s->buf))) ==
+ s->packetlen-4))
+ break;
+ if (s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
+ s->bpp.error = dupprintf(
+ "No valid incoming packet found");
+ crStopV;
+ }
+ }
+ s->maxlen = s->packetlen + s->maclen;
+
+ /*
+ * Now transfer the data into an output packet.
+ */
+ s->pktin = snew_plus(PktIn, s->maxlen);
+ s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+ s->pktin->refcount = 1;
+ s->pktin->type = 0;
+ s->data = snew_plus_get_aux(s->pktin);
+ memcpy(s->data, s->buf, s->maxlen);
+ } else if (s->in.mac && s->in.etm_mode) {
+ if (s->bufsize < 4) {
+ s->bufsize = 4;
+ s->buf = sresize(s->buf, s->bufsize, unsigned char);
+ }
+
+ /*
+ * OpenSSH encrypt-then-MAC mode: the packet length is
+ * unencrypted, unless the cipher supports length encryption.
+ */
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ s->bpp.in_raw, s->buf, 4));
+
+ /* Cipher supports length decryption, so do it */
+ if (s->in.cipher &&
+ (s->in.cipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+ /* Keep the packet the same though, so the MAC passes */
+ unsigned char len[4];
+ memcpy(len, s->buf, 4);
+ s->in.cipher->decrypt_length(
+ s->in.cipher_ctx, len, 4, s->in.sequence);
+ s->len = toint(GET_32BIT(len));
+ } else {
+ s->len = toint(GET_32BIT(s->buf));
+ }
+
+ /*
+ * _Completely_ silly lengths should be stomped on before they
+ * do us any more damage.
+ */
+ if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
+ s->len % s->cipherblk != 0) {
+ s->bpp.error = dupprintf(
+ "Incoming packet length field was garbled");
+ crStopV;
+ }
+
+ /*
+ * So now we can work out the total packet length.
+ */
+ s->packetlen = s->len + 4;
+
+ /*
+ * Allocate the packet to return, now we know its length.
+ */
+ s->pktin = snew_plus(PktIn, OUR_V2_PACKETLIMIT + s->maclen);
+ s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+ s->pktin->refcount = 1;
+ s->pktin->type = 0;
+ s->data = snew_plus_get_aux(s->pktin);
+ memcpy(s->data, s->buf, 4);
+
+ /*
+ * Read the remainder of the packet.
+ */
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ s->bpp.in_raw, s->data + 4,
+ s->packetlen + s->maclen - 4));
+
+ /*
+ * Check the MAC.
+ */
+ if (s->in.mac && !s->in.mac->verify(
+ s->in.mac_ctx, s->data, s->len + 4, s->in.sequence)) {
+ s->bpp.error = dupprintf("Incorrect MAC received on packet");
+ crStopV;
+ }
+
+ /* Decrypt everything between the length field and the MAC. */
+ if (s->in.cipher)
+ s->in.cipher->decrypt(
+ s->in.cipher_ctx, s->data + 4, s->packetlen - 4);
+ } else {
+ if (s->bufsize < s->cipherblk) {
+ s->bufsize = s->cipherblk;
+ s->buf = sresize(s->buf, s->bufsize, unsigned char);
+ }
+
+ /*
+ * Acquire and decrypt the first block of the packet. This will
+ * contain the length and padding details.
+ */
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ s->bpp.in_raw, s->buf, s->cipherblk));
+
+ if (s->in.cipher)
+ s->in.cipher->decrypt(
+ s->in.cipher_ctx, s->buf, s->cipherblk);
+
+ /*
+ * Now get the length figure.
+ */
+ s->len = toint(GET_32BIT(s->buf));
+
+ /*
+ * _Completely_ silly lengths should be stomped on before they
+ * do us any more damage.
+ */
+ if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
+ (s->len + 4) % s->cipherblk != 0) {
+ s->bpp.error = dupprintf(
+ "Incoming packet was garbled on decryption");
+ crStopV;
+ }
+
+ /*
+ * So now we can work out the total packet length.
+ */
+ s->packetlen = s->len + 4;
+
+ /*
+ * Allocate the packet to return, now we know its length.
+ */
+ s->maxlen = s->packetlen + s->maclen;
+ s->pktin = snew_plus(PktIn, s->maxlen);
+ s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+ s->pktin->refcount = 1;
+ s->pktin->type = 0;
+ s->data = snew_plus_get_aux(s->pktin);
+ memcpy(s->data, s->buf, s->cipherblk);
+
+ /*
+ * Read and decrypt the remainder of the packet.
+ */
+ crMaybeWaitUntilV(bufchain_try_fetch_consume(
+ s->bpp.in_raw, s->data + s->cipherblk,
+ s->packetlen + s->maclen - s->cipherblk));
+
+ /* Decrypt everything _except_ the MAC. */
+ if (s->in.cipher)
+ s->in.cipher->decrypt(
+ s->in.cipher_ctx,
+ s->data + s->cipherblk, s->packetlen - s->cipherblk);
+
+ /*
+ * Check the MAC.
+ */
+ if (s->in.mac && !s->in.mac->verify(
+ s->in.mac_ctx, s->data, s->len + 4, s->in.sequence)) {
+ s->bpp.error = dupprintf("Incorrect MAC received on packet");
+ crStopV;
+ }
+ }
+ /* Get and sanity-check the amount of random padding. */
+ s->pad = s->data[4];
+ if (s->pad < 4 || s->len - s->pad < 1) {
+ s->bpp.error = dupprintf(
+ "Invalid padding length on received packet");
+ crStopV;
+ }
+ /*
+ * This enables us to deduce the payload length.
+ */
+ s->payload = s->len - s->pad - 1;
+
+ s->length = s->payload + 5;
+ s->pktin->encrypted_len = s->packetlen;
+
+ s->pktin->sequence = s->in.sequence++;
+
+ s->length = s->packetlen - s->pad;
+ assert(s->length >= 0);
+
+ /*
+ * Decompress packet payload.
+ */
+ {
+ unsigned char *newpayload;
+ int newlen;
+ if (s->in.comp && s->in.comp->decompress(
+ s->in.comp_ctx, s->data + 5, s->length - 5,
+ &newpayload, &newlen)) {
+ if (s->maxlen < newlen + 5) {
+ PktIn *old_pktin = s->pktin;
+
+ s->maxlen = newlen + 5;
+ s->pktin = snew_plus(PktIn, s->maxlen);
+ *s->pktin = *old_pktin; /* structure copy */
+ s->data = snew_plus_get_aux(s->pktin);
+
+ smemclr(old_pktin, s->packetlen + s->maclen);
+ sfree(old_pktin);
+ }
+ s->length = 5 + newlen;
+ memcpy(s->data + 5, newpayload, newlen);
+ sfree(newpayload);
+ }
+ }
+
+ /*
+ * Now we can identify the semantic content of the packet,
+ * and also the initial type byte.
+ */
+ if (s->length <= 5) { /* == 5 we hope, but robustness */
+ /*
+ * RFC 4253 doesn't explicitly say that completely empty
+ * packets with no type byte are forbidden. We handle them
+ * here by giving them a type code larger than 0xFF, which
+ * will be picked up at the next layer and trigger
+ * SSH_MSG_UNIMPLEMENTED.
+ */
+ s->pktin->type = SSH_MSG_NO_TYPE_CODE;
+ s->length = 0;
+ BinarySource_INIT(s->pktin, s->data + 5, 0);
+ } else {
+ s->pktin->type = s->data[5];
+ s->length -= 6;
+ BinarySource_INIT(s->pktin, s->data + 6, s->length);
+ }
+
+ if (s->bpp.logctx) {
+ logblank_t blanks[MAX_BLANKS];
+ int nblanks = ssh2_censor_packet(
+ s->bpp.pls, s->pktin->type, FALSE,
+ make_ptrlen(s->data, s->length), blanks);
+ log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
+ ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
+ s->pktin->type),
+ get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
+ &s->pktin->sequence, 0, NULL);
+ }
+
+ pq_push(s->bpp.in_pq, s->pktin);
+
+ {
+ int type = s->pktin->type;
+ s->pktin = NULL;
+
+ if (type == SSH2_MSG_DISCONNECT)
+ s->bpp.seen_disconnect = TRUE;
+ if (type == SSH2_MSG_NEWKEYS) {
+ /*
+ * Mild layer violation: in this situation we must
+ * suspend processing of the input byte stream until
+ * the transport layer has initialised the new keys by
+ * calling ssh2_bpp_new_incoming_crypto above.
+ */
+ s->pending_newkeys = TRUE;
+ crWaitUntilV(!s->pending_newkeys);
+ }
+ }
+ }
+ crFinishV;
+}
+
+int ssh2_bpp_temporarily_disable_compression(BinaryPacketProtocol *bpp)
+{
+ struct ssh2_bpp_state *s;
+ assert(bpp->vt == &ssh2_bpp_vtable);
+ s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+
+ if (!s->out.comp || !s->out.comp_ctx)
+ return 0;
+
+ return s->out.comp->disable_compression(s->out.comp_ctx);
+}
+
+static PktOut *ssh2_bpp_new_pktout(int pkt_type)
+{
+ PktOut *pkt = ssh_new_packet();
+ pkt->length = 5; /* space for packet length + padding length */
+ pkt->forcepad = 0;
+ pkt->type = pkt_type;
+ put_byte(pkt, pkt_type);
+ pkt->prefix = pkt->length;
+ return pkt;
+}
+
+static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
+{
+ struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+ int origlen, cipherblk, maclen, padding, unencrypted_prefix, i;
+
+ if (s->bpp.logctx) {
+ ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix,
+ pkt->length - pkt->prefix);
+ logblank_t blanks[MAX_BLANKS];
+ int nblanks = ssh2_censor_packet(
+ s->bpp.pls, pkt->type, TRUE, pktdata, blanks);
+ log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
+ ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
+ pkt->type),
+ pktdata.ptr, pktdata.len, nblanks, blanks, &s->out.sequence,
+ pkt->downstream_id, pkt->additional_log_text);
+ }
+
+ /*
+ * Compress packet payload.
+ */
+ {
+ unsigned char *newpayload;
+ int newlen;
+ if (s->out.comp && s->out.comp->compress(
+ s->out.comp_ctx, pkt->data + 5, pkt->length - 5,
+ &newpayload, &newlen)) {
+ pkt->length = 5;
+ put_data(pkt, newpayload, newlen);
+ sfree(newpayload);
+ }
+ }
+
+ /*
+ * Add padding. At least four bytes, and must also bring total
+ * length (minus MAC) up to a multiple of the block size.
+ * If pkt->forcepad is set, make sure the packet is at least that size
+ * after padding.
+ */
+ cipherblk = s->out.cipher ? s->out.cipher->blksize : 8;
+ cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
+ padding = 4;
+ unencrypted_prefix = (s->out.mac && s->out.etm_mode) ? 4 : 0;
+ if (pkt->length + padding < pkt->forcepad)
+ padding = pkt->forcepad - pkt->length;
+ padding +=
+ (cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
+ % cipherblk;
+ assert(padding <= 255);
+ maclen = s->out.mac ? s->out.mac->len : 0;
+ origlen = pkt->length;
+ for (i = 0; i < padding; i++)
+ put_byte(pkt, random_byte());
+ pkt->data[4] = padding;
+ PUT_32BIT(pkt->data, origlen + padding - 4);
+
+ /* Encrypt length if the scheme requires it */
+ if (s->out.cipher &&
+ (s->out.cipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+ s->out.cipher->encrypt_length(s->out.cipher_ctx, pkt->data, 4,
+ s->out.sequence);
+ }
+
+ put_padding(pkt, 0, maclen);
+
+ if (s->out.mac && s->out.etm_mode) {
+ /*
+ * OpenSSH-defined encrypt-then-MAC protocol.
+ */
+ if (s->out.cipher)
+ s->out.cipher->encrypt(s->out.cipher_ctx,
+ pkt->data + 4, origlen + padding - 4);
+ s->out.mac->generate(s->out.mac_ctx, pkt->data, origlen + padding,
+ s->out.sequence);
+ } else {
+ /*
+ * SSH-2 standard protocol.
+ */
+ if (s->out.mac)
+ s->out.mac->generate(
+ s->out.mac_ctx, pkt->data, origlen + padding,
+ s->out.sequence);
+ if (s->out.cipher)
+ s->out.cipher->encrypt(s->out.cipher_ctx,
+ pkt->data, origlen + padding);
+ }
+
+ s->out.sequence++; /* whether or not we MACed */
+ pkt->encrypted_len = origlen + padding;
+
+ bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
+}
diff --git a/ssh2censor.c b/ssh2censor.c
new file mode 100644
index 00000000..3d13589d
--- /dev/null
+++ b/ssh2censor.c
@@ -0,0 +1,107 @@
+/*
+ * Packet-censoring code for SSH-2, used to identify sensitive fields
+ * like passwords so that the logging system can avoid writing them
+ * into log files.
+ */
+
+#include
+
+#include "putty.h"
+#include "ssh.h"
+
+int ssh2_censor_packet(
+ const PacketLogSettings *pls, int type, int sender_is_client,
+ ptrlen pkt, logblank_t *blanks)
+{
+ int nblanks = 0;
+ ptrlen str;
+ BinarySource src[1];
+
+ BinarySource_BARE_INIT(src, pkt.ptr, pkt.len);
+
+ if (pls->omit_data &&
+ (type == SSH2_MSG_CHANNEL_DATA ||
+ type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) {
+ /* "Session data" packets - omit the data string. */
+ get_uint32(src); /* skip channel id */
+ if (type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
+ get_uint32(src); /* skip extended data type */
+ str = get_string(src);
+ if (!get_err(src)) {
+ assert(nblanks < MAX_BLANKS);
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_OMIT;
+ blanks[nblanks].len = str.len;
+ nblanks++;
+ }
+ }
+
+ if (sender_is_client && pls->omit_passwords) {
+ if (type == SSH2_MSG_USERAUTH_REQUEST) {
+ /* If this is a password packet, blank the password(s). */
+ get_string(src); /* username */
+ get_string(src); /* service name */
+ str = get_string(src); /* auth method */
+ if (ptrlen_eq_string(str, "password")) {
+ get_bool(src);
+ /* Blank the password field. */
+ str = get_string(src);
+ if (!get_err(src)) {
+ assert(nblanks < MAX_BLANKS);
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_BLANK;
+ blanks[nblanks].len = str.len;
+ nblanks++;
+ /* If there's another password field beyond it
+ * (change of password), blank that too. */
+ str = get_string(src);
+ if (!get_err(src))
+ blanks[nblanks-1].len =
+ src->pos - blanks[nblanks].offset;
+ }
+ }
+ } else if (pls->actx == SSH2_PKTCTX_KBDINTER &&
+ type == SSH2_MSG_USERAUTH_INFO_RESPONSE) {
+ /* If this is a keyboard-interactive response packet,
+ * blank the responses. */
+ get_uint32(src);
+ assert(nblanks < MAX_BLANKS);
+ blanks[nblanks].offset = src->pos;
+ blanks[nblanks].type = PKTLOG_BLANK;
+ do {
+ str = get_string(src);
+ } while (!get_err(src));
+ blanks[nblanks].len = src->pos - blanks[nblanks].offset;
+ nblanks++;
+ } else if (type == SSH2_MSG_CHANNEL_REQUEST) {
+ /*
+ * If this is an X forwarding request packet, blank the
+ * fake auth data.
+ *
+ * Note that while we blank the X authentication data
+ * here, we don't take any special action to blank the
+ * start of an X11 channel, so using MIT-MAGIC-COOKIE-1
+ * and actually opening an X connection without having
+ * session blanking enabled is likely to leak your cookie
+ * into the log.
+ */
+ get_uint32(src);
+ str = get_string(src);
+ if (ptrlen_eq_string(str, "x11-req")) {
+ get_bool(src);
+ get_bool(src);
+ get_string(src);
+ str = get_string(src);
+ if (!get_err(src)) {
+ assert(nblanks < MAX_BLANKS);
+ blanks[nblanks].offset = src->pos - str.len;
+ blanks[nblanks].type = PKTLOG_BLANK;
+ blanks[nblanks].len = str.len;
+ nblanks++;
+ }
+ }
+ }
+ }
+
+ return nblanks;
+}
diff --git a/sshbpp.h b/sshbpp.h
new file mode 100644
index 00000000..cf1a528d
--- /dev/null
+++ b/sshbpp.h
@@ -0,0 +1,54 @@
+/*
+ * Abstraction of the binary packet protocols used in SSH.
+ */
+
+#ifndef PUTTY_SSHBPP_H
+#define PUTTY_SSHBPP_H
+
+typedef struct BinaryPacketProtocol BinaryPacketProtocol;
+
+struct BinaryPacketProtocolVtable {
+ void (*free)(BinaryPacketProtocol *);
+ void (*handle_input)(BinaryPacketProtocol *);
+ PktOut *(*new_pktout)(int type);
+ void (*format_packet)(BinaryPacketProtocol *, PktOut *);
+};
+
+struct BinaryPacketProtocol {
+ const struct BinaryPacketProtocolVtable *vt;
+ bufchain *in_raw, *out_raw;
+ PacketQueue *in_pq;
+ PacketLogSettings *pls;
+ void *logctx;
+
+ int seen_disconnect;
+ char *error;
+};
+
+#define ssh_bpp_free(bpp) ((bpp)->vt->free(bpp))
+#define ssh_bpp_handle_input(bpp) ((bpp)->vt->handle_input(bpp))
+#define ssh_bpp_new_pktout(bpp, type) ((bpp)->vt->new_pktout(type))
+#define ssh_bpp_format_packet(bpp, pkt) ((bpp)->vt->format_packet(bpp, pkt))
+
+BinaryPacketProtocol *ssh1_bpp_new(void);
+void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
+ const struct ssh_cipher *cipher,
+ const void *session_key);
+void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
+
+BinaryPacketProtocol *ssh2_bpp_new(void);
+void ssh2_bpp_new_outgoing_crypto(
+ BinaryPacketProtocol *bpp,
+ const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+ const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+ const struct ssh_compress *compression);
+void ssh2_bpp_new_incoming_crypto(
+ BinaryPacketProtocol *bpp,
+ const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+ const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+ const struct ssh_compress *compression);
+int ssh2_bpp_temporarily_disable_compression(BinaryPacketProtocol *bpp);
+
+BinaryPacketProtocol *ssh2_bare_bpp_new(void);
+
+#endif /* PUTTY_SSHBPP_H */
From 93afcf02af7a68d736b487d9665aca741a3d5e0b Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 9 Jun 2018 09:59:58 +0100
Subject: [PATCH 373/607] Remove the SSH-1 variadic send_packet() system.
Now we have the new marshalling system, I think it's outlived its
usefulness, because the new system allows us to directly express
various things (e.g. uint16 and non-zero-terminated strings) that were
actually _more_ awkward to do via the variadic interface. So here's a
rewrite that removes send_packet(), and replaces all its call sites
with something that matches our SSH-2 packet construction idioms.
This diff actually _reduces_ the number of lines of code in ssh.c.
Since the variadic system was trying to save code by centralising
things, that seems like the best possible evidence that it wasn't
pulling its weight!
---
ssh.c | 340 ++++++++++++++++++++++++++--------------------------------
1 file changed, 153 insertions(+), 187 deletions(-)
diff --git a/ssh.c b/ssh.c
index 919b2321..9f8d0314 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1203,53 +1203,6 @@ static int s_write(Ssh ssh, const void *data, int len)
return sk_write(ssh->s, data, len);
}
-/*
- * Construct a SSH-1 packet with the specified contents.
- * (This all-at-once interface used to be the only one, but now SSH-1
- * packets can also be constructed incrementally.)
- */
-static PktOut *construct_packet(Ssh ssh, int pkttype, va_list ap)
-{
- int argtype;
- Bignum bn;
- PktOut *pkt;
-
- pkt = ssh_bpp_new_pktout(ssh->bpp, pkttype);
-
- while ((argtype = va_arg(ap, int)) != PKT_END) {
- unsigned char *argp, argchar;
- char *sargp;
- unsigned long argint;
- int arglen;
- switch (argtype) {
- /* Actual fields in the packet */
- case PKT_INT:
- argint = va_arg(ap, int);
- put_uint32(pkt, argint);
- break;
- case PKT_CHAR:
- argchar = (unsigned char) va_arg(ap, int);
- put_byte(pkt, argchar);
- break;
- case PKT_DATA:
- argp = va_arg(ap, unsigned char *);
- arglen = va_arg(ap, int);
- put_data(pkt, argp, arglen);
- break;
- case PKT_STR:
- sargp = va_arg(ap, char *);
- put_stringz(pkt, sargp);
- break;
- case PKT_BIGNUM:
- bn = va_arg(ap, Bignum);
- put_mp_ssh1(pkt, bn);
- break;
- }
- }
-
- return pkt;
-}
-
static void ssh_pkt_write(Ssh ssh, PktOut *pkt)
{
if (ssh->version == 2 && ssh->v2_cbc_ignore_workaround &&
@@ -1270,16 +1223,6 @@ static void ssh_pkt_write(Ssh ssh, PktOut *pkt)
queue_idempotent_callback(&ssh->outgoing_data_sender);
}
-static void send_packet(Ssh ssh, int pkttype, ...)
-{
- PktOut *pkt;
- va_list ap;
- va_start(ap, pkttype);
- pkt = construct_packet(ssh, pkttype, ap);
- va_end(ap);
- ssh_pkt_write(ssh, pkt);
-}
-
static int ssh_versioncmp(const char *a, const char *b)
{
char *ae, *be;
@@ -2773,8 +2716,9 @@ static void ssh_disconnect(Ssh ssh, const char *client_reason,
error = dupstr("Disconnected");
if (wire_reason) {
if (ssh->version == 1) {
- send_packet(ssh, SSH1_MSG_DISCONNECT, PKT_STR, wire_reason,
- PKT_END);
+ PktOut *pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_DISCONNECT);
+ put_stringz(pktout, wire_reason);
+ ssh_pkt_write(ssh, pktout);
} else if (ssh->version == 2) {
PktOut *pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_DISCONNECT);
put_uint32(pktout, code);
@@ -2853,6 +2797,7 @@ static void do_ssh1_login(void *vctx)
{
Ssh ssh = (Ssh)vctx;
PktIn *pktin;
+ PktOut *pkt;
int i, j, ret;
ptrlen pl;
@@ -3096,12 +3041,13 @@ static void do_ssh1_login(void *vctx)
break;
}
- send_packet(ssh, SSH1_CMSG_SESSION_KEY,
- PKT_CHAR, s->cipher_type,
- PKT_DATA, s->cookie, 8,
- PKT_CHAR, (s->len * 8) >> 8, PKT_CHAR, (s->len * 8) & 0xFF,
- PKT_DATA, s->rsabuf, s->len,
- PKT_INT, ssh->v1_local_protoflags, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_SESSION_KEY);
+ put_byte(pkt, s->cipher_type);
+ put_data(pkt, s->cookie, 8);
+ put_uint16(pkt, s->len * 8);
+ put_data(pkt, s->rsabuf, s->len);
+ put_uint32(pkt, ssh->v1_local_protoflags);
+ ssh_pkt_write(ssh, pkt);
logevent("Trying to enable encryption...");
@@ -3173,7 +3119,10 @@ static void do_ssh1_login(void *vctx)
free_prompts(s->cur_prompt);
}
- send_packet(ssh, SSH1_CMSG_USER, PKT_STR, ssh->username, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_USER);
+ put_stringz(pkt, ssh->username);
+ ssh_pkt_write(ssh, pkt);
+
{
char *userlog = dupprintf("Sent username \"%s\"", ssh->username);
logevent(userlog);
@@ -3305,8 +3254,9 @@ static void do_ssh1_login(void *vctx)
continue;
}
logeventf(ssh, "Trying Pageant key #%d", s->keyi);
- send_packet(ssh, SSH1_CMSG_AUTH_RSA,
- PKT_BIGNUM, s->key.modulus, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AUTH_RSA);
+ put_mp_ssh1(pkt, s->key.modulus);
+ ssh_pkt_write(ssh, pkt);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login))
!= NULL);
if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
@@ -3351,9 +3301,10 @@ static void do_ssh1_login(void *vctx)
if (ret) {
if (ret[4] == SSH1_AGENT_RSA_RESPONSE) {
logevent("Sending Pageant's response");
- send_packet(ssh, SSH1_CMSG_AUTH_RSA_RESPONSE,
- PKT_DATA, ret + 5, 16,
- PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp,
+ SSH1_CMSG_AUTH_RSA_RESPONSE);
+ put_data(pkt, ret + 5, 16);
+ ssh_pkt_write(ssh, pkt);
sfree(ret);
crMaybeWaitUntilV(
(pktin = pq_pop(&ssh->pq_ssh1_login))
@@ -3487,8 +3438,9 @@ static void do_ssh1_login(void *vctx)
/*
* Send a public key attempt.
*/
- send_packet(ssh, SSH1_CMSG_AUTH_RSA,
- PKT_BIGNUM, s->key.modulus, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AUTH_RSA);
+ put_mp_ssh1(pkt, s->key.modulus);
+ ssh_pkt_write(ssh, pkt);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login))
!= NULL);
@@ -3524,8 +3476,10 @@ static void do_ssh1_login(void *vctx)
put_data(&md5c, s->session_id, 16);
MD5Final(buffer, &md5c);
- send_packet(ssh, SSH1_CMSG_AUTH_RSA_RESPONSE,
- PKT_DATA, buffer, 16, PKT_END);
+ pkt = ssh_bpp_new_pktout(
+ ssh->bpp, SSH1_CMSG_AUTH_RSA_RESPONSE);
+ put_data(pkt, buffer, 16);
+ ssh_pkt_write(ssh, pkt);
freebn(challenge);
freebn(response);
@@ -3558,7 +3512,8 @@ static void do_ssh1_login(void *vctx)
!s->tis_auth_refused) {
s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
logevent("Requested TIS authentication");
- send_packet(ssh, SSH1_CMSG_AUTH_TIS, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AUTH_TIS);
+ ssh_pkt_write(ssh, pkt);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
if (pktin->type != SSH1_SMSG_AUTH_TIS_CHALLENGE) {
logevent("TIS authentication declined");
@@ -3600,7 +3555,8 @@ static void do_ssh1_login(void *vctx)
!s->ccard_auth_refused) {
s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
logevent("Requested CryptoCard authentication");
- send_packet(ssh, SSH1_CMSG_AUTH_CCARD, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AUTH_CCARD);
+ ssh_pkt_write(ssh, pkt);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
if (pktin->type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
logevent("CryptoCard authentication declined");
@@ -3724,7 +3680,6 @@ static void do_ssh1_login(void *vctx)
* we can use the primary defence.
*/
int bottom, top, pwlen, i;
- char *randomstr;
pwlen = strlen(s->cur_prompt->prompts[0]->result);
if (pwlen < 16) {
@@ -3737,26 +3692,22 @@ static void do_ssh1_login(void *vctx)
assert(pwlen >= bottom && pwlen <= top);
- randomstr = snewn(top + 1, char);
-
for (i = bottom; i <= top; i++) {
if (i == pwlen) {
- send_packet(ssh, s->pwpkt_type,
- PKT_STR, s->cur_prompt->prompts[0]->result,
- PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, s->pwpkt_type);
+ put_stringz(pkt, s->cur_prompt->prompts[0]->result);
+ ssh_pkt_write(ssh, pkt);
} else {
- for (j = 0; j < i; j++) {
- do {
- randomstr[j] = random_byte();
- } while (randomstr[j] == '\0');
- }
- randomstr[i] = '\0';
- send_packet(ssh, SSH1_MSG_IGNORE,
- PKT_STR, randomstr, PKT_END);
+ strbuf *random_data = strbuf_new();
+ for (j = 0; j < i; j++)
+ put_byte(random_data, random_byte());
+
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_IGNORE);
+ put_stringsb(pkt, random_data);
+ ssh_pkt_write(ssh, pkt);
}
}
logevent("Sending password with camouflage packets");
- sfree(randomstr);
}
else if (!(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
/*
@@ -3764,42 +3715,30 @@ static void do_ssh1_login(void *vctx)
* but can deal with padded passwords, so we
* can use the secondary defence.
*/
- char string[64];
- char *ss;
- int len;
-
- len = strlen(s->cur_prompt->prompts[0]->result);
- if (len < sizeof(string)) {
- ss = string;
- strcpy(string, s->cur_prompt->prompts[0]->result);
- len++; /* cover the zero byte */
- while (len < sizeof(string)) {
- string[len++] = (char) random_byte();
- }
- } else {
- ss = s->cur_prompt->prompts[0]->result;
- }
+ strbuf *padded_pw = strbuf_new();
+
logevent("Sending length-padded password");
- send_packet(ssh, s->pwpkt_type,
- PKT_INT, len, PKT_DATA, ss, len,
- PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, s->pwpkt_type);
+ put_asciz(padded_pw, s->cur_prompt->prompts[0]->result);
+ do {
+ put_byte(padded_pw, random_byte());
+ } while (padded_pw->len % 64 != 0);
+ put_stringsb(pkt, padded_pw);
+ ssh_pkt_write(ssh, pkt);
} else {
/*
* The server is believed unable to cope with
* any of our password camouflage methods.
*/
- int len;
- len = strlen(s->cur_prompt->prompts[0]->result);
logevent("Sending unpadded password");
- send_packet(ssh, s->pwpkt_type,
- PKT_INT, len,
- PKT_DATA, s->cur_prompt->prompts[0]->result, len,
- PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, s->pwpkt_type);
+ put_stringz(pkt, s->cur_prompt->prompts[0]->result);
+ ssh_pkt_write(ssh, pkt);
}
} else {
- send_packet(ssh, s->pwpkt_type,
- PKT_STR, s->cur_prompt->prompts[0]->result,
- PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, s->pwpkt_type);
+ put_stringz(pkt, s->cur_prompt->prompts[0]->result);
+ ssh_pkt_write(ssh, pkt);
}
logevent("Sent password");
free_prompts(s->cur_prompt);
@@ -3838,6 +3777,7 @@ static void do_ssh1_login(void *vctx)
static void ssh_channel_try_eof(struct ssh_channel *c)
{
Ssh ssh = c->ssh;
+ PktOut *pktout;
assert(c->pending_eof); /* precondition for calling us */
if (c->halfopen)
return; /* can't close: not even opened yet */
@@ -3846,11 +3786,11 @@ static void ssh_channel_try_eof(struct ssh_channel *c)
c->pending_eof = FALSE; /* we're about to send it */
if (ssh->version == 1) {
- send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
- PKT_END);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_CLOSE);
+ put_uint32(pktout, c->remoteid);
+ ssh_pkt_write(ssh, pktout);
c->closes |= CLOSES_SENT_EOF;
} else {
- PktOut *pktout;
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_EOF);
put_uint32(pktout, c->remoteid);
ssh2_pkt_send(ssh, pktout);
@@ -4339,6 +4279,8 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
epf->daddr, epf->dport);
sfree(pf);
} else {
+ PktOut *pktout;
+
logeventf(ssh, "Requesting remote port %s"
" forward to %s", sportdesc, dportdesc);
@@ -4348,16 +4290,16 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
pf->pfrec = epf;
if (ssh->version == 1) {
- send_packet(ssh, SSH1_CMSG_PORT_FORWARD_REQUEST,
- PKT_INT, epf->sport,
- PKT_STR, epf->daddr,
- PKT_INT, epf->dport,
- PKT_END);
+ pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH1_CMSG_PORT_FORWARD_REQUEST);
+ put_uint32(pktout, epf->sport);
+ put_stringz(pktout, epf->daddr);
+ put_uint32(pktout, epf->dport);
+ ssh_pkt_write(ssh, pktout);
ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS,
SSH1_SMSG_FAILURE,
ssh_rportfwd_succfail, pf);
} else {
- PktOut *pktout;
pktout = ssh_bpp_new_pktout(
ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
put_stringz(pktout, "tcpip-forward");
@@ -4401,13 +4343,15 @@ static void ssh1_smsg_x11_open(Ssh ssh, PktIn *pktin)
/* Remote side is trying to open a channel to talk to our
* X-Server. Give them back a local channel number. */
struct ssh_channel *c;
+ PktOut *pkt;
int remoteid = get_uint32(pktin);
logevent("Received X11 connect request");
/* Refuse if X11 forwarding is disabled. */
if (!ssh->X11_fwd_enabled) {
- send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
- PKT_INT, remoteid, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
+ put_uint32(pkt, remoteid);
+ ssh_pkt_write(ssh, pkt);
logevent("Rejected X11 connect request");
} else {
c = snew(struct ssh_channel);
@@ -4418,9 +4362,10 @@ static void ssh1_smsg_x11_open(Ssh ssh, PktIn *pktin)
c->remoteid = remoteid;
c->halfopen = FALSE;
c->type = CHAN_X11; /* identify channel type */
- send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
- PKT_INT, c->remoteid, PKT_INT,
- c->localid, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
+ put_uint32(pkt, c->remoteid);
+ put_uint32(pkt, c->localid);
+ ssh_pkt_write(ssh, pkt);
logevent("Opened X11 forward channel");
}
}
@@ -4430,12 +4375,14 @@ static void ssh1_smsg_agent_open(Ssh ssh, PktIn *pktin)
/* Remote side is trying to open a channel to talk to our
* agent. Give them back a local channel number. */
struct ssh_channel *c;
+ PktOut *pkt;
int remoteid = toint(get_uint32(pktin));
/* Refuse if agent forwarding is disabled. */
if (!ssh->agentfwd_enabled) {
- send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
- PKT_INT, remoteid, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
+ put_uint32(pkt, remoteid);
+ ssh_pkt_write(ssh, pkt);
} else {
c = snew(struct ssh_channel);
c->ssh = ssh;
@@ -4445,9 +4392,10 @@ static void ssh1_smsg_agent_open(Ssh ssh, PktIn *pktin)
c->type = CHAN_AGENT; /* identify channel type */
c->u.a.pending = NULL;
bufchain_init(&c->u.a.inbuffer);
- send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
- PKT_INT, c->remoteid, PKT_INT, c->localid,
- PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
+ put_uint32(pkt, c->remoteid);
+ put_uint32(pkt, c->localid);
+ ssh_pkt_write(ssh, pkt);
}
}
@@ -4456,6 +4404,7 @@ static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
/* Remote side is trying to open a channel to talk to a
* forwarded port. Give them back a local channel number. */
struct ssh_rportfwd pf, *pfp;
+ PktOut *pkt;
int remoteid;
int port;
ptrlen host;
@@ -4472,8 +4421,9 @@ static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
if (pfp == NULL) {
logeventf(ssh, "Rejected remote port open request for %s:%d",
pf.dhost, port);
- send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
- PKT_INT, remoteid, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
+ put_uint32(pkt, remoteid);
+ ssh_pkt_write(ssh, pkt);
} else {
struct ssh_channel *c = snew(struct ssh_channel);
c->ssh = ssh;
@@ -4486,16 +4436,19 @@ static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
logeventf(ssh, "Port open failed: %s", err);
sfree(err);
sfree(c);
- send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
- PKT_INT, remoteid, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
+ put_uint32(pkt, remoteid);
+ ssh_pkt_write(ssh, pkt);
} else {
ssh_channel_init(c);
c->remoteid = remoteid;
c->halfopen = FALSE;
c->type = CHAN_SOCKDATA; /* identify channel type */
- send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
- PKT_INT, c->remoteid, PKT_INT,
- c->localid, PKT_END);
+ pkt = ssh_bpp_new_pktout(
+ ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
+ put_uint32(pkt, c->remoteid);
+ put_uint32(pkt, c->localid);
+ ssh_pkt_write(ssh, pkt);
logevent("Forwarded port opened successfully");
}
}
@@ -4569,8 +4522,10 @@ static void ssh1_msg_channel_close(Ssh ssh, PktIn *pktin)
if (!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) &&
!(c->closes & CLOSES_SENT_CLOSE)) {
- send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION,
- PKT_INT, c->remoteid, PKT_END);
+ PktOut *pkt = ssh_bpp_new_pktout(
+ ssh->bpp, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION);
+ put_uint32(pkt, c->remoteid);
+ ssh_pkt_write(ssh, pkt);
c->closes |= CLOSES_SENT_CLOSE;
}
@@ -4635,9 +4590,11 @@ static void ssh1_msg_channel_data(Ssh ssh, PktIn *pktin)
static void ssh1_smsg_exit_status(Ssh ssh, PktIn *pktin)
{
+ PktOut *pkt;
ssh->exitcode = get_uint32(pktin);
logeventf(ssh, "Server sent command exit status %d", ssh->exitcode);
- send_packet(ssh, SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_EXIT_CONFIRMATION);
+ ssh_pkt_write(ssh, pkt);
/*
* In case `helpful' firewalls or proxies tack
* extra human-readable text on the end of the
@@ -4673,6 +4630,7 @@ static void do_ssh1_connection(void *vctx)
{
Ssh ssh = (Ssh)vctx;
PktIn *pktin;
+ PktOut *pkt;
crBegin(ssh->do_ssh1_connection_crstate);
@@ -4692,7 +4650,8 @@ static void do_ssh1_connection(void *vctx)
if (ssh_agent_forwarding_permitted(ssh)) {
logevent("Requesting agent forwarding");
- send_packet(ssh, SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AGENT_REQUEST_FORWARDING);
+ ssh_pkt_write(ssh, pkt);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection)) != NULL);
if (pktin->type != SSH1_SMSG_SUCCESS
&& pktin->type != SSH1_SMSG_FAILURE) {
@@ -4721,18 +4680,13 @@ static void do_ssh1_connection(void *vctx)
ssh->x11auth->disp = ssh->x11disp;
logevent("Requesting X11 forwarding");
- if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
- send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
- PKT_STR, ssh->x11auth->protoname,
- PKT_STR, ssh->x11auth->datastring,
- PKT_INT, ssh->x11disp->screennum,
- PKT_END);
- } else {
- send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
- PKT_STR, ssh->x11auth->protoname,
- PKT_STR, ssh->x11auth->datastring,
- PKT_END);
- }
+ pkt = ssh_bpp_new_pktout(
+ ssh->bpp, SSH1_CMSG_X11_REQUEST_FORWARDING);
+ put_stringz(pkt, ssh->x11auth->protoname);
+ put_stringz(pkt, ssh->x11auth->datastring);
+ if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER)
+ put_uint32(pkt, ssh->x11disp->screennum);
+ ssh_pkt_write(ssh, pkt);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection))
!= NULL);
if (pktin->type != SSH1_SMSG_SUCCESS
@@ -4791,7 +4745,9 @@ static void do_ssh1_connection(void *vctx)
}
if (conf_get_int(ssh->conf, CONF_compression)) {
- send_packet(ssh, SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_REQUEST_COMPRESSION);
+ put_uint32(pkt, 6); /* gzip compression level */
+ ssh_pkt_write(ssh, pkt);
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection)) != NULL);
if (pktin->type != SSH1_SMSG_SUCCESS
&& pktin->type != SSH1_SMSG_FAILURE) {
@@ -4819,10 +4775,14 @@ static void do_ssh1_connection(void *vctx)
cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
ssh->fallback_cmd = TRUE;
}
- if (*cmd)
- send_packet(ssh, SSH1_CMSG_EXEC_CMD, PKT_STR, cmd, PKT_END);
- else
- send_packet(ssh, SSH1_CMSG_EXEC_SHELL, PKT_END);
+ if (*cmd) {
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_EXEC_CMD);
+ put_stringz(pkt, cmd);
+ ssh_pkt_write(ssh, pkt);
+ } else {
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_EXEC_SHELL);
+ ssh_pkt_write(ssh, pkt);
+ }
logevent("Started session");
}
@@ -4861,9 +4821,9 @@ static void do_ssh1_connection(void *vctx)
bufchain_prefix(&ssh->user_input, &data, &len);
if (len > 512)
len = 512;
- send_packet(ssh, SSH1_CMSG_STDIN_DATA,
- PKT_INT, len, PKT_DATA, data, len,
- PKT_END);
+ pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_STDIN_DATA);
+ put_string(pkt, data, len);
+ ssh_pkt_write(ssh, pkt);
bufchain_consume(&ssh->user_input, len);
}
crReturnV;
@@ -7037,11 +6997,10 @@ static int ssh_send_channel_data(struct ssh_channel *c, const char *buf,
bufchain_add(&c->v.v2.outbuffer, buf, len);
return ssh2_try_send(c);
} else {
- send_packet(c->ssh, SSH1_MSG_CHANNEL_DATA,
- PKT_INT, c->remoteid,
- PKT_INT, len,
- PKT_DATA, buf, len,
- PKT_END);
+ PktOut *pkt = ssh_bpp_new_pktout(c->ssh->bpp, SSH1_MSG_CHANNEL_DATA);
+ put_uint32(pkt, c->remoteid);
+ put_string(pkt, buf, len);
+ ssh_pkt_write(c->ssh, pkt);
/*
* In SSH-1 we can return 0 here - implying that channels are
* never individually throttled - because the only
@@ -11164,10 +11123,12 @@ static void ssh_size(void *handle, int width, int height)
case SSH_STATE_SESSION:
if (!conf_get_int(ssh->conf, CONF_nopty)) {
if (ssh->version == 1) {
- send_packet(ssh, SSH1_CMSG_WINDOW_SIZE,
- PKT_INT, ssh->term_height,
- PKT_INT, ssh->term_width,
- PKT_INT, 0, PKT_INT, 0, PKT_END);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_WINDOW_SIZE);
+ put_uint32(pktout, ssh->term_height);
+ put_uint32(pktout, ssh->term_width);
+ put_uint32(pktout, 0);
+ put_uint32(pktout, 0);
+ ssh_pkt_write(ssh, pktout);
} else if (ssh->mainchan) {
pktout = ssh2_chanreq_init(ssh->mainchan, "window-change",
NULL, NULL);
@@ -11308,7 +11269,8 @@ static void ssh_special(void *handle, Telnet_Special code)
return;
}
if (ssh->version == 1) {
- send_packet(ssh, SSH1_CMSG_EOF, PKT_END);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_EOF);
+ ssh_pkt_write(ssh, pktout);
} else if (ssh->mainchan) {
sshfwd_write_eof(ssh->mainchan);
ssh->send_ok = 0; /* now stop trying to read from stdin */
@@ -11318,8 +11280,11 @@ static void ssh_special(void *handle, Telnet_Special code)
if (ssh->state == SSH_STATE_CLOSED
|| ssh->state == SSH_STATE_PREPACKET) return;
if (ssh->version == 1) {
- if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
- send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_IGNORE);
+ put_stringz(pktout, "");
+ ssh_pkt_write(ssh, pktout);
+ }
} else {
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
@@ -11468,12 +11433,13 @@ void ssh_send_port_open(void *channel, const char *hostname, int port,
logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org);
if (ssh->version == 1) {
- send_packet(ssh, SSH1_MSG_PORT_OPEN,
- PKT_INT, c->localid,
- PKT_STR, hostname,
- PKT_INT, port,
- /* PKT_STR, , */
- PKT_END);
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_PORT_OPEN);
+ put_uint32(pktout, c->localid);
+ put_stringz(pktout, hostname);
+ put_uint32(pktout, port);
+ /* originator string would go here, but we didn't specify
+ * SSH_PROTOFLAG_HOST_IN_FWD_OPEN */
+ ssh_pkt_write(ssh, pktout);
} else {
pktout = ssh2_chanopen_init(c, "direct-tcpip");
{
From 281d317ab9f37210b9340cce6d7725cf7c9acf27 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 13 Jun 2018 19:42:19 +0100
Subject: [PATCH 374/607] Make put_padding() have a consistent argument order.
Thanks to Alex Landau for pointing out that commit 8b98fea4a
introduced two uses of it with the arguments one way round and one
with them the other way round. (Plus a fourth use where it doesn't
matter, because the padding at the end of the encrypted blob of an
OpenSSH PEM private key consists of n bytes with value n. :-)
On the basis of majority vote, I've switched the order in the function
definition to match the two of the three call sites that expressed the
same opinion, and fixed the third.
---
marshal.c | 2 +-
marshal.h | 6 +++---
ssh2bpp.c | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/marshal.c b/marshal.c
index a8371b66..f0612a85 100644
--- a/marshal.c
+++ b/marshal.c
@@ -11,7 +11,7 @@ void BinarySink_put_data(BinarySink *bs, const void *data, size_t len)
bs->write(bs, data, len);
}
-void BinarySink_put_padding(BinarySink *bs, unsigned char padbyte, size_t len)
+void BinarySink_put_padding(BinarySink *bs, size_t len, unsigned char padbyte)
{
char buf[16];
memset(buf, padbyte, sizeof(buf));
diff --git a/marshal.h b/marshal.h
index 0debe82f..2aa0301a 100644
--- a/marshal.h
+++ b/marshal.h
@@ -111,8 +111,8 @@ struct BinarySink {
BinarySink_put_mp_ssh2(BinarySink_UPCAST(bs), val)
/* Padding with a specified byte. */
-#define put_padding(bs, padbyte, len) \
- BinarySink_put_padding(BinarySink_UPCAST(bs), padbyte, len)
+#define put_padding(bs, len, padbyte) \
+ BinarySink_put_padding(BinarySink_UPCAST(bs), len, padbyte)
/* Fallback: just emit raw data bytes, using a syntax that matches the
* rest of these macros. */
@@ -130,7 +130,7 @@ struct BinarySink {
* declaration(s) of their other parameter type(s) are in scope.
*/
void BinarySink_put_data(BinarySink *, const void *data, size_t len);
-void BinarySink_put_padding(BinarySink *, unsigned char padbyte, size_t len);
+void BinarySink_put_padding(BinarySink *, size_t len, unsigned char padbyte);
void BinarySink_put_byte(BinarySink *, unsigned char);
void BinarySink_put_bool(BinarySink *, int);
void BinarySink_put_uint16(BinarySink *, unsigned long);
diff --git a/ssh2bpp.c b/ssh2bpp.c
index da0ac900..0ea1f793 100644
--- a/ssh2bpp.c
+++ b/ssh2bpp.c
@@ -582,7 +582,7 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
s->out.sequence);
}
- put_padding(pkt, 0, maclen);
+ put_padding(pkt, maclen, 0);
if (s->out.mac && s->out.etm_mode) {
/*
From ba5e56cd1b448ac7d97e2d8d7cb9eafab66a45b9 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 13 Jun 2018 19:44:44 +0100
Subject: [PATCH 375/607] Add a missing check of outgoing_data.
When the whole SSH connection is throttled and then unthrottled, we
need to requeue the callback that transfers data to the Socket from
the new outgoing_data queue introduced in commit 9e3522a97.
The user-visible effect of this missing call was that outgoing SFTP
transactions would lock up, because in SFTP mode we enable the
"simple@putty.projects.tartarus.org" mode and essentially turn off the
per-channel window management, so throttling of the whole connection
becomes the main source of back-pressure.
---
ssh.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/ssh.c b/ssh.c
index 9f8d0314..7df6b509 100644
--- a/ssh.c
+++ b/ssh.c
@@ -2310,11 +2310,14 @@ static void ssh_sent(Plug plug, int bufsize)
{
Ssh ssh = FROMFIELD(plug, struct ssh_tag, plugvt);
/*
- * If the send backlog on the SSH socket itself clears, we
- * should unthrottle the whole world if it was throttled.
+ * If the send backlog on the SSH socket itself clears, we should
+ * unthrottle the whole world if it was throttled, and also resume
+ * sending our bufchain of queued wire data.
*/
- if (bufsize < SSH_MAX_BACKLOG)
+ if (bufsize < SSH_MAX_BACKLOG) {
ssh_throttle_all(ssh, 0, bufsize);
+ queue_idempotent_callback(&ssh->outgoing_data_sender);
+ }
}
static void ssh_hostport_setup(const char *host, int port, Conf *conf,
From d4304f1b7b3f5e4c02f373977f9f7fe172d4f5fd Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 15 Jun 2018 19:08:05 +0100
Subject: [PATCH 376/607] Fix cut and paste goof in SSH-2 compression support.
The new SSH-2 BPP has two functions ssh2_bpp_new_outgoing_crypto and
ssh2_bpp_new_incoming_crypto, which (due to general symmetry) are
_almost_ identical, except that the code that sets up the compression
context in the two directions has to call compress_init in the former
and decompress_init in the latter.
Except that it called compress_init in both, so compression in SSH-2
has been completely broken for a week. How embarrassing. I _remember_
thinking that I'd better make sure to change that one call between the
two, but apparently it fell out of my head before I committed.
---
ssh2bpp.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/ssh2bpp.c b/ssh2bpp.c
index 0ea1f793..11f5d08b 100644
--- a/ssh2bpp.c
+++ b/ssh2bpp.c
@@ -145,10 +145,10 @@ void ssh2_bpp_new_incoming_crypto(
}
s->in.comp = compression;
- /* out_comp is always non-NULL, because no compression is
+ /* in_comp is always non-NULL, because no compression is
* indicated by ssh_comp_none. So compress_init always exists, but
- * it may return a null out_comp_ctx. */
- s->in.comp_ctx = compression->compress_init();
+ * it may return a null in_comp_ctx. */
+ s->in.comp_ctx = compression->decompress_init();
/* Clear the pending_newkeys flag, so that handle_input below will
* start consuming the input data again. */
From 20e8fdece39dd1d680c0feb8eb1432e18c81ade2 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 16 Jun 2018 14:44:10 +0100
Subject: [PATCH 377/607] Stop saying we'll try compression later, if it is
later.
On the post-userauth rekey, when we're specifically rekeying in order
to turn on delayed compression, we shouldn't write the Event Log
"Server supports delayed compression; will try this later" message
that we did in the original key exchange. At this point, it _is_
later, and we're about to turn on compression right now!
---
ssh.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/ssh.c b/ssh.c
index 7df6b509..dbc23c09 100644
--- a/ssh.c
+++ b/ssh.c
@@ -5731,7 +5731,8 @@ static void do_ssh2_transport(void *vctx)
}
if ((i == KEXLIST_CSCOMP || i == KEXLIST_SCCOMP) &&
in_commasep_string(alg->u.comp->delayed_name,
- str.ptr, str.len))
+ str.ptr, str.len) &&
+ !s->userauth_succeeded)
s->pending_compression = TRUE; /* try this later */
}
bombout(("Couldn't agree a %s (available: %.*s)",
From fecd42858c3fb4abd5b9c9dae1d456141820d244 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 8 Jul 2018 15:29:41 +0100
Subject: [PATCH 378/607] winpgnt.c: put all file-mapping code in one function.
Previously, the code to recover and memory-map the file-mapping object
Pageant uses for its IPC, and the code to convey its contents to and
from the cross-platform agent code, were widely separated, with the
former living in the WM_COPYDATA handler in the window procedure, and
the latter in answer_msg.
Now all of that code lives in answer_filemapping_message; WndProc only
handles the _window message_ contents - i.e. ensures the WM_COPYDATA
message has the right dwData id and that its lpData contains an ASCIZ
string - and answer_filemapping_message goes all the way from that
file-mapping object name to calling pageant_handle_msg.
While I'm here, I've also tidied up the code so that it uses the 'goto
cleanup' idiom rather than nesting everything inconveniently deeply,
and arranged that if anything goes wrong then we at least _construct_
an error message (although as yet we don't use that for anything
unless we're compiled with DEBUG_IPC enabled).
---
windows/winpgnt.c | 315 +++++++++++++++++++++++++---------------------
1 file changed, 170 insertions(+), 145 deletions(-)
diff --git a/windows/winpgnt.c b/windows/winpgnt.c
index 9c57a2d6..7aa61c79 100644
--- a/windows/winpgnt.c
+++ b/windows/winpgnt.c
@@ -380,59 +380,6 @@ void keylist_update(void)
}
}
-struct PageantReply {
- char buf[AGENT_MAX_MSGLEN - 4];
- int len, overflowed;
- BinarySink_IMPLEMENTATION;
-};
-
-static void pageant_reply_BinarySink_write(
- BinarySink *bs, const void *data, size_t len)
-{
- struct PageantReply *rep = BinarySink_DOWNCAST(bs, struct PageantReply);
- if (!rep->overflowed && len <= sizeof(rep->buf) - rep->len) {
- memcpy(rep->buf + rep->len, data, len);
- rep->len += len;
- } else {
- rep->overflowed = TRUE;
- }
-}
-
-static void answer_msg(void *msgv)
-{
- unsigned char *msg = (unsigned char *)msgv;
- unsigned msglen;
- struct PageantReply reply;
-
- reply.len = 0;
- reply.overflowed = FALSE;
- BinarySink_INIT(&reply, pageant_reply_BinarySink_write);
-
- msglen = GET_32BIT(msg);
- if (msglen > AGENT_MAX_MSGLEN) {
- pageant_failure_msg(BinarySink_UPCAST(&reply),
- "incoming length field too large", NULL, NULL);
- } else {
- pageant_handle_msg(BinarySink_UPCAST(&reply),
- msg + 4, msglen, NULL, NULL);
- if (reply.len > AGENT_MAX_MSGLEN) {
- reply.len = 0;
- reply.overflowed = FALSE;
- pageant_failure_msg(BinarySink_UPCAST(&reply),
- "output would exceed max msglen", NULL, NULL);
- }
- }
-
- /*
- * Windows Pageant answers messages in place, by overwriting the
- * input message buffer.
- */
- assert(4 + reply.len <= AGENT_MAX_MSGLEN);
- PUT_32BIT(msg, reply.len);
- memcpy(msg + 4, reply.buf, reply.len);
- smemclr(reply.buf, sizeof(reply.buf));
-}
-
static void win_add_keyfile(Filename *filename)
{
char *err;
@@ -829,6 +776,168 @@ PSID get_default_sid(void)
}
#endif
+struct PageantReply {
+ char buf[AGENT_MAX_MSGLEN - 4];
+ int len, overflowed;
+ BinarySink_IMPLEMENTATION;
+};
+
+static void pageant_reply_BinarySink_write(
+ BinarySink *bs, const void *data, size_t len)
+{
+ struct PageantReply *rep = BinarySink_DOWNCAST(bs, struct PageantReply);
+ if (!rep->overflowed && len <= sizeof(rep->buf) - rep->len) {
+ memcpy(rep->buf + rep->len, data, len);
+ rep->len += len;
+ } else {
+ rep->overflowed = TRUE;
+ }
+}
+
+static char *answer_filemapping_message(const char *mapname)
+{
+ HANDLE maphandle = INVALID_HANDLE_VALUE;
+ void *mapaddr = NULL;
+ char *err = NULL;
+ unsigned char *msg;
+ unsigned msglen;
+ struct PageantReply reply;
+
+#ifndef NO_SECURITY
+ PSID mapsid = NULL;
+ PSID expectedsid = NULL;
+ PSID expectedsid_bc = NULL;
+ PSECURITY_DESCRIPTOR psd = NULL;
+#endif
+
+#ifdef DEBUG_IPC
+ debug(("mapname = \"%s\"\n", mapname));
+#endif
+
+ maphandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
+ if (maphandle == NULL || maphandle == INVALID_HANDLE_VALUE) {
+ err = dupprintf("OpenFileMapping(\"%s\"): %s",
+ mapname, win_strerror(GetLastError()));
+ goto cleanup;
+ }
+
+#ifdef DEBUG_IPC
+ debug(("maphandle = %p\n", maphandle));
+#endif
+
+#ifndef NO_SECURITY
+ if (has_security) {
+ DWORD retd;
+
+ if ((expectedsid = get_user_sid()) == NULL) {
+ err = dupstr("unable to get user SID");
+ goto cleanup;
+ }
+
+ if ((expectedsid_bc = get_default_sid()) == NULL) {
+ err = dupstr("unable to get default SID");
+ goto cleanup;
+ }
+
+ if ((retd = p_GetSecurityInfo(
+ maphandle, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
+ &mapsid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)) {
+ err = dupprintf("unable to get owner of file mapping: "
+ "GetSecurityInfo returned: %s",
+ win_strerror(retd));
+ goto cleanup;
+ }
+
+#ifdef DEBUG_IPC
+ {
+ LPTSTR ours, ours2, theirs;
+ ConvertSidToStringSid(mapsid, &theirs);
+ ConvertSidToStringSid(expectedsid, &ours);
+ ConvertSidToStringSid(expectedsid_bc, &ours2);
+ debug(("got sids:\n oursnew=%s\n oursold=%s\n"
+ " theirs=%s\n", ours, ours2, theirs));
+ LocalFree(ours);
+ LocalFree(ours2);
+ LocalFree(theirs);
+ }
+#endif
+
+ if (!EqualSid(mapsid, expectedsid) &&
+ !EqualSid(mapsid, expectedsid_bc)) {
+ err = dupstr("wrong owning SID of file mapping");
+ goto cleanup;
+ }
+ } else
+#endif /* NO_SECURITY */
+ {
+#ifdef DEBUG_IPC
+ debug(("security APIs not present\n"));
+#endif
+ }
+
+ mapaddr = MapViewOfFile(maphandle, FILE_MAP_WRITE, 0, 0, 0);
+ if (!mapaddr) {
+ err = dupprintf("unable to obtain view of file mapping: %s",
+ win_strerror(GetLastError()));
+ goto cleanup;
+ }
+
+#ifdef DEBUG_IPC
+ debug(("mapped address = %p\n", mapaddr));
+#endif
+
+ msglen = GET_32BIT((unsigned char *)mapaddr);
+
+#ifdef DEBUG_IPC
+ debug(("msg length=%08x, msg type=%02x\n",
+ msglen, (unsigned)((unsigned char *) mapaddr)[4]));
+#endif
+
+ reply.len = 0;
+ reply.overflowed = FALSE;
+ BinarySink_INIT(&reply, pageant_reply_BinarySink_write);
+
+ if (msglen > AGENT_MAX_MSGLEN - 4) {
+ pageant_failure_msg(BinarySink_UPCAST(&reply),
+ "incoming length field too large", NULL, NULL);
+ } else {
+ pageant_handle_msg(BinarySink_UPCAST(&reply),
+ msg + 4, msglen, NULL, NULL);
+ if (reply.overflowed || reply.len > AGENT_MAX_MSGLEN - 4) {
+ reply.len = 0;
+ reply.overflowed = FALSE;
+ pageant_failure_msg(BinarySink_UPCAST(&reply),
+ "output would overflow message buffer",
+ NULL, NULL);
+ }
+ }
+
+ if (reply.len > AGENT_MAX_MSGLEN - 4) {
+ err = dupstr("even error-message output overflows buffer");
+ goto cleanup;
+ }
+
+ /*
+ * Windows Pageant answers messages in place, by overwriting the
+ * input message buffer.
+ */
+ assert(4 + reply.len <= AGENT_MAX_MSGLEN);
+ PUT_32BIT(msg, reply.len);
+ memcpy(msg + 4, reply.buf, reply.len);
+ smemclr(reply.buf, sizeof(reply.buf));
+
+ cleanup:
+ /* expectedsid has the lifetime of the program, so we don't free it */
+ sfree(expectedsid_bc);
+ if (psd)
+ LocalFree(psd);
+ if (mapaddr)
+ UnmapViewOfFile(mapaddr);
+ if (maphandle != NULL && maphandle != INVALID_HANDLE_VALUE)
+ CloseHandle(maphandle);
+ return err;
+}
+
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
@@ -971,14 +1080,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
case WM_COPYDATA:
{
COPYDATASTRUCT *cds;
- char *mapname;
- void *p;
- HANDLE filemap;
-#ifndef NO_SECURITY
- PSID mapowner, ourself, ourself2;
-#endif
- PSECURITY_DESCRIPTOR psd = NULL;
- int ret = 0;
+ char *mapname, *err;
cds = (COPYDATASTRUCT *) lParam;
if (cds->dwData != AGENT_COPYDATA_ID)
@@ -986,92 +1088,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
mapname = (char *) cds->lpData;
if (mapname[cds->cbData - 1] != '\0')
return 0; /* failure to be ASCIZ! */
+ err = answer_filemapping_message(mapname);
+ if (err) {
#ifdef DEBUG_IPC
- debug(("mapname is :%s:\n", mapname));
-#endif
- filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
-#ifdef DEBUG_IPC
- debug(("filemap is %p\n", filemap));
-#endif
- if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
-#ifndef NO_SECURITY
- int rc;
- if (has_security) {
- if ((ourself = get_user_sid()) == NULL) {
-#ifdef DEBUG_IPC
- debug(("couldn't get user SID\n"));
-#endif
- CloseHandle(filemap);
- return 0;
- }
-
- if ((ourself2 = get_default_sid()) == NULL) {
-#ifdef DEBUG_IPC
- debug(("couldn't get default SID\n"));
+ debug(("IPC failed: %s\n", err));
#endif
- CloseHandle(filemap);
- return 0;
- }
-
- if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
- OWNER_SECURITY_INFORMATION,
- &mapowner, NULL, NULL, NULL,
- &psd) != ERROR_SUCCESS)) {
-#ifdef DEBUG_IPC
- debug(("couldn't get owner info for filemap: %d\n",
- rc));
-#endif
- CloseHandle(filemap);
- sfree(ourself2);
- return 0;
- }
-#ifdef DEBUG_IPC
- {
- LPTSTR ours, ours2, theirs;
- ConvertSidToStringSid(mapowner, &theirs);
- ConvertSidToStringSid(ourself, &ours);
- ConvertSidToStringSid(ourself2, &ours2);
- debug(("got sids:\n oursnew=%s\n oursold=%s\n"
- " theirs=%s\n", ours, ours2, theirs));
- LocalFree(ours);
- LocalFree(ours2);
- LocalFree(theirs);
- }
-#endif
- if (!EqualSid(mapowner, ourself) &&
- !EqualSid(mapowner, ourself2)) {
- CloseHandle(filemap);
- LocalFree(psd);
- sfree(ourself2);
- return 0; /* security ID mismatch! */
- }
-#ifdef DEBUG_IPC
- debug(("security stuff matched\n"));
-#endif
- LocalFree(psd);
- sfree(ourself2);
- } else {
-#ifdef DEBUG_IPC
- debug(("security APIs not present\n"));
-#endif
- }
-#endif
- p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
-#ifdef DEBUG_IPC
- debug(("p is %p\n", p));
- {
- int i;
- for (i = 0; i < 5; i++)
- debug(("p[%d]=%02x\n", i,
- ((unsigned char *) p)[i]));
- }
-#endif
- answer_msg(p);
- ret = 1;
- UnmapViewOfFile(p);
- }
- CloseHandle(filemap);
- return ret;
+ sfree(err);
+ return 0;
+ }
+ return 1;
}
}
From ac51a712b3abf82277c086fdc76a99e5cdee5c13 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 8 Jul 2018 16:46:32 +0100
Subject: [PATCH 379/607] winpgnt.c: handle arbitrarily large file mappings.
I heard recently that at least one third-party client of Pageant
exists, and that it's used to generate signatures to use with TLS
client certificates. Apparently the signature scheme is compatible,
but TLS tends to need signatures over more data than will fit in
AGENT_MAX_MSGLEN.
Before the BinarySink refactor in commit b6cbad89f, this was OK
because the Windows Pageant IPC didn't check the size of the _input_
message against AGENT_MAX_MSGLEN, only the output one. But then we
started checking both, so that third-party TLS client started failing.
Now we use VirtualQuery to find out the actual size of the file
mapping we've been passed, and our only requirement is that the input
and output messages should both fit in _that_. So TLS should work
again, and also, other clients should be able to retrieve longer lists
of public keys if they pass a larger file mapping.
One side effect of this change is that Pageant's reply message is now
written directly into the shared-memory region. Previously, it was
written into a separate buffer and then memcpy()ed over after
pageant_handle_msg returned, but now the buffer is variable-size, it
seems to me to make more sense to avoid that extra not-entirely
controlled malloc. So I've done one very small reordering of
statements in the cross-platform pageant_handle_msg(), which fixes the
only case I could find where that function started writing its output
before it had finished using the contents of the input buffer.
---
pageant.c | 4 ++--
windows/winpgnt.c | 59 +++++++++++++++++++++++++++++++++--------------
2 files changed, 44 insertions(+), 19 deletions(-)
diff --git a/pageant.c b/pageant.c
index 5c3efb13..f12694de 100644
--- a/pageant.c
+++ b/pageant.c
@@ -339,11 +339,11 @@ void pageant_handle_msg(BinarySink *bs,
return;
}
- put_byte(bs, SSH2_AGENT_SIGN_RESPONSE);
-
signature = strbuf_new();
ssh_key_sign(key->key, sigdata.ptr, sigdata.len,
BinarySink_UPCAST(signature));
+
+ put_byte(bs, SSH2_AGENT_SIGN_RESPONSE);
put_stringsb(bs, signature);
plog(logctx, logfn, "reply: SSH2_AGENT_SIGN_RESPONSE");
diff --git a/windows/winpgnt.c b/windows/winpgnt.c
index 7aa61c79..2325692c 100644
--- a/windows/winpgnt.c
+++ b/windows/winpgnt.c
@@ -4,6 +4,7 @@
#include
#include
+#include
#include
#include
#include
@@ -777,8 +778,9 @@ PSID get_default_sid(void)
#endif
struct PageantReply {
- char buf[AGENT_MAX_MSGLEN - 4];
- int len, overflowed;
+ char *buf;
+ size_t size, len;
+ int overflowed;
BinarySink_IMPLEMENTATION;
};
@@ -786,7 +788,7 @@ static void pageant_reply_BinarySink_write(
BinarySink *bs, const void *data, size_t len)
{
struct PageantReply *rep = BinarySink_DOWNCAST(bs, struct PageantReply);
- if (!rep->overflowed && len <= sizeof(rep->buf) - rep->len) {
+ if (!rep->overflowed && len <= rep->size - rep->len) {
memcpy(rep->buf + rep->len, data, len);
rep->len += len;
} else {
@@ -799,7 +801,7 @@ static char *answer_filemapping_message(const char *mapname)
HANDLE maphandle = INVALID_HANDLE_VALUE;
void *mapaddr = NULL;
char *err = NULL;
- unsigned char *msg;
+ size_t mapsize;
unsigned msglen;
struct PageantReply reply;
@@ -810,6 +812,8 @@ static char *answer_filemapping_message(const char *mapname)
PSECURITY_DESCRIPTOR psd = NULL;
#endif
+ reply.buf = NULL;
+
#ifdef DEBUG_IPC
debug(("mapname = \"%s\"\n", mapname));
#endif
@@ -886,6 +890,31 @@ static char *answer_filemapping_message(const char *mapname)
debug(("mapped address = %p\n", mapaddr));
#endif
+ {
+ MEMORY_BASIC_INFORMATION mbi;
+ size_t mbiSize = VirtualQuery(mapaddr, &mbi, sizeof(mbi));
+ if (mbiSize == 0) {
+ err = dupprintf("unable to query view of file mapping: %s",
+ win_strerror(GetLastError()));
+ goto cleanup;
+ }
+ if (mbiSize < (offsetof(MEMORY_BASIC_INFORMATION, RegionSize) +
+ sizeof(mbi.RegionSize))) {
+ err = dupstr("VirtualQuery returned too little data to get "
+ "region size");
+ goto cleanup;
+ }
+
+ mapsize = mbi.RegionSize;
+ }
+#ifdef DEBUG_IPC
+ debug(("region size = %zd\n", mapsize));
+#endif
+ if (mapsize < 5) {
+ err = dupstr("mapping smaller than smallest possible request");
+ goto cleanup;
+ }
+
msglen = GET_32BIT((unsigned char *)mapaddr);
#ifdef DEBUG_IPC
@@ -893,17 +922,19 @@ static char *answer_filemapping_message(const char *mapname)
msglen, (unsigned)((unsigned char *) mapaddr)[4]));
#endif
+ reply.buf = (char *)mapaddr + 4;
+ reply.size = mapsize - 4;
reply.len = 0;
reply.overflowed = FALSE;
BinarySink_INIT(&reply, pageant_reply_BinarySink_write);
- if (msglen > AGENT_MAX_MSGLEN - 4) {
+ if (msglen > mapsize - 4) {
pageant_failure_msg(BinarySink_UPCAST(&reply),
"incoming length field too large", NULL, NULL);
} else {
pageant_handle_msg(BinarySink_UPCAST(&reply),
- msg + 4, msglen, NULL, NULL);
- if (reply.overflowed || reply.len > AGENT_MAX_MSGLEN - 4) {
+ (unsigned char *)mapaddr + 4, msglen, NULL, NULL);
+ if (reply.overflowed) {
reply.len = 0;
reply.overflowed = FALSE;
pageant_failure_msg(BinarySink_UPCAST(&reply),
@@ -912,19 +943,13 @@ static char *answer_filemapping_message(const char *mapname)
}
}
- if (reply.len > AGENT_MAX_MSGLEN - 4) {
- err = dupstr("even error-message output overflows buffer");
+ if (reply.overflowed) {
+ err = dupstr("even failure message overflows buffer");
goto cleanup;
}
- /*
- * Windows Pageant answers messages in place, by overwriting the
- * input message buffer.
- */
- assert(4 + reply.len <= AGENT_MAX_MSGLEN);
- PUT_32BIT(msg, reply.len);
- memcpy(msg + 4, reply.buf, reply.len);
- smemclr(reply.buf, sizeof(reply.buf));
+ /* Write in the initial length field, and we're done. */
+ PUT_32BIT(((unsigned char *)mapaddr), reply.len);
cleanup:
/* expectedsid has the lifetime of the program, so we don't free it */
From 98528db25a9df211141b8711a3756ee2b6ea26e9 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sun, 8 Jul 2018 17:04:12 +0100
Subject: [PATCH 380/607] Raise AGENT_MAX_MSGLEN to 256Kb.
That's the same value as in the OpenSSH source code, so it should be
large enough that anyone needing to sign a larger message will have
other problems too.
---
pageant.h | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/pageant.h b/pageant.h
index 551c2a36..26263129 100644
--- a/pageant.h
+++ b/pageant.h
@@ -5,12 +5,11 @@
#include
/*
- * FIXME: it would be nice not to have this arbitrary limit. It's
- * currently needed because the Windows Pageant IPC system needs an
- * upper bound known to the client, but it's also reused as a basic
- * sanity check on incoming messages' length fields.
+ * Upper limit on length of any agent message. Used as a basic sanity
+ * check on messages' length fields, and used by the Windows Pageant
+ * client IPC to decide how large a file mapping to allocate.
*/
-#define AGENT_MAX_MSGLEN 8192
+#define AGENT_MAX_MSGLEN 262144
typedef void (*pageant_logfn_t)(void *logctx, const char *fmt, va_list ap);
From daa086fe73d667a92de8b380d78302bdad827b85 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 9 Jul 2018 07:18:08 +0100
Subject: [PATCH 381/607] winpgnt.c: fix an outdated error message.
I just spotted it while I was looking through this module anyway. It
was using %.100s to prevent an sprintf buffer overflow, which hasn't
been necessary since I switched everything over to dupprintf, and also
it was printing the integer value of GetLastError() without using my
convenient translation wrapper win_strerror.
---
windows/winpgnt.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/windows/winpgnt.c b/windows/winpgnt.c
index 2325692c..3734ab17 100644
--- a/windows/winpgnt.c
+++ b/windows/winpgnt.c
@@ -1136,8 +1136,8 @@ void spawn_cmd(const char *cmdline, const char *args, int show)
if (ShellExecute(NULL, _T("open"), cmdline,
args, NULL, show) <= (HINSTANCE) 32) {
char *msg;
- msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
- (int)GetLastError());
+ msg = dupprintf("Failed to run \"%s\": %s", cmdline,
+ win_strerror(GetLastError()));
MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
sfree(msg);
}
From d4abff521a7cba4c703fbe0bdbaa3b9607c0ac24 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 9 Jul 2018 20:59:36 +0100
Subject: [PATCH 382/607] Reinstate calls to ssh_free_pktout!
I think ever since commit 679fa90df last month, PuTTY has been
forgetting to free any of its outgoing packet structures after turning
them into their encrypted wire format. And apparently no users of the
development snapshots have noticed - including me!
---
ssh1bpp.c | 2 ++
ssh2bpp-bare.c | 2 ++
ssh2bpp.c | 2 ++
3 files changed, 6 insertions(+)
diff --git a/ssh1bpp.c b/ssh1bpp.c
index a00c4d3c..5b0f119b 100644
--- a/ssh1bpp.c
+++ b/ssh1bpp.c
@@ -277,4 +277,6 @@ static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
bufchain_add(s->bpp.out_raw, pkt->data + pktoffs,
biglen + 4); /* len(length+padding+type+data+CRC) */
+
+ ssh_free_pktout(pkt);
}
diff --git a/ssh2bpp-bare.c b/ssh2bpp-bare.c
index 270c0118..bdf49a21 100644
--- a/ssh2bpp-bare.c
+++ b/ssh2bpp-bare.c
@@ -157,4 +157,6 @@ static void ssh2_bare_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
PUT_32BIT(pkt->data, pkt->length - 4);
bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
+
+ ssh_free_pktout(pkt);
}
diff --git a/ssh2bpp.c b/ssh2bpp.c
index 11f5d08b..ef007024 100644
--- a/ssh2bpp.c
+++ b/ssh2bpp.c
@@ -610,4 +610,6 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
pkt->encrypted_len = origlen + padding;
bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
+
+ ssh_free_pktout(pkt);
}
From 445fa12da70b901a37a56779bb4179fe72156db1 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 10 Jul 2018 21:04:32 +0100
Subject: [PATCH 383/607] Fix duplicate packets in CBC mode.
Yesterday's reinstatement of ssh_free_pktout revealed - via valgrind
spotting the use-after-free - that the code that prefixed sensible
packets with IV-muddling SSH_MSG_IGNOREs was actually sending a second
copy of the sensible packet in place of the IGNORE, due to a typo.
---
ssh.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ssh.c b/ssh.c
index dbc23c09..dd564c92 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1216,7 +1216,7 @@ static void ssh_pkt_write(Ssh ssh, PktOut *pkt)
*/
PktOut *ipkt = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
put_stringz(ipkt, "");
- ssh_bpp_format_packet(ssh->bpp, pkt);
+ ssh_bpp_format_packet(ssh->bpp, ipkt);
}
ssh_bpp_format_packet(ssh->bpp, pkt);
From bcb94f966e4aeca2a9619b87dfe96d546fde16c6 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 10 Jul 2018 21:27:43 +0100
Subject: [PATCH 384/607] Make compression functions return void.
The return value wasn't used to indicate failure; it only indicated
whether any compression was being done at all or whether the
compression method was ssh_comp_none, and we can tell the latter just
as well by the fact that its init function returns a null context
pointer.
---
ssh.c | 10 +++++++---
ssh.h | 8 ++++----
ssh2bpp.c | 20 +++++++++-----------
sshzlib.c | 6 ++----
4 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/ssh.c b/ssh.c
index dd564c92..369fbf35 100644
--- a/ssh.c
+++ b/ssh.c
@@ -341,8 +341,12 @@ static void *ssh_comp_none_init(void)
static void ssh_comp_none_cleanup(void *handle)
{
}
-static int ssh_comp_none_block(void *handle, unsigned char *block, int len,
- unsigned char **outblock, int *outlen)
+static void ssh_comp_none_block(void *handle, unsigned char *block, int len,
+ unsigned char **outblock, int *outlen)
+{
+}
+static int ssh_decomp_none_block(void *handle, unsigned char *block, int len,
+ unsigned char **outblock, int *outlen)
{
return 0;
}
@@ -353,7 +357,7 @@ static int ssh_comp_none_disable(void *handle)
const static struct ssh_compress ssh_comp_none = {
"none", NULL,
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
- ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
+ ssh_comp_none_init, ssh_comp_none_cleanup, ssh_decomp_none_block,
ssh_comp_none_disable, NULL
};
extern const struct ssh_compress ssh_zlib;
diff --git a/ssh.h b/ssh.h
index f00c7e99..5620af02 100644
--- a/ssh.h
+++ b/ssh.h
@@ -560,8 +560,8 @@ struct ssh_compress {
const char *delayed_name;
void *(*compress_init) (void);
void (*compress_cleanup) (void *);
- int (*compress) (void *, unsigned char *block, int len,
- unsigned char **outblock, int *outlen);
+ void (*compress) (void *, unsigned char *block, int len,
+ unsigned char **outblock, int *outlen);
void *(*decompress_init) (void);
void (*decompress_cleanup) (void *);
int (*decompress) (void *, unsigned char *block, int len,
@@ -981,8 +981,8 @@ void *zlib_compress_init(void);
void zlib_compress_cleanup(void *);
void *zlib_decompress_init(void);
void zlib_decompress_cleanup(void *);
-int zlib_compress_block(void *, unsigned char *block, int len,
- unsigned char **outblock, int *outlen);
+void zlib_compress_block(void *, unsigned char *block, int len,
+ unsigned char **outblock, int *outlen);
int zlib_decompress_block(void *, unsigned char *block, int len,
unsigned char **outblock, int *outlen);
diff --git a/ssh2bpp.c b/ssh2bpp.c
index ef007024..e6e9975f 100644
--- a/ssh2bpp.c
+++ b/ssh2bpp.c
@@ -537,19 +537,17 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
pkt->downstream_id, pkt->additional_log_text);
}
- /*
- * Compress packet payload.
- */
- {
+ if (s->out.comp && s->out.comp_ctx) {
unsigned char *newpayload;
int newlen;
- if (s->out.comp && s->out.comp->compress(
- s->out.comp_ctx, pkt->data + 5, pkt->length - 5,
- &newpayload, &newlen)) {
- pkt->length = 5;
- put_data(pkt, newpayload, newlen);
- sfree(newpayload);
- }
+ /*
+ * Compress packet payload.
+ */
+ s->out.comp->compress(s->out.comp_ctx, pkt->data + 5, pkt->length - 5,
+ &newpayload, &newlen);
+ pkt->length = 5;
+ put_data(pkt, newpayload, newlen);
+ sfree(newpayload);
}
/*
diff --git a/sshzlib.c b/sshzlib.c
index 855ddd86..290e6fe4 100644
--- a/sshzlib.c
+++ b/sshzlib.c
@@ -679,8 +679,8 @@ static int zlib_disable_compression(void *handle)
return n;
}
-int zlib_compress_block(void *handle, unsigned char *block, int len,
- unsigned char **outblock, int *outlen)
+void zlib_compress_block(void *handle, unsigned char *block, int len,
+ unsigned char **outblock, int *outlen)
{
struct LZ77Context *ectx = (struct LZ77Context *)handle;
struct Outbuf *out = (struct Outbuf *) ectx->userdata;
@@ -796,8 +796,6 @@ int zlib_compress_block(void *handle, unsigned char *block, int len,
*outblock = out->outbuf;
*outlen = out->outlen;
-
- return 1;
}
/* ----------------------------------------------------------------------
From 20a9bd5642ac66ae1190069989d33c5fcefe5672 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 9 Jul 2018 20:30:11 +0100
Subject: [PATCH 385/607] Move password-packet padding into the BPP module.
Now when we construct a packet containing sensitive data, we just set
a field saying '... and make it take up at least this much space, to
disguise its true size', and nothing in the rest of the system worries
about that flag until ssh2bpp.c acts on it.
Also, I've changed the strategy for doing the padding. Previously, we
were following the real packet with an SSH_MSG_IGNORE to make up the
size. But that was only a partial defence: it works OK against passive
traffic analysis, but an attacker proxying the TCP stream and
dribbling it out one byte at a time could still have found out the
size of the real packet by noting when the dribbled data provoked a
response. Now I put the SSH_MSG_IGNORE _first_, which should defeat
that attack.
But that in turn doesn't work when we're doing compression, because we
can't predict the compressed sizes accurately enough to make that
strategy sensible. Fortunately, compression provides an alternative
strategy anyway: if we've got zlib turned on when we send one of these
sensitive packets, then we can pad out the compressed zlib data as
much as we like by adding empty RFC1951 blocks (effectively chaining
ZLIB_PARTIAL_FLUSHes). So both strategies should now be dribble-proof.
---
ssh.c | 87 ++++---------------------
ssh.h | 8 +--
ssh1bpp.c | 2 +-
ssh2bpp.c | 108 ++++++++++++++++++++++++------
sshbpp.h | 1 -
sshzlib.c | 192 ++++++++++++++----------------------------------------
6 files changed, 152 insertions(+), 246 deletions(-)
diff --git a/ssh.c b/ssh.c
index 369fbf35..4c237e31 100644
--- a/ssh.c
+++ b/ssh.c
@@ -342,7 +342,8 @@ static void ssh_comp_none_cleanup(void *handle)
{
}
static void ssh_comp_none_block(void *handle, unsigned char *block, int len,
- unsigned char **outblock, int *outlen)
+ unsigned char **outblock, int *outlen,
+ int minlen)
{
}
static int ssh_decomp_none_block(void *handle, unsigned char *block, int len,
@@ -350,15 +351,11 @@ static int ssh_decomp_none_block(void *handle, unsigned char *block, int len,
{
return 0;
}
-static int ssh_comp_none_disable(void *handle)
-{
- return 0;
-}
const static struct ssh_compress ssh_comp_none = {
"none", NULL,
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_decomp_none_block,
- ssh_comp_none_disable, NULL
+ NULL
};
extern const struct ssh_compress ssh_zlib;
const static struct ssh_compress *const compressions[] = {
@@ -1327,72 +1324,6 @@ static void ssh_send_outgoing_data(void *ctx)
}
}
-/*
- * Send a packet whose length needs to be disguised (typically
- * passwords or keyboard-interactive responses).
- */
-static void ssh2_pkt_send_with_padding(Ssh ssh, PktOut *pkt, int padsize)
-{
-#if 0
- if (0) {
- /*
- * The simplest way to do this is to adjust the
- * variable-length padding field in the outgoing packet.
- *
- * Currently compiled out, because some Cisco SSH servers
- * don't like excessively padded packets (bah, why's it
- * always Cisco?)
- */
- pkt->forcepad = padsize;
- ssh2_pkt_send(ssh, pkt);
- } else
-#endif
- {
- /*
- * If we can't do that, however, an alternative approach is to
- * bundle the packet tightly together with an SSH_MSG_IGNORE
- * such that their combined length is a constant. So first we
- * construct the final form of this packet and append it to
- * the outgoing_data bufchain...
- */
- ssh_pkt_write(ssh, pkt);
-
- /*
- * ... but before we return from this function (triggering a
- * call to the outgoing_data_sender), we also construct an
- * SSH_MSG_IGNORE which includes a string that's an exact
- * multiple of the cipher block size. (If the cipher is NULL
- * so that the block size is unavailable, we don't do this
- * trick at all, because we gain nothing by it.)
- */
- if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
- int stringlen, i;
-
- stringlen = (256 - bufchain_size(&ssh->outgoing_data));
- stringlen += ssh->v2_out_cipherblksize - 1;
- stringlen -= (stringlen % ssh->v2_out_cipherblksize);
-
- /*
- * Temporarily disable actual compression, so we can
- * guarantee to get this string exactly the length we want
- * it. The compression-disabling routine should return an
- * integer indicating how many bytes we should adjust our
- * string length by.
- */
- stringlen -= ssh2_bpp_temporarily_disable_compression(ssh->bpp);
-
- pkt = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
- {
- strbuf *substr = strbuf_new();
- for (i = 0; i < stringlen; i++)
- put_byte(substr, random_byte());
- put_stringsb(pkt, substr);
- }
- ssh_pkt_write(ssh, pkt);
- }
- }
-}
-
/*
* Send all queued SSH-2 packets.
*/
@@ -9563,7 +9494,8 @@ static void do_ssh2_userauth(void *vctx)
}
/*
- * Send the response(s) to the server.
+ * Send the response(s) to the server, padding
+ * them to disguise their true length.
*/
s->pktout = ssh_bpp_new_pktout(
ssh->bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE);
@@ -9572,7 +9504,8 @@ static void do_ssh2_userauth(void *vctx)
put_stringz(s->pktout,
s->cur_prompt->prompts[i]->result);
}
- ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
+ s->pktout->minlen = 256;
+ ssh2_pkt_send(ssh, s->pktout);
/*
* Free the prompts structure from this iteration.
@@ -9661,7 +9594,8 @@ static void do_ssh2_userauth(void *vctx)
put_stringz(s->pktout, "password");
put_bool(s->pktout, FALSE);
put_stringz(s->pktout, s->password);
- ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
+ s->pktout->minlen = 256;
+ ssh2_pkt_send(ssh, s->pktout);
logevent("Sent password");
s->type = AUTH_TYPE_PASSWORD;
@@ -9797,7 +9731,8 @@ static void do_ssh2_userauth(void *vctx)
put_stringz(s->pktout,
s->cur_prompt->prompts[1]->result);
free_prompts(s->cur_prompt);
- ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
+ s->pktout->minlen = 256;
+ ssh2_pkt_send(ssh, s->pktout);
logevent("Sent new password");
/*
diff --git a/ssh.h b/ssh.h
index 5620af02..99ff5b73 100644
--- a/ssh.h
+++ b/ssh.h
@@ -81,7 +81,7 @@ typedef struct PktOut {
long prefix; /* bytes up to and including type field */
long length; /* total bytes, including prefix */
int type;
- long forcepad; /* SSH-2: force padding to at least this length */
+ long minlen; /* SSH-2: ensure wire length is at least this */
unsigned char *data; /* allocated storage */
long maxlen; /* amount of storage allocated for `data' */
long encrypted_len; /* for SSH-2 total-size counting */
@@ -561,12 +561,12 @@ struct ssh_compress {
void *(*compress_init) (void);
void (*compress_cleanup) (void *);
void (*compress) (void *, unsigned char *block, int len,
- unsigned char **outblock, int *outlen);
+ unsigned char **outblock, int *outlen,
+ int minlen);
void *(*decompress_init) (void);
void (*decompress_cleanup) (void *);
int (*decompress) (void *, unsigned char *block, int len,
unsigned char **outblock, int *outlen);
- int (*disable_compression) (void *);
const char *text_name;
};
@@ -982,7 +982,7 @@ void zlib_compress_cleanup(void *);
void *zlib_decompress_init(void);
void zlib_decompress_cleanup(void *);
void zlib_compress_block(void *, unsigned char *block, int len,
- unsigned char **outblock, int *outlen);
+ unsigned char **outblock, int *outlen, int minlen);
int zlib_decompress_block(void *, unsigned char *block, int len,
unsigned char **outblock, int *outlen);
diff --git a/ssh1bpp.c b/ssh1bpp.c
index 5b0f119b..7f5b72f6 100644
--- a/ssh1bpp.c
+++ b/ssh1bpp.c
@@ -251,7 +251,7 @@ static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
unsigned char *compblk;
int complen;
zlib_compress_block(s->compctx, pkt->data + 12, pkt->length - 12,
- &compblk, &complen);
+ &compblk, &complen, 0);
/* Replace the uncompressed packet data with the compressed
* version. */
pkt->length = 12;
diff --git a/ssh2bpp.c b/ssh2bpp.c
index e6e9975f..007ab735 100644
--- a/ssh2bpp.c
+++ b/ssh2bpp.c
@@ -496,32 +496,19 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
crFinishV;
}
-int ssh2_bpp_temporarily_disable_compression(BinaryPacketProtocol *bpp)
-{
- struct ssh2_bpp_state *s;
- assert(bpp->vt == &ssh2_bpp_vtable);
- s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
-
- if (!s->out.comp || !s->out.comp_ctx)
- return 0;
-
- return s->out.comp->disable_compression(s->out.comp_ctx);
-}
-
static PktOut *ssh2_bpp_new_pktout(int pkt_type)
{
PktOut *pkt = ssh_new_packet();
pkt->length = 5; /* space for packet length + padding length */
- pkt->forcepad = 0;
+ pkt->minlen = 0;
pkt->type = pkt_type;
put_byte(pkt, pkt_type);
pkt->prefix = pkt->length;
return pkt;
}
-static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
+static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
{
- struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
int origlen, cipherblk, maclen, padding, unencrypted_prefix, i;
if (s->bpp.logctx) {
@@ -537,14 +524,29 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
pkt->downstream_id, pkt->additional_log_text);
}
+ cipherblk = s->out.cipher ? s->out.cipher->blksize : 8;
+ cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
+
if (s->out.comp && s->out.comp_ctx) {
unsigned char *newpayload;
- int newlen;
+ int minlen, newlen;
+
/*
* Compress packet payload.
*/
+ minlen = pkt->minlen;
+ if (minlen) {
+ /*
+ * Work out how much compressed data we need (at least) to
+ * make the overall packet length come to pkt->minlen.
+ */
+ if (s->out.mac)
+ minlen -= s->out.mac->len;
+ minlen -= 8; /* length field + min padding */
+ }
+
s->out.comp->compress(s->out.comp_ctx, pkt->data + 5, pkt->length - 5,
- &newpayload, &newlen);
+ &newpayload, &newlen, minlen);
pkt->length = 5;
put_data(pkt, newpayload, newlen);
sfree(newpayload);
@@ -556,12 +558,8 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
* If pkt->forcepad is set, make sure the packet is at least that size
* after padding.
*/
- cipherblk = s->out.cipher ? s->out.cipher->blksize : 8;
- cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
padding = 4;
unencrypted_prefix = (s->out.mac && s->out.etm_mode) ? 4 : 0;
- if (pkt->length + padding < pkt->forcepad)
- padding = pkt->forcepad - pkt->length;
padding +=
(cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
% cipherblk;
@@ -607,6 +605,74 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
s->out.sequence++; /* whether or not we MACed */
pkt->encrypted_len = origlen + padding;
+}
+
+static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
+{
+ struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+
+ if (pkt->minlen > 0 && !(s->out.comp && s->out.comp_ctx)) {
+ /*
+ * If we've been told to pad the packet out to a given minimum
+ * length, but we're not compressing (and hence can't get the
+ * compression to do the padding by pointlessly opening and
+ * closing zlib blocks), then our other strategy is to precede
+ * this message with an SSH_MSG_IGNORE that makes it up to the
+ * right length.
+ *
+ * A third option in principle, and the most obviously
+ * sensible, would be to set the explicit padding field in the
+ * packet to more than its minimum value. Sadly, that turns
+ * out to break some servers (our institutional memory thinks
+ * Cisco in particular) and so we abandoned that idea shortly
+ * after trying it.
+ */
+
+ /*
+ * Calculate the length we expect the real packet to have.
+ */
+ int block, length;
+ PktOut *ignore_pkt;
+
+ block = s->out.cipher ? s->out.cipher->blksize : 0;
+ if (block < 8)
+ block = 8;
+ length = pkt->length;
+ length += 4; /* minimum 4 byte padding */
+ length += block-1;
+ length -= (length % block);
+ if (s->out.mac)
+ length += s->out.mac->len;
+
+ if (length < pkt->minlen) {
+ /*
+ * We need an ignore message. Calculate its length.
+ */
+ length = pkt->minlen - length;
+
+ /*
+ * And work backwards from that to the length of the
+ * contained string.
+ */
+ if (s->out.mac)
+ length -= s->out.mac->len;
+ length -= 8; /* length field + min padding */
+ length -= 5; /* type code + string length prefix */
+
+ if (length < 0)
+ length = 0;
+
+ ignore_pkt = ssh2_bpp_new_pktout(SSH2_MSG_IGNORE);
+ put_uint32(ignore_pkt, length);
+ while (length-- > 0)
+ put_byte(ignore_pkt, random_byte());
+ ssh2_bpp_format_packet_inner(s, ignore_pkt);
+ bufchain_add(s->bpp.out_raw, ignore_pkt->data, ignore_pkt->length);
+ ssh_free_pktout(ignore_pkt);
+ }
+ }
+
+ ssh2_bpp_format_packet_inner(s, pkt);
bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
ssh_free_pktout(pkt);
diff --git a/sshbpp.h b/sshbpp.h
index cf1a528d..5ec3c6ce 100644
--- a/sshbpp.h
+++ b/sshbpp.h
@@ -47,7 +47,6 @@ void ssh2_bpp_new_incoming_crypto(
const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
const struct ssh_mac *mac, int etm_mode, const void *mac_key,
const struct ssh_compress *compression);
-int ssh2_bpp_temporarily_disable_compression(BinaryPacketProtocol *bpp);
BinaryPacketProtocol *ssh2_bare_bpp_new(void);
diff --git a/sshzlib.c b/sshzlib.c
index 290e6fe4..17550477 100644
--- a/sshzlib.c
+++ b/sshzlib.c
@@ -374,7 +374,6 @@ struct Outbuf {
unsigned long outbits;
int noutbits;
int firstblock;
- int comp_disabled;
};
static void outbits(struct Outbuf *out, unsigned long bits, int nbits)
@@ -502,14 +501,6 @@ static void zlib_literal(struct LZ77Context *ectx, unsigned char c)
{
struct Outbuf *out = (struct Outbuf *) ectx->userdata;
- if (out->comp_disabled) {
- /*
- * We're in an uncompressed block, so just output the byte.
- */
- outbits(out, c, 8);
- return;
- }
-
if (c <= 143) {
/* 0 through 143 are 8 bits long starting at 00110000. */
outbits(out, mirrorbytes[0x30 + c], 8);
@@ -525,8 +516,6 @@ static void zlib_match(struct LZ77Context *ectx, int distance, int len)
int i, j, k;
struct Outbuf *out = (struct Outbuf *) ectx->userdata;
- assert(!out->comp_disabled);
-
while (len > 0) {
int thislen;
@@ -623,7 +612,6 @@ void *zlib_compress_init(void)
out = snew(struct Outbuf);
out->outbits = out->noutbits = 0;
out->firstblock = 1;
- out->comp_disabled = FALSE;
ectx->userdata = out;
return ectx;
@@ -637,50 +625,9 @@ void zlib_compress_cleanup(void *handle)
sfree(ectx);
}
-/*
- * Turn off actual LZ77 analysis for one block, to facilitate
- * construction of a precise-length IGNORE packet. Returns the
- * length adjustment (which is only valid for packets < 65536
- * bytes, but that seems reasonable enough).
- */
-static int zlib_disable_compression(void *handle)
-{
- struct LZ77Context *ectx = (struct LZ77Context *)handle;
- struct Outbuf *out = (struct Outbuf *) ectx->userdata;
- int n;
-
- out->comp_disabled = TRUE;
-
- n = 0;
- /*
- * If this is the first block, we will start by outputting two
- * header bytes, and then three bits to begin an uncompressed
- * block. This will cost three bytes (because we will start on
- * a byte boundary, this is certain).
- */
- if (out->firstblock) {
- n = 3;
- } else {
- /*
- * Otherwise, we will output seven bits to close the
- * previous static block, and _then_ three bits to begin an
- * uncompressed block, and then flush the current byte.
- * This may cost two bytes or three, depending on noutbits.
- */
- n += (out->noutbits + 10) / 8;
- }
-
- /*
- * Now we output four bytes for the length / ~length pair in
- * the uncompressed block.
- */
- n += 4;
-
- return n;
-}
-
void zlib_compress_block(void *handle, unsigned char *block, int len,
- unsigned char **outblock, int *outlen)
+ unsigned char **outblock, int *outlen,
+ int minlen)
{
struct LZ77Context *ectx = (struct LZ77Context *)handle;
struct Outbuf *out = (struct Outbuf *) ectx->userdata;
@@ -702,98 +649,58 @@ void zlib_compress_block(void *handle, unsigned char *block, int len,
} else
in_block = TRUE;
- if (out->comp_disabled) {
- if (in_block)
- outbits(out, 0, 7); /* close static block */
-
- while (len > 0) {
- int blen = (len < 65535 ? len : 65535);
-
- /*
- * Start a Deflate (RFC1951) uncompressed block. We
- * transmit a zero bit (BFINAL=0), followed by two more
- * zero bits (BTYPE=00). Of course these are in the
- * wrong order (00 0), not that it matters.
- */
- outbits(out, 0, 3);
-
- /*
- * Output zero bits to align to a byte boundary.
- */
- if (out->noutbits)
- outbits(out, 0, 8 - out->noutbits);
-
- /*
- * Output the block length, and then its one's
- * complement. They're little-endian, so all we need to
- * do is pass them straight to outbits() with bit count
- * 16.
- */
- outbits(out, blen, 16);
- outbits(out, blen ^ 0xFFFF, 16);
-
- /*
- * Do the `compression': we need to pass the data to
- * lz77_compress so that it will be taken into account
- * for subsequent (distance,length) pairs. But
- * lz77_compress is passed FALSE, which means it won't
- * actually find (or even look for) any matches; so
- * every character will be passed straight to
- * zlib_literal which will spot out->comp_disabled and
- * emit in the uncompressed format.
- */
- lz77_compress(ectx, block, blen, FALSE);
+ if (!in_block) {
+ /*
+ * Start a Deflate (RFC1951) fixed-trees block. We
+ * transmit a zero bit (BFINAL=0), followed by a zero
+ * bit and a one bit (BTYPE=01). Of course these are in
+ * the wrong order (01 0).
+ */
+ outbits(out, 2, 3);
+ }
- len -= blen;
- block += blen;
- }
- outbits(out, 2, 3); /* open new block */
- } else {
- if (!in_block) {
- /*
- * Start a Deflate (RFC1951) fixed-trees block. We
- * transmit a zero bit (BFINAL=0), followed by a zero
- * bit and a one bit (BTYPE=01). Of course these are in
- * the wrong order (01 0).
- */
- outbits(out, 2, 3);
- }
+ /*
+ * Do the compression.
+ */
+ lz77_compress(ectx, block, len, TRUE);
- /*
- * Do the compression.
- */
- lz77_compress(ectx, block, len, TRUE);
+ /*
+ * End the block (by transmitting code 256, which is
+ * 0000000 in fixed-tree mode), and transmit some empty
+ * blocks to ensure we have emitted the byte containing the
+ * last piece of genuine data. There are three ways we can
+ * do this:
+ *
+ * - Minimal flush. Output end-of-block and then open a
+ * new static block. This takes 9 bits, which is
+ * guaranteed to flush out the last genuine code in the
+ * closed block; but allegedly zlib can't handle it.
+ *
+ * - Zlib partial flush. Output EOB, open and close an
+ * empty static block, and _then_ open the new block.
+ * This is the best zlib can handle.
+ *
+ * - Zlib sync flush. Output EOB, then an empty
+ * _uncompressed_ block (000, then sync to byte
+ * boundary, then send bytes 00 00 FF FF). Then open the
+ * new block.
+ *
+ * For the moment, we will use Zlib partial flush.
+ */
+ outbits(out, 0, 7); /* close block */
+ outbits(out, 2, 3 + 7); /* empty static block */
+ outbits(out, 2, 3); /* open new block */
- /*
- * End the block (by transmitting code 256, which is
- * 0000000 in fixed-tree mode), and transmit some empty
- * blocks to ensure we have emitted the byte containing the
- * last piece of genuine data. There are three ways we can
- * do this:
- *
- * - Minimal flush. Output end-of-block and then open a
- * new static block. This takes 9 bits, which is
- * guaranteed to flush out the last genuine code in the
- * closed block; but allegedly zlib can't handle it.
- *
- * - Zlib partial flush. Output EOB, open and close an
- * empty static block, and _then_ open the new block.
- * This is the best zlib can handle.
- *
- * - Zlib sync flush. Output EOB, then an empty
- * _uncompressed_ block (000, then sync to byte
- * boundary, then send bytes 00 00 FF FF). Then open the
- * new block.
- *
- * For the moment, we will use Zlib partial flush.
- */
- outbits(out, 0, 7); /* close block */
- outbits(out, 2, 3 + 7); /* empty static block */
- outbits(out, 2, 3); /* open new block */
+ /*
+ * If we've been asked to pad out the compressed data until it's
+ * at least a given length, do so by emitting further empty static
+ * blocks.
+ */
+ while (out->outlen < minlen) {
+ outbits(out, 0, 7); /* close block */
+ outbits(out, 2, 3); /* open new static block */
}
- out->comp_disabled = FALSE;
-
*outblock = out->outbuf;
*outlen = out->outlen;
}
@@ -1391,7 +1298,6 @@ const struct ssh_compress ssh_zlib = {
zlib_decompress_init,
zlib_decompress_cleanup,
zlib_decompress_block,
- zlib_disable_compression,
"zlib (RFC1950)"
};
From 9f6b59fa2e02527d5b3b6faca627f27d5fba245d Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 16 Aug 2018 19:01:36 +0100
Subject: [PATCH 386/607] Fix platform field in Windows on Arm installers.
I had previously left the platform field (in line 7 of the installer
database's SummaryInformation table) set at "x86" instead of any value
you might expect such as "Arm" or "Arm64", because I found that an MSI
file with either of the latter values was rejected by WoA's msiexec as
invalid.
It turns out this is because I _also_ needed to upgrade the installer
database schema version to a higher value than I even knew existed:
apparently the problem is that those platform fields aren't present in
the older schema. A test confirms that this works.
Unfortunately, WiX 3 doesn't actually know _how_ to write MSIs with
those platform values. But that's OK, because diffing the x86 and x64
MSIs against each other suggested that there were basically no other
changes in the database tables - so I can just generate the installer
as if for x64, and then rewrite that one field after installer
construction using GNOME msitools to take apart the binary file
structure and put it back together. (Those are the same tools I'm
using as part of my system for running WiX on Linux in the first
place.)
This commit introduces a script to do that post-hoc bodging, and calls
it from Buildscr. I've also changed over the choice of Program Files
folder for the Arm installers so that it's ProgramFiles64Folder
instead of ProgramFilesFolder - so now the Windows on Arm installer
doesn't incongruously default to installing in C:\Program Files (x86)!
---
Buildscr | 9 ++++--
windows/installer.wxs | 9 ++++--
windows/msiplatform.py | 63 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 76 insertions(+), 5 deletions(-)
create mode 100755 windows/msiplatform.py
diff --git a/Buildscr b/Buildscr
index 08be3ec6..12ff2fcf 100644
--- a/Buildscr
+++ b/Buildscr
@@ -199,8 +199,13 @@ ifneq "$(cross_winsigncode)" "" in putty/windows do $(cross_winsigncode) -N -i h
# Build a WiX MSI installer, for each of build32 and build64.
in putty/windows with wixonlinux do candle -arch x86 -dRealPlatform=x86 -dDllOk=yes -dBuilddir=build32/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer32.msi -spdb
in putty/windows with wixonlinux do candle -arch x64 -dRealPlatform=x64 -dDllOk=yes -dBuilddir=build64/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer64.msi -spdb
-in putty/windows with wixonlinux do candle -arch x86 -dRealPlatform=Arm -dDllOk=no -dBuilddir=abuild32/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installera32.msi -spdb
-in putty/windows with wixonlinux do candle -arch x86 -dRealPlatform=Arm64 -dDllOk=no -dBuilddir=abuild64/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installera64.msi -spdb
+in putty/windows with wixonlinux do candle -arch x64 -dRealPlatform=Arm -dDllOk=no -dBuilddir=abuild32/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installera32.msi -spdb
+in putty/windows with wixonlinux do candle -arch x64 -dRealPlatform=Arm64 -dDllOk=no -dBuilddir=abuild64/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installera64.msi -spdb
+
+# Bodge the platform fields for the Windows on Arm installers, since
+# WiX 3 doesn't understand Arm platform names itself.
+in putty/windows do ./msiplatform.py installera32.msi Arm
+in putty/windows do ./msiplatform.py installera64.msi Arm64
# Sign the Windows installers.
ifneq "$(cross_winsigncode)" "" in putty/windows do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi installera32.msi installera64.msi
diff --git a/windows/installer.wxs b/windows/installer.wxs
index 47da2c9e..005d7e03 100644
--- a/windows/installer.wxs
+++ b/windows/installer.wxs
@@ -5,17 +5,20 @@
-
-
-
+
+
+
+
+
+
diff --git a/windows/msiplatform.py b/windows/msiplatform.py
new file mode 100755
index 00000000..eea4272f
--- /dev/null
+++ b/windows/msiplatform.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+import argparse
+import os
+import tempfile
+import shutil
+import subprocess
+import pipes
+
+def run(command, verbose):
+ if verbose:
+ sys.stdout.write("$ {}\n".format(" ".join(
+ pipes.quote(word) for word in command)))
+ out = subprocess.check_output(command)
+ if verbose:
+ sys.stdout.write("".join(
+ "> {}\n".format(line) for line in out.splitlines()))
+
+def set_platform(msi, platform, verbose):
+ run(["msidump", "-t", msi], verbose)
+
+ summary_stream = "_SummaryInformation.idt"
+
+ with open(summary_stream) as fh:
+ lines = [line.rstrip("\r\n").split("\t")
+ for line in iter(fh.readline, "")]
+
+ for line in lines[3:]:
+ if line[0] == "7":
+ line[1] = ";".join([platform] + line[1].split(";", 1)[1:])
+
+ with open(summary_stream, "w") as fh:
+ for line in lines:
+ fh.write("\t".join(line) + "\r\n")
+
+ run(["msibuild", msi, "-i", summary_stream], verbose)
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Change the platform field of an MSI installer package.')
+ parser.add_argument("msi", help="MSI installer file.")
+ parser.add_argument("platform", help="New value for the platform field.")
+ parser.add_argument("-v", "--verbose", action="store_true",
+ help="Log what this script is doing.")
+ parser.add_argument("-k", "--keep", action="store_true",
+ help="Don't delete the temporary working directory.")
+ args = parser.parse_args()
+
+ msi = os.path.abspath(args.msi)
+ msidir = os.path.dirname(msi)
+ try:
+ tempdir = tempfile.mkdtemp(dir=msidir)
+ os.chdir(tempdir)
+ set_platform(msi, args.platform, args.verbose)
+ finally:
+ if args.keep:
+ sys.stdout.write(
+ "Retained temporary directory {}\n".format(tempdir))
+ else:
+ shutil.rmtree(tempdir)
+
+if __name__ == '__main__':
+ main()
From 6c924ba862475b83fcdeaaa43d8516c59b0c79a1 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Sat, 25 Aug 2018 14:36:25 +0100
Subject: [PATCH 387/607] GPG key rollover.
This commit adds the new ids and fingerprints in the keys appendix of
the manual, and moves the old ones down into the historic-keys
section. I've tweaked a few pieces of wording for ongoing use, so that
they don't imply a specific number of past key rollovers.
The -pgpfp option in all the tools now shows the new Master Key
fingerprint and the previous (2015) one. I've adjusted all the uses of
the #defines in putty.h so that future rollovers should only have to
modify the #defines themselves.
Most importantly, sign.sh bakes in the ids of the current release and
snapshot keys, so that snapshots will automatically be signed with the
new snapshot key and the -r option will invoke the new release key.
---
doc/pgpkeys.but | 91 ++++++++++++++++++++++++++++------------------
putty.h | 16 ++++----
sign.sh | 4 +-
unix/uxmisc.c | 10 ++---
windows/wincons.c | 10 ++---
windows/winutils.c | 10 ++---
6 files changed, 81 insertions(+), 60 deletions(-)
diff --git a/doc/pgpkeys.but b/doc/pgpkeys.but
index 71143af2..85008c88 100644
--- a/doc/pgpkeys.but
+++ b/doc/pgpkeys.but
@@ -53,31 +53,25 @@ The current issue of those keys are available for download from the
PuTTY website, and are also available on PGP keyservers using the key
IDs listed below.
-\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2015.asc}{\s{Master Key}}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2018.asc}{\s{Master Key} (2018)}
-\dd RSA, 4096-bit. Key ID: \cw{4096R/04676F7C} (long version:
-\cw{4096R/AB585DC604676F7C}). Fingerprint:
-\cw{440D\_E3B5\_B7A1\_CA85\_B3CC\_\_1718\_AB58\_5DC6\_0467\_6F7C}
+\dd RSA, 4096-bit. Key ID: \cw{76BC7FE4EBFD2D9E}. Fingerprint:
+\cw{24E1\_B1C5\_75EA\_3C9F\_F752\_\_A922\_76BC\_7FE4\_EBFD\_2D9E}
-\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2015.asc}{\s{Release Key}}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2018.asc}{\s{Release Key} (2018)}
-\dd RSA, 2048-bit. Key ID: \cw{2048R/B43434E4} (long version:
-\cw{2048R/9DFE2648B43434E4}). Fingerprint:
-\cw{0054\_DDAA\_8ADA\_15D2\_768A\_\_6DE7\_9DFE\_2648\_B434\_34E4}
+\dd RSA, 3072-bit. Key ID: \cw{6289A25F4AE8DA82}. Fingerprint:
+\cw{E273\_94AC\_A3F9\_D904\_9522\_\_E054\_6289\_A25F\_4AE8\_DA82}
-\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2016.asc}{\s{Secure Contact Key}}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2018.asc}{\s{Snapshot Key} (2018)}
-\dd RSA, 2048-bit. Main key ID: \cw{2048R/8A0AF00B} (long version:
-\cw{2048R/C4FCAAD08A0AF00B}). Encryption subkey ID:
-\cw{2048R/50C2CF5C} (long version: \cw{2048R/9EB39CC150C2CF5C}).
-Fingerprint:
-\cw{8A26\_250E\_763F\_E359\_75F3\_\_118F\_C4FC\_AAD0\_8A0A\_F00B}
+\dd RSA, 3072-bit. Key ID: \cw{38BA7229B7588FD1}. Fingerprint:
+\cw{C92B\_52E9\_9AB6\_1DDA\_33DB\_\_2B7A\_38BA\_7229\_B758\_8FD1}
-\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2015.asc}{\s{Snapshot Key}}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2018.asc}{\s{Secure Contact Key} (2018)}
-\dd RSA, 2048-bit. Key ID: \cw{2048R/D15F7E8A} (long version:
-\cw{2048R/EEF20295D15F7E8A}). Fingerprint:
-\cw{0A3B\_0048\_FE49\_9B67\_A234\_\_FEB6\_EEF2\_0295\_D15F\_7E8A}
+\dd RSA, 3072-bit. Key ID: \cw{657D487977F95C98}. Fingerprint:
+\cw{A680\_0082\_2998\_6E46\_22CA\_\_0E43\_657D\_4879\_77F9\_5C98}
\H{pgpkeys-security} Security details
@@ -156,28 +150,53 @@ once.
\H{pgpkeys-rollover} Key rollover
-Our current keys were generated in September 2015, except for the
-Secure Contact Key which was generated in February 2016 (we didn't
-think of it until later).
+Our current keys were generated in August 2018.
+
+Each new Master Key is signed with the old one, to show that it really
+is owned by the same people and not substituted by an attacker.
-Prior to that, we had a much older set of keys generated in 2000. For
-each of the key types above (other than the Secure Contact Key), we
-provided both an RSA key \e{and} a DSA key (because at the time we
-generated them, RSA was not in practice available to everyone, due to
-export restrictions).
+Each new Master Key also signs the previous Release Keys, in case
+you're trying to verify the signatures on a release prior to the
+rollover and can find a chain of trust to those keys from any of the
+people who have signed our new Master Key.
-The new Master Key is signed with both of the old ones, to show that
-it really is owned by the same people and not substituted by an
-attacker. Also, we have retrospectively signed the old Release Keys
-with the new Master Key, in case you're trying to verify the
-signatures on a release prior to the rollover and can find a chain of
-trust to those keys from any of the people who have signed our new
-Master Key.
+Each release is signed with the Release Key that was current at the
+time of release. We don't go back and re-sign old releases with newly
+generated keys.
-Future releases will be signed with the up-to-date keys shown above.
-Releases prior to the rollover are signed with the old Release Keys.
+The details of all previous keys are given here.
+
+\s{Key generated in 2016} (when we first introduced the Secure Contact Key)
+
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2016.asc}{\s{Secure Contact Key} (2016)}
+
+\dd RSA, 2048-bit. Main key ID: \cw{2048R/8A0AF00B} (long version:
+\cw{2048R/C4FCAAD08A0AF00B}). Encryption subkey ID:
+\cw{2048R/50C2CF5C} (long version: \cw{2048R/9EB39CC150C2CF5C}).
+Fingerprint:
+\cw{8A26\_250E\_763F\_E359\_75F3\_\_118F\_C4FC\_AAD0\_8A0A\_F00B}
+
+\s{Keys generated in the 2015 rollover}
+
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2015.asc}{\s{Master Key} (2015)}
+
+\dd RSA, 4096-bit. Key ID: \cw{4096R/04676F7C} (long version:
+\cw{4096R/AB585DC604676F7C}). Fingerprint:
+\cw{440D\_E3B5\_B7A1\_CA85\_B3CC\_\_1718\_AB58\_5DC6\_0467\_6F7C}
+
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2015.asc}{\s{Release Key} (2015)}
+
+\dd RSA, 2048-bit. Key ID: \cw{2048R/B43434E4} (long version:
+\cw{2048R/9DFE2648B43434E4}). Fingerprint:
+\cw{0054\_DDAA\_8ADA\_15D2\_768A\_\_6DE7\_9DFE\_2648\_B434\_34E4}
+
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2015.asc}{\s{Snapshot Key} (2015)}
+
+\dd RSA, 2048-bit. Key ID: \cw{2048R/D15F7E8A} (long version:
+\cw{2048R/EEF20295D15F7E8A}). Fingerprint:
+\cw{0A3B\_0048\_FE49\_9B67\_A234\_\_FEB6\_EEF2\_0295\_D15F\_7E8A}
-For completeness, those old keys are given here:
+\s{Original keys generated in 2000} (two sets, RSA and DSA)
\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-rsa.asc}{\s{Master Key} (original RSA)}
diff --git a/putty.h b/putty.h
index fb9a2d55..844c6fa1 100644
--- a/putty.h
+++ b/putty.h
@@ -30,15 +30,17 @@
#define MAX_TICK_MINS (INT_MAX / (60 * TICKSPERSEC))
/*
- * Fingerprints of the PGP master keys that can be used to establish a trust
- * path between an executable and other files.
+ * Fingerprints of the current and previous PGP master keys, to
+ * establish a trust path between an executable and other files.
*/
-#define PGP_MASTER_KEY_FP \
+#define PGP_MASTER_KEY_YEAR "2018"
+#define PGP_MASTER_KEY_DETAILS "RSA, 4096-bit"
+#define PGP_MASTER_KEY_FP \
+ "24E1 B1C5 75EA 3C9F F752 A922 76BC 7FE4 EBFD 2D9E"
+#define PGP_PREV_MASTER_KEY_YEAR "2015"
+#define PGP_PREV_MASTER_KEY_DETAILS "RSA, 4096-bit"
+#define PGP_PREV_MASTER_KEY_FP \
"440D E3B5 B7A1 CA85 B3CC 1718 AB58 5DC6 0467 6F7C"
-#define PGP_RSA_MASTER_KEY_FP \
- "8F 15 97 DA 25 30 AB 0D 88 D1 92 54 11 CF 0C 4C"
-#define PGP_DSA_MASTER_KEY_FP \
- "313C 3E76 4B74 C2C5 F2AE 83A8 4F5E 6DF5 6A93 B34E"
/* Three attribute types:
* The ATTRs (normal attributes) are stored with the characters in
diff --git a/sign.sh b/sign.sh
index 8dbdb613..bece850a 100755
--- a/sign.sh
+++ b/sign.sh
@@ -9,14 +9,14 @@
set -e
-keyname=EEF20295D15F7E8A
+keyname=38BA7229B7588FD1
preliminary=false
while :; do
case "$1" in
-r)
shift
- keyname=9DFE2648B43434E4
+ keyname=6289A25F4AE8DA82
;;
-p)
shift
diff --git a/unix/uxmisc.c b/unix/uxmisc.c
index c478856b..320a6c9e 100644
--- a/unix/uxmisc.c
+++ b/unix/uxmisc.c
@@ -158,12 +158,12 @@ void pgp_fingerprints(void)
"one. See the manual for more information.\n"
"(Note: these fingerprints have nothing to do with SSH!)\n"
"\n"
- "PuTTY Master Key as of 2015 (RSA, 4096-bit):\n"
+ "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR
+ " (" PGP_MASTER_KEY_DETAILS "):\n"
" " PGP_MASTER_KEY_FP "\n\n"
- "Original PuTTY Master Key (RSA, 1024-bit):\n"
- " " PGP_RSA_MASTER_KEY_FP "\n"
- "Original PuTTY Master Key (DSA, 1024-bit):\n"
- " " PGP_DSA_MASTER_KEY_FP "\n", stdout);
+ "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR
+ ", " PGP_PREV_MASTER_KEY_DETAILS "):\n"
+ " " PGP_PREV_MASTER_KEY_FP "\n", stdout);
}
/*
diff --git a/windows/wincons.c b/windows/wincons.c
index e315f64a..4827ddbd 100644
--- a/windows/wincons.c
+++ b/windows/wincons.c
@@ -327,12 +327,12 @@ void pgp_fingerprints(void)
"one. See the manual for more information.\n"
"(Note: these fingerprints have nothing to do with SSH!)\n"
"\n"
- "PuTTY Master Key as of 2015 (RSA, 4096-bit):\n"
+ "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR
+ " (" PGP_MASTER_KEY_DETAILS "):\n"
" " PGP_MASTER_KEY_FP "\n\n"
- "Original PuTTY Master Key (RSA, 1024-bit):\n"
- " " PGP_RSA_MASTER_KEY_FP "\n"
- "Original PuTTY Master Key (DSA, 1024-bit):\n"
- " " PGP_DSA_MASTER_KEY_FP "\n", stdout);
+ "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR
+ ", " PGP_PREV_MASTER_KEY_DETAILS "):\n"
+ " " PGP_PREV_MASTER_KEY_FP "\n", stdout);
}
void console_provide_logctx(void *logctx)
diff --git a/windows/winutils.c b/windows/winutils.c
index 31b98d18..58614966 100644
--- a/windows/winutils.c
+++ b/windows/winutils.c
@@ -142,12 +142,12 @@ void pgp_fingerprints(void)
"one. See the manual for more information.\n"
"(Note: these fingerprints have nothing to do with SSH!)\n"
"\n"
- "PuTTY Master Key as of 2015 (RSA, 4096-bit):\n"
+ "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR
+ " (" PGP_MASTER_KEY_DETAILS "):\n"
" " PGP_MASTER_KEY_FP "\n\n"
- "Original PuTTY Master Key (RSA, 1024-bit):\n"
- " " PGP_RSA_MASTER_KEY_FP "\n"
- "Original PuTTY Master Key (DSA, 1024-bit):\n"
- " " PGP_DSA_MASTER_KEY_FP,
+ "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR
+ ", " PGP_PREV_MASTER_KEY_DETAILS "):\n"
+ " " PGP_PREV_MASTER_KEY_FP,
"PGP fingerprints", MB_ICONINFORMATION | MB_OK,
HELPCTXID(pgp_fingerprints));
}
From 566d4826f4115006b2bfaf5210649a7935cb3c00 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 12 Sep 2018 08:55:39 +0100
Subject: [PATCH 388/607] testback.c: add some missing 'const'.
This source file isn't actually built as part of even a test binary,
so it hasn't been kept up to date with internal API changes. But it
might still come in useful in the future (I think its original purpose
was to substitute for a normal backend in order to test the GUI side
of a new PuTTY port before the network side was written), so I'll at
least try to carry on keeping it updated.
---
testback.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/testback.c b/testback.c
index 752ec4ca..a84caee0 100644
--- a/testback.c
+++ b/testback.c
@@ -32,15 +32,15 @@
#include "putty.h"
-static const char *null_init(void *, void **, Conf *, char *, int, char **,
- int, int);
-static const char *loop_init(void *, void **, Conf *, char *, int, char **,
- int, int);
+static const char *null_init(void *, void **, Conf *, const char *, int,
+ char **, int, int);
+static const char *loop_init(void *, void **, Conf *, const char *, int,
+ char **, int, int);
static void null_free(void *);
static void loop_free(void *);
static void null_reconfig(void *, Conf *);
-static int null_send(void *, char *, int);
-static int loop_send(void *, char *, int);
+static int null_send(void *, const char *, int);
+static int loop_send(void *, const char *, int);
static int null_sendbuffer(void *);
static void null_size(void *, int, int);
static void null_special(void *, Telnet_Special);
@@ -73,14 +73,14 @@ struct loop_state {
};
static const char *null_init(void *frontend_handle, void **backend_handle,
- Conf *conf, char *host, int port,
+ Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive) {
return NULL;
}
static const char *loop_init(void *frontend_handle, void **backend_handle,
- Conf *conf, char *host, int port,
+ Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive) {
struct loop_state *st = snew(struct loop_state);
@@ -104,12 +104,12 @@ static void null_reconfig(void *handle, Conf *conf) {
}
-static int null_send(void *handle, char *buf, int len) {
+static int null_send(void *handle, const char *buf, int len) {
return 0;
}
-static int loop_send(void *handle, char *buf, int len) {
+static int loop_send(void *handle, const char *buf, int len) {
struct loop_state *st = handle;
return from_backend(st->term, 0, buf, len);
From e72e8ebe59439e373c336c84d44d214fdc5476f0 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 11 Sep 2018 15:02:59 +0100
Subject: [PATCH 389/607] Expose the Ldisc structure tag throughout the code.
That's one fewer anonymous 'void *' which might be accidentally
confused with some other pointer type if I misremember the order of
function arguments.
While I'm here, I've made its pointer-nature explicit - that is,
'Ldisc' is now a typedef for the structure type itself rather than a
pointer to it. A stylistic change only, but it feels more natural to
me these days for a thing you're going to eventually pass to a 'free'
function.
---
defs.h | 2 ++
fuzzterm.c | 4 ++--
ldisc.c | 28 +++++++++++-----------------
ldisc.h | 4 ++--
ldiscucs.c | 6 ++----
pscp.c | 2 +-
psftp.c | 2 +-
putty.h | 16 ++++++++--------
raw.c | 2 +-
rlogin.c | 2 +-
ssh.c | 4 ++--
telnet.c | 4 ++--
terminal.h | 2 +-
testback.c | 4 ++--
unix/gtkwin.c | 2 +-
unix/uxpty.c | 2 +-
unix/uxser.c | 2 +-
windows/window.c | 2 +-
windows/winser.c | 2 +-
19 files changed, 43 insertions(+), 49 deletions(-)
diff --git a/defs.h b/defs.h
index bfdf345d..f1d73fb5 100644
--- a/defs.h
+++ b/defs.h
@@ -44,6 +44,8 @@ typedef struct SockAddr_tag *SockAddr;
typedef struct Socket_vtable Socket_vtable;
typedef struct Plug_vtable Plug_vtable;
+typedef struct Ldisc_tag Ldisc;
+
/* Note indirection: for historical reasons (it used to be closer to
* the OS socket type), the type that most code uses for a socket is
* 'Socket', not 'Socket *'. So an implementation of Socket or Plug
diff --git a/fuzzterm.c b/fuzzterm.c
index 21246069..cdefb0d9 100644
--- a/fuzzterm.c
+++ b/fuzzterm.c
@@ -71,8 +71,8 @@ void set_title(void *frontend, char *t) { }
void set_icon(void *frontend, char *t) { }
void set_sbar(void *frontend, int a, int b, int c) { }
-void ldisc_send(void *handle, const void *buf, int len, int interactive) {}
-void ldisc_echoedit_update(void *handle) {}
+void ldisc_send(Ldisc *ldisc, const void *buf, int len, int interactive) {}
+void ldisc_echoedit_update(Ldisc *ldisc) {}
Context get_ctx(void *frontend) {
static char x;
diff --git a/ldisc.c b/ldisc.c
index 06c5e90f..ba90e1d0 100644
--- a/ldisc.c
+++ b/ldisc.c
@@ -22,12 +22,12 @@
(ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \
term_ldisc(ldisc->term, LD_EDIT))))
-static void c_write(Ldisc ldisc, const void *buf, int len)
+static void c_write(Ldisc *ldisc, const void *buf, int len)
{
from_backend(ldisc->frontend, 0, buf, len);
}
-static int plen(Ldisc ldisc, unsigned char c)
+static int plen(Ldisc *ldisc, unsigned char c)
{
if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term)))
return 1;
@@ -42,7 +42,7 @@ static int plen(Ldisc ldisc, unsigned char c)
return 4; /* hex representation */
}
-static void pwrite(Ldisc ldisc, unsigned char c)
+static void pwrite(Ldisc *ldisc, unsigned char c)
{
if ((c >= 32 && c <= 126) ||
(!in_utf(ldisc->term) && c >= 0xA0) ||
@@ -60,7 +60,7 @@ static void pwrite(Ldisc ldisc, unsigned char c)
}
}
-static int char_start(Ldisc ldisc, unsigned char c)
+static int char_start(Ldisc *ldisc, unsigned char c)
{
if (in_utf(ldisc->term))
return (c < 0x80 || c >= 0xC0);
@@ -68,7 +68,7 @@ static int char_start(Ldisc ldisc, unsigned char c)
return 1;
}
-static void bsb(Ldisc ldisc, int n)
+static void bsb(Ldisc *ldisc, int n)
{
while (n--)
c_write(ldisc, "\010 \010", 3);
@@ -77,11 +77,11 @@ static void bsb(Ldisc ldisc, int n)
#define CTRL(x) (x^'@')
#define KCTRL(x) ((x^'@') | 0x100)
-void *ldisc_create(Conf *conf, Terminal *term,
+Ldisc *ldisc_create(Conf *conf, Terminal *term,
Backend *back, void *backhandle,
void *frontend)
{
- Ldisc ldisc = snew(struct ldisc_tag);
+ Ldisc *ldisc = snew(Ldisc);
ldisc->buf = NULL;
ldisc->buflen = 0;
@@ -104,10 +104,8 @@ void *ldisc_create(Conf *conf, Terminal *term,
return ldisc;
}
-void ldisc_configure(void *handle, Conf *conf)
+void ldisc_configure(Ldisc *ldisc, Conf *conf)
{
- Ldisc ldisc = (Ldisc) handle;
-
ldisc->telnet_keyboard = conf_get_int(conf, CONF_telnet_keyboard);
ldisc->telnet_newline = conf_get_int(conf, CONF_telnet_newline);
ldisc->protocol = conf_get_int(conf, CONF_protocol);
@@ -115,10 +113,8 @@ void ldisc_configure(void *handle, Conf *conf)
ldisc->localedit = conf_get_int(conf, CONF_localedit);
}
-void ldisc_free(void *handle)
+void ldisc_free(Ldisc *ldisc)
{
- Ldisc ldisc = (Ldisc) handle;
-
if (ldisc->term)
ldisc->term->ldisc = NULL;
if (ldisc->back)
@@ -128,16 +124,14 @@ void ldisc_free(void *handle)
sfree(ldisc);
}
-void ldisc_echoedit_update(void *handle)
+void ldisc_echoedit_update(Ldisc *ldisc)
{
- Ldisc ldisc = (Ldisc) handle;
frontend_echoedit_update(ldisc->frontend, ECHOING, EDITING);
}
-void ldisc_send(void *handle, const void *vbuf, int len, int interactive)
+void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, int interactive)
{
const char *buf = (const char *)vbuf;
- Ldisc ldisc = (Ldisc) handle;
int keyflag = 0;
assert(ldisc->term);
diff --git a/ldisc.h b/ldisc.h
index 5dbe2a76..44f5772f 100644
--- a/ldisc.h
+++ b/ldisc.h
@@ -8,7 +8,7 @@
#ifndef PUTTY_LDISC_H
#define PUTTY_LDISC_H
-typedef struct ldisc_tag {
+struct Ldisc_tag {
Terminal *term;
Backend *back;
void *backhandle;
@@ -21,6 +21,6 @@ typedef struct ldisc_tag {
char *buf;
int buflen, bufsiz, quotenext;
-} *Ldisc;
+};
#endif /* PUTTY_LDISC_H */
diff --git a/ldiscucs.c b/ldiscucs.c
index 1634bc43..6c943a09 100644
--- a/ldiscucs.c
+++ b/ldiscucs.c
@@ -12,10 +12,9 @@
#include "terminal.h"
#include "ldisc.h"
-void lpage_send(void *handle,
+void lpage_send(Ldisc *ldisc,
int codepage, const char *buf, int len, int interactive)
{
- Ldisc ldisc = (Ldisc)handle;
wchar_t *widebuffer = 0;
int widesize = 0;
int wclen;
@@ -34,9 +33,8 @@ void lpage_send(void *handle,
sfree(widebuffer);
}
-void luni_send(void *handle, const wchar_t *widebuf, int len, int interactive)
+void luni_send(Ldisc *ldisc, const wchar_t *widebuf, int len, int interactive)
{
- Ldisc ldisc = (Ldisc)handle;
int ratio = (in_utf(ldisc->term))?3:1;
char *linebuffer;
int linesize;
diff --git a/pscp.c b/pscp.c
index 22cbf57e..964e2443 100644
--- a/pscp.c
+++ b/pscp.c
@@ -60,7 +60,7 @@ const char *const appname = "PSCP";
*/
#define MAX_SCP_BUFSIZE 16384
-void ldisc_echoedit_update(void *handle) { }
+void ldisc_echoedit_update(Ldisc *ldisc) { }
static void tell_char(FILE *stream, char c)
{
diff --git a/psftp.c b/psftp.c
index 46eeeb40..b28c0976 100644
--- a/psftp.c
+++ b/psftp.c
@@ -2483,7 +2483,7 @@ void connection_fatal(void *frontend, const char *fmt, ...)
cleanup_exit(1);
}
-void ldisc_echoedit_update(void *handle) { }
+void ldisc_echoedit_update(Ldisc *ldisc) { }
/*
* In psftp, all agent requests should be synchronous, so this is a
diff --git a/putty.h b/putty.h
index 844c6fa1..042f66c5 100644
--- a/putty.h
+++ b/putty.h
@@ -461,7 +461,7 @@ struct backend_tag {
* may be lost. */
int (*sendok) (void *handle);
int (*ldisc) (void *handle, int);
- void (*provide_ldisc) (void *handle, void *ldisc);
+ void (*provide_ldisc) (void *handle, Ldisc *ldisc);
void (*provide_logctx) (void *handle, void *logctx);
/*
* back->unthrottle() tells the back end that the front end
@@ -1174,18 +1174,18 @@ extern Backend ssh_backend;
/*
* Exports from ldisc.c.
*/
-void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *);
-void ldisc_configure(void *, Conf *);
-void ldisc_free(void *);
-void ldisc_send(void *handle, const void *buf, int len, int interactive);
-void ldisc_echoedit_update(void *handle);
+Ldisc *ldisc_create(Conf *, Terminal *, Backend *, void *, void *);
+void ldisc_configure(Ldisc *, Conf *);
+void ldisc_free(Ldisc *);
+void ldisc_send(Ldisc *, const void *buf, int len, int interactive);
+void ldisc_echoedit_update(Ldisc *);
/*
* Exports from ldiscucs.c.
*/
-void lpage_send(void *, int codepage, const char *buf, int len,
+void lpage_send(Ldisc *, int codepage, const char *buf, int len,
int interactive);
-void luni_send(void *, const wchar_t * widebuf, int len, int interactive);
+void luni_send(Ldisc *, const wchar_t * widebuf, int len, int interactive);
/*
* Exports from sshrand.c.
diff --git a/raw.c b/raw.c
index 67d34601..58a500c7 100644
--- a/raw.c
+++ b/raw.c
@@ -274,7 +274,7 @@ static int raw_ldisc(void *handle, int option)
return 0;
}
-static void raw_provide_ldisc(void *handle, void *ldisc)
+static void raw_provide_ldisc(void *handle, Ldisc *ldisc)
{
/* This is a stub. */
}
diff --git a/rlogin.c b/rlogin.c
index 9156340c..7f9093e4 100644
--- a/rlogin.c
+++ b/rlogin.c
@@ -366,7 +366,7 @@ static int rlogin_ldisc(void *handle, int option)
return 0;
}
-static void rlogin_provide_ldisc(void *handle, void *ldisc)
+static void rlogin_provide_ldisc(void *handle, Ldisc *ldisc)
{
/* This is a stub. */
}
diff --git a/ssh.c b/ssh.c
index 4c237e31..d02b7321 100644
--- a/ssh.c
+++ b/ssh.c
@@ -700,7 +700,7 @@ struct ssh_tag {
const Plug_vtable *plugvt;
- void *ldisc;
+ Ldisc *ldisc;
void *logctx;
unsigned char session_key[32];
@@ -11428,7 +11428,7 @@ static int ssh_ldisc(void *handle, int option)
return FALSE;
}
-static void ssh_provide_ldisc(void *handle, void *ldisc)
+static void ssh_provide_ldisc(void *handle, Ldisc *ldisc)
{
Ssh ssh = (Ssh) handle;
ssh->ldisc = ldisc;
diff --git a/telnet.c b/telnet.c
index 6fe509dc..0f958b50 100644
--- a/telnet.c
+++ b/telnet.c
@@ -175,7 +175,7 @@ typedef struct telnet_tag {
int closed_on_socket_error;
void *frontend;
- void *ldisc;
+ Ldisc *ldisc;
int term_width, term_height;
int opt_states[NUM_OPTS];
@@ -1059,7 +1059,7 @@ static int telnet_ldisc(void *handle, int option)
return FALSE;
}
-static void telnet_provide_ldisc(void *handle, void *ldisc)
+static void telnet_provide_ldisc(void *handle, Ldisc *ldisc)
{
Telnet telnet = (Telnet) handle;
telnet->ldisc = ldisc;
diff --git a/terminal.h b/terminal.h
index ba4a07f6..3eb60939 100644
--- a/terminal.h
+++ b/terminal.h
@@ -230,7 +230,7 @@ struct terminal_tag {
void (*resize_fn)(void *, int, int);
void *resize_ctx;
- void *ldisc;
+ Ldisc *ldisc;
void *frontend;
diff --git a/testback.c b/testback.c
index a84caee0..d84c77db 100644
--- a/testback.c
+++ b/testback.c
@@ -49,7 +49,7 @@ static int null_connected(void *);
static int null_exitcode(void *);
static int null_sendok(void *);
static int null_ldisc(void *, int);
-static void null_provide_ldisc(void *, void *);
+static void null_provide_ldisc(void *, Ldisc *);
static void null_provide_logctx(void *, void *);
static void null_unthrottle(void *, int);
static int null_cfg_info(void *);
@@ -157,7 +157,7 @@ static int null_ldisc(void *handle, int option) {
return 0;
}
-static void null_provide_ldisc (void *handle, void *ldisc) {
+static void null_provide_ldisc (void *handle, Ldisc *ldisc) {
}
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 5b84c7c9..e28146b4 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -158,7 +158,7 @@ struct gui_data {
char *wintitle;
char *icontitle;
int master_fd, master_func_id;
- void *ldisc;
+ Ldisc *ldisc;
Backend *back;
void *backhandle;
Terminal *term;
diff --git a/unix/uxpty.c b/unix/uxpty.c
index 7aaad8ac..9df61532 100644
--- a/unix/uxpty.c
+++ b/unix/uxpty.c
@@ -1206,7 +1206,7 @@ static int pty_ldisc(void *handle, int option)
return 0; /* neither editing nor echoing */
}
-static void pty_provide_ldisc(void *handle, void *ldisc)
+static void pty_provide_ldisc(void *handle, Ldisc *ldisc)
{
/* Pty pty = (Pty)handle; */
/* This is a stub. */
diff --git a/unix/uxser.c b/unix/uxser.c
index e77f797a..558cf015 100644
--- a/unix/uxser.c
+++ b/unix/uxser.c
@@ -544,7 +544,7 @@ static int serial_ldisc(void *handle, int option)
return 0;
}
-static void serial_provide_ldisc(void *handle, void *ldisc)
+static void serial_provide_ldisc(void *handle, Ldisc *ldisc)
{
/* This is a stub. */
}
diff --git a/windows/window.c b/windows/window.c
index 1a7bb472..80b94201 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -125,7 +125,7 @@ static int caret_x = -1, caret_y = -1;
static int kbd_codepage;
-static void *ldisc;
+static Ldisc *ldisc;
static Backend *back;
static void *backhandle;
diff --git a/windows/winser.c b/windows/winser.c
index 976f8955..2dd45fac 100644
--- a/windows/winser.c
+++ b/windows/winser.c
@@ -409,7 +409,7 @@ static int serial_ldisc(void *handle, int option)
return 0;
}
-static void serial_provide_ldisc(void *handle, void *ldisc)
+static void serial_provide_ldisc(void *handle, Ldisc *ldisc)
{
/* This is a stub. */
}
From 3814a5cee87e759568f53df4497d48d61e66cf84 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 11 Sep 2018 15:17:16 +0100
Subject: [PATCH 390/607] Make 'LogContext' a typedef visible throughout the
code.
Same principle again - the more of these structures have globally
visible tags (even if the structure contents are still opaque in most
places), the fewer of them I can mistake for each other.
---
cmdgen.c | 2 +-
defs.h | 1 +
logging.c | 40 ++++++++++++++++------------------------
pscp.c | 2 +-
psftp.c | 2 +-
putty.h | 24 ++++++++++++------------
raw.c | 2 +-
rlogin.c | 2 +-
ssh.c | 4 ++--
sshbpp.h | 2 +-
telnet.c | 2 +-
terminal.c | 2 +-
terminal.h | 2 +-
testback.c | 4 ++--
unix/gtkwin.c | 2 +-
unix/uxcons.c | 2 +-
unix/uxpgnt.c | 2 +-
unix/uxplink.c | 2 +-
unix/uxpty.c | 2 +-
unix/uxser.c | 2 +-
windows/wincons.c | 2 +-
windows/winser.c | 2 +-
windows/winstuff.h | 2 +-
23 files changed, 51 insertions(+), 58 deletions(-)
diff --git a/cmdgen.c b/cmdgen.c
index 7cf920f3..034b9154 100644
--- a/cmdgen.c
+++ b/cmdgen.c
@@ -116,7 +116,7 @@ void nonfatal(const char *p, ...)
/*
* Stubs to let everything else link sensibly.
*/
-void log_eventlog(void *handle, const char *event)
+void log_eventlog(LogContext *logctx, const char *event)
{
}
char *x_get_default(const char *key)
diff --git a/defs.h b/defs.h
index f1d73fb5..45b56d60 100644
--- a/defs.h
+++ b/defs.h
@@ -45,6 +45,7 @@ typedef struct Socket_vtable Socket_vtable;
typedef struct Plug_vtable Plug_vtable;
typedef struct Ldisc_tag Ldisc;
+typedef struct LogContext_tag LogContext;
/* Note indirection: for historical reasons (it used to be closer to
* the OS socket type), the type that most code uses for a socket is
diff --git a/logging.c b/logging.c
index 41c5b257..75c457e3 100644
--- a/logging.c
+++ b/logging.c
@@ -12,7 +12,7 @@
#include "putty.h"
/* log session to file stuff ... */
-struct LogContext {
+struct LogContext_tag {
FILE *lgfp;
enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state;
bufchain queue;
@@ -31,7 +31,7 @@ static Filename *xlatlognam(Filename *s, char *hostname, int port,
* isn't open, buffering data if it's in the process of being
* opened asynchronously, etc.
*/
-static void logwrite(struct LogContext *ctx, void *data, int len)
+static void logwrite(LogContext *ctx, void *data, int len)
{
/*
* In state L_CLOSED, we call logfopen, which will set the state
@@ -59,7 +59,7 @@ static void logwrite(struct LogContext *ctx, void *data, int len)
* Convenience wrapper on logwrite() which printf-formats the
* string.
*/
-static void logprintf(struct LogContext *ctx, const char *fmt, ...)
+static void logprintf(LogContext *ctx, const char *fmt, ...)
{
va_list ap;
char *data;
@@ -75,16 +75,16 @@ static void logprintf(struct LogContext *ctx, const char *fmt, ...)
/*
* Flush any open log file.
*/
-void logflush(void *handle) {
- struct LogContext *ctx = (struct LogContext *)handle;
+void logflush(LogContext *ctx)
+{
if (ctx->logtype > 0)
if (ctx->state == L_OPEN)
fflush(ctx->lgfp);
}
-static void logfopen_callback(void *handle, int mode)
+static void logfopen_callback(void *vctx, int mode)
{
- struct LogContext *ctx = (struct LogContext *)handle;
+ LogContext *ctx = (LogContext *)vctx;
char buf[256], *event;
struct tm tm;
const char *fmode;
@@ -160,9 +160,8 @@ static void logfopen_callback(void *handle, int mode)
* file and asking the user whether they want to append, overwrite
* or cancel logging.
*/
-void logfopen(void *handle)
+void logfopen(LogContext *ctx)
{
- struct LogContext *ctx = (struct LogContext *)handle;
struct tm tm;
int mode;
@@ -199,9 +198,8 @@ void logfopen(void *handle)
logfopen_callback(ctx, mode); /* open the file */
}
-void logfclose(void *handle)
+void logfclose(LogContext *ctx)
{
- struct LogContext *ctx = (struct LogContext *)handle;
if (ctx->lgfp) {
fclose(ctx->lgfp);
ctx->lgfp = NULL;
@@ -212,9 +210,8 @@ void logfclose(void *handle)
/*
* Log session traffic.
*/
-void logtraffic(void *handle, unsigned char c, int logmode)
+void logtraffic(LogContext *ctx, unsigned char c, int logmode)
{
- struct LogContext *ctx = (struct LogContext *)handle;
if (ctx->logtype > 0) {
if (ctx->logtype == logmode)
logwrite(ctx, &c, 1);
@@ -230,9 +227,8 @@ void logtraffic(void *handle, unsigned char c, int logmode)
* platforms. Platforms which don't have a meaningful stderr can
* just avoid defining FLAG_STDERR.
*/
-void log_eventlog(void *handle, const char *event)
+void log_eventlog(LogContext *ctx, const char *event)
{
- struct LogContext *ctx = (struct LogContext *)handle;
if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) {
fprintf(stderr, "%s\n", event);
fflush(stderr);
@@ -252,13 +248,12 @@ void log_eventlog(void *handle, const char *event)
* If n_blanks != 0, blank or omit some parts.
* Set of blanking areas must be in increasing order.
*/
-void log_packet(void *handle, int direction, int type,
+void log_packet(LogContext *ctx, int direction, int type,
const char *texttype, const void *data, int len,
int n_blanks, const struct logblank_t *blanks,
const unsigned long *seq,
unsigned downstream_id, const char *additional_log_text)
{
- struct LogContext *ctx = (struct LogContext *)handle;
char dumpdata[80], smalldata[5];
int p = 0, b = 0, omitted = 0;
int output_pos = 0; /* NZ if pending output in dumpdata */
@@ -372,9 +367,9 @@ void log_packet(void *handle, int direction, int type,
logflush(ctx);
}
-void *log_init(void *frontend, Conf *conf)
+LogContext *log_init(void *frontend, Conf *conf)
{
- struct LogContext *ctx = snew(struct LogContext);
+ LogContext *ctx = snew(LogContext);
ctx->lgfp = NULL;
ctx->state = L_CLOSED;
ctx->frontend = frontend;
@@ -385,10 +380,8 @@ void *log_init(void *frontend, Conf *conf)
return ctx;
}
-void log_free(void *handle)
+void log_free(LogContext *ctx)
{
- struct LogContext *ctx = (struct LogContext *)handle;
-
logfclose(ctx);
bufchain_clear(&ctx->queue);
if (ctx->currlogfilename)
@@ -397,9 +390,8 @@ void log_free(void *handle)
sfree(ctx);
}
-void log_reconfig(void *handle, Conf *conf)
+void log_reconfig(LogContext *ctx, Conf *conf)
{
- struct LogContext *ctx = (struct LogContext *)handle;
int reset_logging;
if (!filename_equal(conf_get_filename(ctx->conf, CONF_logfilename),
diff --git a/pscp.c b/pscp.c
index 964e2443..bc2140f5 100644
--- a/pscp.c
+++ b/pscp.c
@@ -340,7 +340,7 @@ static void do_cmd(char *host, char *user, char *cmd)
{
const char *err;
char *realhost;
- void *logctx;
+ LogContext *logctx;
if (host == NULL || host[0] == '\0')
bump("Empty host name");
diff --git a/psftp.c b/psftp.c
index b28c0976..cd419ebe 100644
--- a/psftp.c
+++ b/psftp.c
@@ -2669,7 +2669,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
{
char *host, *realhost;
const char *err;
- void *logctx;
+ LogContext *logctx;
/* Separate host and username */
host = userhost;
diff --git a/putty.h b/putty.h
index 042f66c5..14451756 100644
--- a/putty.h
+++ b/putty.h
@@ -462,7 +462,7 @@ struct backend_tag {
int (*sendok) (void *handle);
int (*ldisc) (void *handle, int);
void (*provide_ldisc) (void *handle, Ldisc *ldisc);
- void (*provide_logctx) (void *handle, void *logctx);
+ void (*provide_logctx) (void *handle, LogContext *logctx);
/*
* back->unthrottle() tells the back end that the front end
* buffer is clearing.
@@ -1110,7 +1110,7 @@ int term_data_untrusted(Terminal *, const void *data, int len);
void term_provide_resize_fn(Terminal *term,
void (*resize_fn)(void *, int, int),
void *resize_ctx);
-void term_provide_logctx(Terminal *term, void *logctx);
+void term_provide_logctx(Terminal *term, LogContext *logctx);
void term_set_focus(Terminal *term, int has_focus);
char *term_get_ttymode(Terminal *term, const char *mode);
int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input);
@@ -1120,14 +1120,14 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);
/*
* Exports from logging.c.
*/
-void *log_init(void *frontend, Conf *conf);
-void log_free(void *logctx);
-void log_reconfig(void *logctx, Conf *conf);
-void logfopen(void *logctx);
-void logfclose(void *logctx);
-void logtraffic(void *logctx, unsigned char c, int logmode);
-void logflush(void *logctx);
-void log_eventlog(void *logctx, const char *string);
+LogContext *log_init(void *frontend, Conf *conf);
+void log_free(LogContext *logctx);
+void log_reconfig(LogContext *logctx, Conf *conf);
+void logfopen(LogContext *logctx);
+void logfclose(LogContext *logctx);
+void logtraffic(LogContext *logctx, unsigned char c, int logmode);
+void logflush(LogContext *logctx);
+void log_eventlog(LogContext *logctx, const char *string);
enum { PKT_INCOMING, PKT_OUTGOING };
enum { PKTLOG_EMIT, PKTLOG_BLANK, PKTLOG_OMIT };
struct logblank_t {
@@ -1135,7 +1135,7 @@ struct logblank_t {
int len;
int type;
};
-void log_packet(void *logctx, int direction, int type,
+void log_packet(LogContext *logctx, int direction, int type,
const char *texttype, const void *data, int len,
int n_blanks, const struct logblank_t *blanks,
const unsigned long *sequence,
@@ -1353,7 +1353,7 @@ int askappend(void *frontend, Filename *filename,
*/
extern int console_batch_mode;
int console_get_userpass_input(prompts_t *p);
-void console_provide_logctx(void *logctx);
+void console_provide_logctx(LogContext *logctx);
int is_interactive(void);
/*
diff --git a/raw.c b/raw.c
index 58a500c7..658bd030 100644
--- a/raw.c
+++ b/raw.c
@@ -279,7 +279,7 @@ static void raw_provide_ldisc(void *handle, Ldisc *ldisc)
/* This is a stub. */
}
-static void raw_provide_logctx(void *handle, void *logctx)
+static void raw_provide_logctx(void *handle, LogContext *logctx)
{
/* This is a stub. */
}
diff --git a/rlogin.c b/rlogin.c
index 7f9093e4..dcba9c0c 100644
--- a/rlogin.c
+++ b/rlogin.c
@@ -371,7 +371,7 @@ static void rlogin_provide_ldisc(void *handle, Ldisc *ldisc)
/* This is a stub. */
}
-static void rlogin_provide_logctx(void *handle, void *logctx)
+static void rlogin_provide_logctx(void *handle, LogContext *logctx)
{
/* This is a stub. */
}
diff --git a/ssh.c b/ssh.c
index d02b7321..609e1155 100644
--- a/ssh.c
+++ b/ssh.c
@@ -701,7 +701,7 @@ struct ssh_tag {
const Plug_vtable *plugvt;
Ldisc *ldisc;
- void *logctx;
+ LogContext *logctx;
unsigned char session_key[32];
int v1_remote_protoflags;
@@ -11434,7 +11434,7 @@ static void ssh_provide_ldisc(void *handle, Ldisc *ldisc)
ssh->ldisc = ldisc;
}
-static void ssh_provide_logctx(void *handle, void *logctx)
+static void ssh_provide_logctx(void *handle, LogContext *logctx)
{
Ssh ssh = (Ssh) handle;
ssh->logctx = logctx;
diff --git a/sshbpp.h b/sshbpp.h
index 5ec3c6ce..c1ac7a13 100644
--- a/sshbpp.h
+++ b/sshbpp.h
@@ -19,7 +19,7 @@ struct BinaryPacketProtocol {
bufchain *in_raw, *out_raw;
PacketQueue *in_pq;
PacketLogSettings *pls;
- void *logctx;
+ LogContext *logctx;
int seen_disconnect;
char *error;
diff --git a/telnet.c b/telnet.c
index 0f958b50..ed585276 100644
--- a/telnet.c
+++ b/telnet.c
@@ -1065,7 +1065,7 @@ static void telnet_provide_ldisc(void *handle, Ldisc *ldisc)
telnet->ldisc = ldisc;
}
-static void telnet_provide_logctx(void *handle, void *logctx)
+static void telnet_provide_logctx(void *handle, LogContext *logctx)
{
/* This is a stub. */
}
diff --git a/terminal.c b/terminal.c
index a368b23f..04e222d0 100644
--- a/terminal.c
+++ b/terminal.c
@@ -6687,7 +6687,7 @@ int term_data_untrusted(Terminal *term, const void *vdata, int len)
return 0; /* assumes that term_data() always returns 0 */
}
-void term_provide_logctx(Terminal *term, void *logctx)
+void term_provide_logctx(Terminal *term, LogContext *logctx)
{
term->logctx = logctx;
}
diff --git a/terminal.h b/terminal.h
index 3eb60939..244ff3bc 100644
--- a/terminal.h
+++ b/terminal.h
@@ -234,7 +234,7 @@ struct terminal_tag {
void *frontend;
- void *logctx;
+ LogContext *logctx;
struct unicode_data *ucsdata;
diff --git a/testback.c b/testback.c
index d84c77db..78b89333 100644
--- a/testback.c
+++ b/testback.c
@@ -50,7 +50,7 @@ static int null_exitcode(void *);
static int null_sendok(void *);
static int null_ldisc(void *, int);
static void null_provide_ldisc(void *, Ldisc *);
-static void null_provide_logctx(void *, void *);
+static void null_provide_logctx(void *, LogContext *);
static void null_unthrottle(void *, int);
static int null_cfg_info(void *);
@@ -161,7 +161,7 @@ static void null_provide_ldisc (void *handle, Ldisc *ldisc) {
}
-static void null_provide_logctx(void *handle, void *logctx) {
+static void null_provide_logctx(void *handle, LogContext *logctx) {
}
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index e28146b4..18bea32f 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -162,7 +162,7 @@ struct gui_data {
Backend *back;
void *backhandle;
Terminal *term;
- void *logctx;
+ LogContext *logctx;
int exited;
struct unicode_data ucsdata;
Conf *conf;
diff --git a/unix/uxcons.c b/unix/uxcons.c
index d0fe986b..e7f531dc 100644
--- a/unix/uxcons.c
+++ b/unix/uxcons.c
@@ -405,7 +405,7 @@ void old_keyfile_warning(void)
postmsg(&cf);
}
-void console_provide_logctx(void *logctx)
+void console_provide_logctx(LogContext *logctx)
{
console_logctx = logctx;
}
diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
index 0e37c1a0..07248d39 100644
--- a/unix/uxpgnt.c
+++ b/unix/uxpgnt.c
@@ -92,7 +92,7 @@ int platform_default_i(const char *name, int def) { return def; }
FontSpec *platform_default_fontspec(const char *name) { return fontspec_new(""); }
Filename *platform_default_filename(const char *name) { return filename_from_str(""); }
char *x_get_default(const char *key) { return NULL; }
-void log_eventlog(void *handle, const char *event) {}
+void log_eventlog(LogContext *logctx, const char *event) {}
int from_backend(void *frontend, int is_stderr, const void *data, int datalen)
{ assert(!"only here to satisfy notional call from backend_socket_log"); }
diff --git a/unix/uxplink.c b/unix/uxplink.c
index a8d35b7a..64e29c31 100644
--- a/unix/uxplink.c
+++ b/unix/uxplink.c
@@ -25,7 +25,7 @@
#define MAX_STDIN_BACKLOG 4096
-static void *logctx;
+static LogContext *logctx;
static struct termios orig_termios;
diff --git a/unix/uxpty.c b/unix/uxpty.c
index 9df61532..b1204a10 100644
--- a/unix/uxpty.c
+++ b/unix/uxpty.c
@@ -1212,7 +1212,7 @@ static void pty_provide_ldisc(void *handle, Ldisc *ldisc)
/* This is a stub. */
}
-static void pty_provide_logctx(void *handle, void *logctx)
+static void pty_provide_logctx(void *handle, LogContext *logctx)
{
/* Pty pty = (Pty)handle; */
/* This is a stub. */
diff --git a/unix/uxser.c b/unix/uxser.c
index 558cf015..50eb83e6 100644
--- a/unix/uxser.c
+++ b/unix/uxser.c
@@ -549,7 +549,7 @@ static void serial_provide_ldisc(void *handle, Ldisc *ldisc)
/* This is a stub. */
}
-static void serial_provide_logctx(void *handle, void *logctx)
+static void serial_provide_logctx(void *handle, LogContext *logctx)
{
/* This is a stub. */
}
diff --git a/windows/wincons.c b/windows/wincons.c
index 4827ddbd..a7cbeeac 100644
--- a/windows/wincons.c
+++ b/windows/wincons.c
@@ -335,7 +335,7 @@ void pgp_fingerprints(void)
" " PGP_PREV_MASTER_KEY_FP "\n", stdout);
}
-void console_provide_logctx(void *logctx)
+void console_provide_logctx(LogContext *logctx)
{
console_logctx = logctx;
}
diff --git a/windows/winser.c b/windows/winser.c
index 2dd45fac..f0ea94bd 100644
--- a/windows/winser.c
+++ b/windows/winser.c
@@ -414,7 +414,7 @@ static void serial_provide_ldisc(void *handle, Ldisc *ldisc)
/* This is a stub. */
}
-static void serial_provide_logctx(void *handle, void *logctx)
+static void serial_provide_logctx(void *handle, LogContext *logctx)
{
/* This is a stub. */
}
diff --git a/windows/winstuff.h b/windows/winstuff.h
index 5dc18fb1..727c4b06 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -240,7 +240,7 @@ void quit_help(HWND hwnd);
* windlg.c. Likewise the saved-sessions list.
*/
GLOBAL Terminal *term;
-GLOBAL void *logctx;
+GLOBAL LogContext *logctx;
/*
* Windows-specific clipboard helper function shared with windlg.c,
From c51fe7c2171a8aa38e2246af22862f6f02d2b3d8 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 11 Sep 2018 15:33:10 +0100
Subject: [PATCH 391/607] Pass the Ssh structure to portfwd.c with a tag.
Again, safer than using a 'void *'.
---
defs.h | 1 +
portfwd.c | 18 +++++++++---------
ssh.c | 3 +--
ssh.h | 5 ++---
4 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/defs.h b/defs.h
index 45b56d60..58a4edff 100644
--- a/defs.h
+++ b/defs.h
@@ -47,6 +47,7 @@ typedef struct Plug_vtable Plug_vtable;
typedef struct Ldisc_tag Ldisc;
typedef struct LogContext_tag LogContext;
+typedef struct ssh_tag *Ssh;
/* Note indirection: for historical reasons (it used to be closer to
* the OS socket type), the type that most code uses for a socket is
* 'Socket', not 'Socket *'. So an implementation of Socket or Plug
diff --git a/portfwd.c b/portfwd.c
index 069bc3bf..e6b06e14 100644
--- a/portfwd.c
+++ b/portfwd.c
@@ -23,8 +23,8 @@ typedef enum {
struct PortForwarding {
struct ssh_channel *c; /* channel structure held by ssh.c */
- void *backhandle; /* instance of SSH backend itself */
- /* Note that backhandle need not be filled in if c is non-NULL */
+ Ssh ssh; /* instance of SSH backend itself */
+ /* Note that ssh need not be filled in if c is non-NULL */
Socket s;
int throttled, throttle_override;
int ready;
@@ -47,7 +47,7 @@ struct PortForwarding {
};
struct PortListener {
- void *backhandle; /* instance of SSH backend itself */
+ Ssh ssh; /* instance of SSH backend itself */
Socket s;
int is_dynamic;
/*
@@ -396,7 +396,7 @@ static void pfd_receive(Plug plug, int urgent, char *data, int len)
*/
sk_set_frozen(pf->s, 1);
- pf->c = new_sock_channel(pf->backhandle, pf);
+ pf->c = new_sock_channel(pf->ssh, pf);
if (pf->c == NULL) {
pfd_close(pf);
return;
@@ -464,7 +464,7 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
pf->throttled = pf->throttle_override = 0;
pf->ready = 1;
pf->c = c;
- pf->backhandle = NULL; /* we shouldn't need this */
+ pf->ssh = NULL; /* we shouldn't need this */
pf->socks_state = SOCKS_NONE;
pf->s = new_connection(addr, dummy_realhost, port,
@@ -497,7 +497,7 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
pf->plugvt = &PortForwarding_plugvt;
pf->c = NULL;
- pf->backhandle = pl->backhandle;
+ pf->ssh = pl->ssh;
pf->s = s = constructor(ctx, &pf->plugvt);
if ((err = sk_socket_error(s)) != NULL) {
@@ -518,7 +518,7 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
pf->socks_state = SOCKS_NONE;
pf->hostname = dupstr(pl->hostname);
pf->port = pl->port;
- pf->c = new_sock_channel(pl->backhandle, pf);
+ pf->c = new_sock_channel(pl->ssh, pf);
if (pf->c == NULL) {
free_portfwd_state(pf);
@@ -547,7 +547,7 @@ static const Plug_vtable PortListener_plugvt = {
* dynamically allocated error message string.
*/
char *pfl_listen(char *desthost, int destport, char *srcaddr,
- int port, void *backhandle, Conf *conf,
+ int port, Ssh ssh, Conf *conf,
struct PortListener **pl_ret, int address_family)
{
const char *err;
@@ -564,7 +564,7 @@ char *pfl_listen(char *desthost, int destport, char *srcaddr,
pl->is_dynamic = FALSE;
} else
pl->is_dynamic = TRUE;
- pl->backhandle = backhandle;
+ pl->ssh = ssh;
pl->s = new_listener(srcaddr, port, &pl->plugvt,
!conf_get_int(conf, CONF_lport_acceptall),
diff --git a/ssh.c b/ssh.c
index 609e1155..1b75fb84 100644
--- a/ssh.c
+++ b/ssh.c
@@ -11293,9 +11293,8 @@ static void ssh_special(void *handle, Telnet_Special code)
}
}
-void *new_sock_channel(void *handle, struct PortForwarding *pf)
+void *new_sock_channel(Ssh ssh, struct PortForwarding *pf)
{
- Ssh ssh = (Ssh) handle;
struct ssh_channel *c;
c = snew(struct ssh_channel);
diff --git a/ssh.h b/ssh.h
index 99ff5b73..6c606abb 100644
--- a/ssh.h
+++ b/ssh.h
@@ -8,7 +8,6 @@
#include "misc.h"
struct ssh_channel;
-typedef struct ssh_tag *Ssh;
extern int sshfwd_write(struct ssh_channel *c, const void *, int);
extern void sshfwd_write_eof(struct ssh_channel *c);
@@ -666,7 +665,7 @@ void logevent(void *, const char *);
struct PortForwarding;
/* Allocate and register a new channel for port forwarding */
-void *new_sock_channel(void *handle, struct PortForwarding *pf);
+void *new_sock_channel(Ssh ssh, struct PortForwarding *pf);
void ssh_send_port_open(void *channel, const char *hostname, int port,
const char *org);
@@ -682,7 +681,7 @@ extern void pfd_override_throttle(struct PortForwarding *, int enable);
struct PortListener;
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
extern char *pfl_listen(char *desthost, int destport, char *srcaddr,
- int port, void *backhandle, Conf *conf,
+ int port, Ssh ssh, Conf *conf,
struct PortListener **pl, int address_family);
extern void pfl_terminate(struct PortListener *);
From eefebaaa9ebce7000e32f1d5ecda13a38ba6bd70 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Tue, 11 Sep 2018 16:23:38 +0100
Subject: [PATCH 392/607] Turn Backend into a sensible classoid.
Nearly every part of the code that ever handles a full backend
structure has historically done it using a pair of pointer variables,
one pointing at a constant struct full of function pointers, and the
other pointing to a 'void *' state object that's passed to each of
those.
While I'm modernising the rest of the code, this seems like a good
time to turn that into the same more or less type-safe and less
cumbersome system as I'm using for other parts of the code, such as
Socket, Plug, BinaryPacketProtocol and so forth: the Backend structure
contains a vtable pointer, and a system of macro wrappers handles
dispatching through that vtable.
---
be_all.c | 2 +-
be_all_s.c | 2 +-
be_none.c | 2 +-
be_nos_s.c | 2 +-
be_nossh.c | 2 +-
be_ssh.c | 2 +-
cmdline.c | 9 ++--
config.c | 23 +++++-----
defs.h | 5 ++-
ldisc.c | 55 ++++++++++++------------
ldisc.h | 3 +-
pinger.c | 10 ++---
pscp.c | 80 +++++++++++++++++------------------
psftp.c | 74 ++++++++++++++++----------------
putty.h | 102 +++++++++++++++++++++++++++------------------
raw.c | 54 ++++++++++++------------
rlogin.c | 61 ++++++++++++++-------------
settings.c | 21 +++++-----
ssh.c | 92 ++++++++++++++++++++--------------------
ssh.h | 2 +-
telnet.c | 69 +++++++++++++++---------------
terminal.c | 21 ++++------
terminal.h | 3 +-
testback.c | 92 ++++++++++++++++++++--------------------
unix/gtkwin.c | 60 +++++++++++++-------------
unix/unix.h | 8 ++--
unix/uxplink.c | 70 +++++++++++++++----------------
unix/uxpterm.c | 2 +-
unix/uxpty.c | 74 ++++++++++++++++----------------
unix/uxputty.c | 16 +++----
unix/uxser.c | 52 ++++++++++++-----------
windows/wincfg.c | 2 +-
windows/window.c | 64 ++++++++++++++--------------
windows/winplink.c | 56 ++++++++++++-------------
windows/winser.c | 52 ++++++++++++-----------
windows/winstuff.h | 2 +-
36 files changed, 634 insertions(+), 612 deletions(-)
diff --git a/be_all.c b/be_all.c
index c58903cc..6a09f748 100644
--- a/be_all.c
+++ b/be_all.c
@@ -22,7 +22,7 @@ const int be_default_protocol = PROT_TELNET;
const int be_default_protocol = PROT_SSH;
#endif
-Backend *backends[] = {
+const struct Backend_vtable *const backends[] = {
&ssh_backend,
&telnet_backend,
&rlogin_backend,
diff --git a/be_all_s.c b/be_all_s.c
index 0ffd0737..d40d7639 100644
--- a/be_all_s.c
+++ b/be_all_s.c
@@ -22,7 +22,7 @@ const int be_default_protocol = PROT_TELNET;
const int be_default_protocol = PROT_SSH;
#endif
-Backend *backends[] = {
+const struct Backend_vtable *const backends[] = {
&ssh_backend,
&telnet_backend,
&rlogin_backend,
diff --git a/be_none.c b/be_none.c
index 688b8daf..7cf52fa1 100644
--- a/be_none.c
+++ b/be_none.c
@@ -6,6 +6,6 @@
#include
#include "putty.h"
-Backend *backends[] = {
+const struct Backend_vtable *const backends[] = {
NULL
};
diff --git a/be_nos_s.c b/be_nos_s.c
index a574ead9..a12125ab 100644
--- a/be_nos_s.c
+++ b/be_nos_s.c
@@ -10,7 +10,7 @@ const int be_default_protocol = PROT_TELNET;
const char *const appname = "PuTTYtel";
-Backend *backends[] = {
+const struct Backend_vtable *const backends[] = {
&telnet_backend,
&rlogin_backend,
&raw_backend,
diff --git a/be_nossh.c b/be_nossh.c
index 33d783a8..463497a2 100644
--- a/be_nossh.c
+++ b/be_nossh.c
@@ -10,7 +10,7 @@ const int be_default_protocol = PROT_TELNET;
const char *const appname = "PuTTYtel";
-Backend *backends[] = {
+const struct Backend_vtable *const backends[] = {
&telnet_backend,
&rlogin_backend,
&raw_backend,
diff --git a/be_ssh.c b/be_ssh.c
index 57d241c2..ec1da386 100644
--- a/be_ssh.c
+++ b/be_ssh.c
@@ -10,7 +10,7 @@
const int be_default_protocol = PROT_SSH;
-Backend *backends[] = {
+const struct Backend_vtable *const backends[] = {
&ssh_backend,
NULL
};
diff --git a/cmdline.c b/cmdline.c
index 0fefb428..0ce3f8a1 100644
--- a/cmdline.c
+++ b/cmdline.c
@@ -275,13 +275,14 @@ int cmdline_process_param(const char *p, char *value,
const char *comma = strchr(p, ',');
if (comma) {
char *prefix = dupprintf("%.*s", (int)(comma - p), p);
- const Backend *b = backend_from_name(prefix);
+ const struct Backend_vtable *vt =
+ backend_vt_from_name(prefix);
- if (b) {
- default_protocol = b->protocol;
+ if (vt) {
+ default_protocol = vt->protocol;
conf_set_int(conf, CONF_protocol,
default_protocol);
- port_override = b->default_port;
+ port_override = vt->default_port;
} else {
cmdline_error("unrecognised protocol prefix '%s'",
prefix);
diff --git a/config.c b/config.c
index 16d3f7d5..fac7dc3d 100644
--- a/config.c
+++ b/config.c
@@ -267,10 +267,10 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg,
conf_set_int(conf, CONF_protocol, newproto);
if (oldproto != newproto) {
- Backend *ob = backend_from_proto(oldproto);
- Backend *nb = backend_from_proto(newproto);
- assert(ob);
- assert(nb);
+ const struct Backend_vtable *ovt = backend_vt_from_proto(oldproto);
+ const struct Backend_vtable *nvt = backend_vt_from_proto(newproto);
+ assert(ovt);
+ assert(nvt);
/* Iff the user hasn't changed the port from the old protocol's
* default, update it with the new protocol's default.
* (This includes a "default" of 0, implying that there is no
@@ -281,8 +281,8 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg,
* getting to the protocol; we want that non-default port
* to be preserved. */
port = conf_get_int(conf, CONF_port);
- if (port == ob->default_port)
- conf_set_int(conf, CONF_port, nb->default_port);
+ if (port == ovt->default_port)
+ conf_set_int(conf, CONF_port, nvt->default_port);
}
dlg_refresh(hp->host, dlg);
dlg_refresh(hp->port, dlg);
@@ -1503,7 +1503,7 @@ void setup_config_box(struct controlbox *b, int midsession,
hp->port = c;
ctrl_columns(s, 1, 100);
- if (!backend_from_proto(PROT_SSH)) {
+ if (!backend_vt_from_proto(PROT_SSH)) {
ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
HELPCTX(session_hostname),
config_protocolbuttons_handler, P(hp),
@@ -1594,7 +1594,7 @@ void setup_config_box(struct controlbox *b, int midsession,
{
const char *sshlogname, *sshrawlogname;
if ((midsession && protocol == PROT_SSH) ||
- (!midsession && backend_from_proto(PROT_SSH))) {
+ (!midsession && backend_vt_from_proto(PROT_SSH))) {
sshlogname = "SSH packets";
sshrawlogname = "SSH packets and raw data";
} else {
@@ -1630,7 +1630,7 @@ void setup_config_box(struct controlbox *b, int midsession,
conf_checkbox_handler, I(CONF_logflush));
if ((midsession && protocol == PROT_SSH) ||
- (!midsession && backend_from_proto(PROT_SSH))) {
+ (!midsession && backend_vt_from_proto(PROT_SSH))) {
s = ctrl_getset(b, "Session/Logging", "ssh",
"Options specific to SSH packet logging");
ctrl_checkbox(s, "Omit known password fields", 'k',
@@ -2107,7 +2107,7 @@ void setup_config_box(struct controlbox *b, int midsession,
#endif
{
- const char *label = backend_from_proto(PROT_SSH) ?
+ const char *label = backend_vt_from_proto(PROT_SSH) ?
"Logical name of remote host (e.g. for SSH key lookup):" :
"Logical name of remote host:";
s = ctrl_getset(b, "Connection", "identity",
@@ -2319,7 +2319,8 @@ void setup_config_box(struct controlbox *b, int midsession,
* when we're not doing SSH.
*/
- if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
+ if (backend_vt_from_proto(PROT_SSH) &&
+ (!midsession || protocol == PROT_SSH)) {
/*
* The Connection/SSH panel.
diff --git a/defs.h b/defs.h
index 58a4edff..43f56dfe 100644
--- a/defs.h
+++ b/defs.h
@@ -21,7 +21,6 @@
#endif
typedef struct conf_tag Conf;
-typedef struct backend_tag Backend;
typedef struct terminal_tag Terminal;
typedef struct Filename Filename;
@@ -44,10 +43,14 @@ typedef struct SockAddr_tag *SockAddr;
typedef struct Socket_vtable Socket_vtable;
typedef struct Plug_vtable Plug_vtable;
+typedef struct Backend Backend;
+typedef struct Backend_vtable Backend_vtable;
+
typedef struct Ldisc_tag Ldisc;
typedef struct LogContext_tag LogContext;
typedef struct ssh_tag *Ssh;
+
/* Note indirection: for historical reasons (it used to be closer to
* the OS socket type), the type that most code uses for a socket is
* 'Socket', not 'Socket *'. So an implementation of Socket or Plug
diff --git a/ldisc.c b/ldisc.c
index ba90e1d0..3596ea63 100644
--- a/ldisc.c
+++ b/ldisc.c
@@ -15,11 +15,11 @@
#define ECHOING (ldisc->localecho == FORCE_ON || \
(ldisc->localecho == AUTO && \
- (ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \
+ (backend_ldisc_option_state(ldisc->backend, LD_ECHO) || \
term_ldisc(ldisc->term, LD_ECHO))))
#define EDITING (ldisc->localedit == FORCE_ON || \
(ldisc->localedit == AUTO && \
- (ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \
+ (backend_ldisc_option_state(ldisc->backend, LD_EDIT) || \
term_ldisc(ldisc->term, LD_EDIT))))
static void c_write(Ldisc *ldisc, const void *buf, int len)
@@ -78,8 +78,7 @@ static void bsb(Ldisc *ldisc, int n)
#define KCTRL(x) ((x^'@') | 0x100)
Ldisc *ldisc_create(Conf *conf, Terminal *term,
- Backend *back, void *backhandle,
- void *frontend)
+ Backend *backend, void *frontend)
{
Ldisc *ldisc = snew(Ldisc);
@@ -88,8 +87,7 @@ Ldisc *ldisc_create(Conf *conf, Terminal *term,
ldisc->bufsiz = 0;
ldisc->quotenext = 0;
- ldisc->back = back;
- ldisc->backhandle = backhandle;
+ ldisc->backend = backend;
ldisc->term = term;
ldisc->frontend = frontend;
@@ -98,8 +96,8 @@ Ldisc *ldisc_create(Conf *conf, Terminal *term,
/* Link ourselves into the backend and the terminal */
if (term)
term->ldisc = ldisc;
- if (back)
- back->provide_ldisc(backhandle, ldisc);
+ if (backend)
+ backend_provide_ldisc(backend, ldisc);
return ldisc;
}
@@ -117,8 +115,8 @@ void ldisc_free(Ldisc *ldisc)
{
if (ldisc->term)
ldisc->term->ldisc = NULL;
- if (ldisc->back)
- ldisc->back->provide_ldisc(ldisc->backhandle, NULL);
+ if (ldisc->backend)
+ backend_provide_ldisc(ldisc->backend, NULL);
if (ldisc->buf)
sfree(ldisc->buf);
sfree(ldisc);
@@ -219,7 +217,7 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, int interactive)
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
ldisc->buflen--;
}
- ldisc->back->special(ldisc->backhandle, TS_EL);
+ backend_special(ldisc->backend, TS_EL);
/*
* We don't send IP, SUSP or ABORT if the user has
* configured telnet specials off! This breaks
@@ -228,11 +226,11 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, int interactive)
if (!ldisc->telnet_keyboard)
goto default_case;
if (c == CTRL('C'))
- ldisc->back->special(ldisc->backhandle, TS_IP);
+ backend_special(ldisc->backend, TS_IP);
if (c == CTRL('Z'))
- ldisc->back->special(ldisc->backhandle, TS_SUSP);
+ backend_special(ldisc->backend, TS_SUSP);
if (c == CTRL('\\'))
- ldisc->back->special(ldisc->backhandle, TS_ABORT);
+ backend_special(ldisc->backend, TS_ABORT);
break;
case CTRL('R'): /* redraw line */
if (ECHOING) {
@@ -247,9 +245,9 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, int interactive)
break;
case CTRL('D'): /* logout or send */
if (ldisc->buflen == 0) {
- ldisc->back->special(ldisc->backhandle, TS_EOF);
+ backend_special(ldisc->backend, TS_EOF);
} else {
- ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);
+ backend_send(ldisc->backend, ldisc->buf, ldisc->buflen);
ldisc->buflen = 0;
}
break;
@@ -285,13 +283,14 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, int interactive)
/* FALLTHROUGH */
case KCTRL('M'): /* send with newline */
if (ldisc->buflen > 0)
- ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);
+ backend_send(ldisc->backend,
+ ldisc->buf, ldisc->buflen);
if (ldisc->protocol == PROT_RAW)
- ldisc->back->send(ldisc->backhandle, "\r\n", 2);
+ backend_send(ldisc->backend, "\r\n", 2);
else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
- ldisc->back->special(ldisc->backhandle, TS_EOL);
+ backend_special(ldisc->backend, TS_EOL);
else
- ldisc->back->send(ldisc->backhandle, "\r", 1);
+ backend_send(ldisc->backend, "\r", 1);
if (ECHOING)
c_write(ldisc, "\r\n", 2);
ldisc->buflen = 0;
@@ -313,7 +312,7 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, int interactive)
}
} else {
if (ldisc->buflen != 0) {
- ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);
+ backend_send(ldisc->backend, ldisc->buf, ldisc->buflen);
while (ldisc->buflen > 0) {
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
ldisc->buflen--;
@@ -326,33 +325,33 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, int interactive)
switch (buf[0]) {
case CTRL('M'):
if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
- ldisc->back->special(ldisc->backhandle, TS_EOL);
+ backend_special(ldisc->backend, TS_EOL);
else
- ldisc->back->send(ldisc->backhandle, "\r", 1);
+ backend_send(ldisc->backend, "\r", 1);
break;
case CTRL('?'):
case CTRL('H'):
if (ldisc->telnet_keyboard) {
- ldisc->back->special(ldisc->backhandle, TS_EC);
+ backend_special(ldisc->backend, TS_EC);
break;
}
case CTRL('C'):
if (ldisc->telnet_keyboard) {
- ldisc->back->special(ldisc->backhandle, TS_IP);
+ backend_special(ldisc->backend, TS_IP);
break;
}
case CTRL('Z'):
if (ldisc->telnet_keyboard) {
- ldisc->back->special(ldisc->backhandle, TS_SUSP);
+ backend_special(ldisc->backend, TS_SUSP);
break;
}
default:
- ldisc->back->send(ldisc->backhandle, buf, len);
+ backend_send(ldisc->backend, buf, len);
break;
}
} else
- ldisc->back->send(ldisc->backhandle, buf, len);
+ backend_send(ldisc->backend, buf, len);
}
}
}
diff --git a/ldisc.h b/ldisc.h
index 44f5772f..af7afde3 100644
--- a/ldisc.h
+++ b/ldisc.h
@@ -10,8 +10,7 @@
struct Ldisc_tag {
Terminal *term;
- Backend *back;
- void *backhandle;
+ Backend *backend;
void *frontend;
/*
diff --git a/pinger.c b/pinger.c
index d8f110ac..c83b71d2 100644
--- a/pinger.c
+++ b/pinger.c
@@ -9,8 +9,7 @@ struct pinger_tag {
int interval;
int pending;
unsigned long when_set, next;
- Backend *back;
- void *backhandle;
+ Backend *backend;
};
static void pinger_schedule(Pinger pinger);
@@ -20,7 +19,7 @@ static void pinger_timer(void *ctx, unsigned long now)
Pinger pinger = (Pinger)ctx;
if (pinger->pending && now == pinger->next) {
- pinger->back->special(pinger->backhandle, TS_PING);
+ backend_special(pinger->backend, TS_PING);
pinger->pending = FALSE;
pinger_schedule(pinger);
}
@@ -45,14 +44,13 @@ static void pinger_schedule(Pinger pinger)
}
}
-Pinger pinger_new(Conf *conf, Backend *back, void *backhandle)
+Pinger pinger_new(Conf *conf, Backend *backend)
{
Pinger pinger = snew(struct pinger_tag);
pinger->interval = conf_get_int(conf, CONF_ping_interval);
pinger->pending = FALSE;
- pinger->back = back;
- pinger->backhandle = backhandle;
+ pinger->backend = backend;
pinger_schedule(pinger);
return pinger;
diff --git a/pscp.c b/pscp.c
index bc2140f5..854e6815 100644
--- a/pscp.c
+++ b/pscp.c
@@ -43,8 +43,7 @@ static int fallback_cmd_is_sftp = 0;
static int using_sftp = 0;
static int uploading = 0;
-static Backend *back;
-static void *backhandle;
+static Backend *backend;
static Conf *conf;
int sent_eof = FALSE;
@@ -247,7 +246,7 @@ static int ssh_scp_recv(void *buf, int len)
}
while (outlen > 0) {
- if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0)
+ if (backend_exitcode(backend) >= 0 || ssh_sftp_loop_iteration() < 0)
return 0; /* doom */
}
@@ -259,8 +258,8 @@ static int ssh_scp_recv(void *buf, int len)
*/
static void ssh_scp_init(void)
{
- while (!back->sendok(backhandle)) {
- if (back->exitcode(backhandle) >= 0) {
+ while (!backend_sendok(backend)) {
+ if (backend_exitcode(backend) >= 0) {
errs++;
return;
}
@@ -271,7 +270,7 @@ static void ssh_scp_init(void)
}
/* Work out which backend we ended up using. */
- if (!ssh_fallback_cmd(backhandle))
+ if (!ssh_fallback_cmd(backend))
using_sftp = main_cmd_is_sftp;
else
using_sftp = fallback_cmd_is_sftp;
@@ -300,9 +299,9 @@ static void bump(const char *fmt, ...)
sfree(str2);
errs++;
- if (back != NULL && back->connected(backhandle)) {
+ if (backend && backend_connected(backend)) {
char ch;
- back->special(backhandle, TS_EOF);
+ backend_special(backend, TS_EOF);
sent_eof = TRUE;
ssh_scp_recv(&ch, 1);
}
@@ -498,21 +497,19 @@ static void do_cmd(char *host, char *user, char *cmd)
}
conf_set_int(conf, CONF_nopty, TRUE);
- back = &ssh_backend;
-
logctx = log_init(NULL, conf);
console_provide_logctx(logctx);
platform_psftp_pre_conn_setup();
- err = back->init(NULL, &backhandle, conf,
- conf_get_str(conf, CONF_host),
- conf_get_int(conf, CONF_port),
- &realhost, 0,
- conf_get_int(conf, CONF_tcp_keepalives));
+ err = backend_init(&ssh_backend, NULL, &backend, conf,
+ conf_get_str(conf, CONF_host),
+ conf_get_int(conf, CONF_port),
+ &realhost, 0,
+ conf_get_int(conf, CONF_tcp_keepalives));
if (err != NULL)
bump("ssh_init: %s", err);
- back->provide_logctx(backhandle, logctx);
+ backend_provide_logctx(backend, logctx);
ssh_scp_init();
if (verbose && realhost != NULL && errs == 0)
tell_user(stderr, "Connected to %s", realhost);
@@ -644,12 +641,12 @@ int sftp_recvdata(char *buf, int len)
}
int sftp_senddata(char *buf, int len)
{
- back->send(backhandle, buf, len);
+ backend_send(backend, buf, len);
return 1;
}
int sftp_sendbuffer(void)
{
- return back->sendbuffer(backhandle);
+ return backend_sendbuffer(backend);
}
/* ----------------------------------------------------------------------
@@ -806,8 +803,8 @@ int scp_send_errmsg(char *str)
if (using_sftp) {
/* do nothing; we never need to send our errors to the server */
} else {
- back->send(backhandle, "\001", 1);/* scp protocol error prefix */
- back->send(backhandle, str, strlen(str));
+ backend_send(backend, "\001", 1);/* scp protocol error prefix */
+ backend_send(backend, str, strlen(str));
}
return 0; /* can't fail */
}
@@ -822,7 +819,7 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime)
} else {
char buf[80];
sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
- back->send(backhandle, buf, strlen(buf));
+ backend_send(backend, buf, strlen(buf));
return response();
}
}
@@ -869,9 +866,9 @@ int scp_send_filename(const char *name, uint64 size, int permissions)
if (permissions < 0)
permissions = 0644;
sprintf(buf, "C%04o %s ", (int)(permissions & 07777), sizestr);
- back->send(backhandle, buf, strlen(buf));
- back->send(backhandle, name, strlen(name));
- back->send(backhandle, "\n", 1);
+ backend_send(backend, buf, strlen(buf));
+ backend_send(backend, name, strlen(name));
+ backend_send(backend, "\n", 1);
return response();
}
}
@@ -903,7 +900,7 @@ int scp_send_filedata(char *data, int len)
scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, len);
return 0;
} else {
- int bufsize = back->send(backhandle, data, len);
+ int bufsize = backend_send(backend, data, len);
/*
* If the network transfer is backing up - that is, the
@@ -914,7 +911,7 @@ int scp_send_filedata(char *data, int len)
while (bufsize > MAX_SCP_BUFSIZE) {
if (ssh_sftp_loop_iteration() < 0)
return 1;
- bufsize = back->sendbuffer(backhandle);
+ bufsize = backend_sendbuffer(backend);
}
return 0;
@@ -963,7 +960,7 @@ int scp_send_finish(void)
scp_has_times = 0;
return 0;
} else {
- back->send(backhandle, "", 1);
+ backend_send(backend, "", 1);
return response();
}
}
@@ -1033,9 +1030,9 @@ int scp_send_dirname(const char *name, int modes)
} else {
char buf[40];
sprintf(buf, "D%04o 0 ", modes);
- back->send(backhandle, buf, strlen(buf));
- back->send(backhandle, name, strlen(name));
- back->send(backhandle, "\n", 1);
+ backend_send(backend, buf, strlen(buf));
+ backend_send(backend, name, strlen(name));
+ backend_send(backend, "\n", 1);
return response();
}
}
@@ -1046,7 +1043,7 @@ int scp_send_enddir(void)
sfree(scp_sftp_remotepath);
return 0;
} else {
- back->send(backhandle, "E\n", 2);
+ backend_send(backend, "E\n", 2);
return response();
}
}
@@ -1144,7 +1141,7 @@ int scp_sink_setup(const char *source, int preserve, int recursive)
int scp_sink_init(void)
{
if (!using_sftp) {
- back->send(backhandle, "", 1);
+ backend_send(backend, "", 1);
}
return 0;
}
@@ -1457,14 +1454,14 @@ int scp_get_sink_action(struct scp_sink_action *act)
case '\02': /* fatal error */
bump("%s", act->buf);
case 'E':
- back->send(backhandle, "", 1);
+ backend_send(backend, "", 1);
act->action = SCP_SINK_ENDDIR;
return 0;
case 'T':
if (sscanf(act->buf, "%lu %*d %lu %*d",
&act->mtime, &act->atime) == 2) {
act->settime = 1;
- back->send(backhandle, "", 1);
+ backend_send(backend, "", 1);
continue; /* go round again */
}
bump("Protocol error: Illegal time format");
@@ -1521,7 +1518,7 @@ int scp_accept_filexfer(void)
sfree(scp_sftp_currentname);
return 0;
} else {
- back->send(backhandle, "", 1);
+ backend_send(backend, "", 1);
return 0; /* can't fail */
}
}
@@ -1606,7 +1603,7 @@ int scp_finish_filerecv(void)
fxp_close_recv(pktin, req);
return 0;
} else {
- back->send(backhandle, "", 1);
+ backend_send(backend, "", 1);
return response();
}
}
@@ -2355,7 +2352,7 @@ int psftp_main(int argc, char *argv[])
}
argc -= i;
argv += i;
- back = NULL;
+ backend = NULL;
if (list) {
if (argc != 1)
@@ -2375,9 +2372,9 @@ int psftp_main(int argc, char *argv[])
tolocal(argc, argv);
}
- if (back != NULL && back->connected(backhandle)) {
+ if (backend && backend_connected(backend)) {
char ch;
- back->special(backhandle, TS_EOF);
+ backend_special(backend, TS_EOF);
sent_eof = TRUE;
ssh_scp_recv(&ch, 1);
}
@@ -2385,9 +2382,8 @@ int psftp_main(int argc, char *argv[])
cmdline_cleanup();
console_provide_logctx(NULL);
- back->free(backhandle);
- backhandle = NULL;
- back = NULL;
+ backend_free(backend);
+ backend = NULL;
sk_cleanup();
return (errs == 0 ? 0 : 1);
}
diff --git a/psftp.c b/psftp.c
index cd419ebe..c2d23832 100644
--- a/psftp.c
+++ b/psftp.c
@@ -34,8 +34,7 @@ void do_sftp_cleanup();
*/
char *pwd, *homedir;
-static Backend *back;
-static void *backhandle;
+static Backend *backend;
static Conf *conf;
int sent_eof = FALSE;
@@ -967,14 +966,14 @@ int sftp_cmd_quit(struct sftp_command *cmd)
int sftp_cmd_close(struct sftp_command *cmd)
{
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
- if (back != NULL && back->connected(backhandle)) {
+ if (backend_connected(backend)) {
char ch;
- back->special(backhandle, TS_EOF);
+ backend_special(backend, TS_EOF);
sent_eof = TRUE;
sftp_recvdata(&ch, 1);
}
@@ -999,7 +998,7 @@ int sftp_cmd_ls(struct sftp_command *cmd)
struct sftp_request *req;
int i;
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1123,7 +1122,7 @@ int sftp_cmd_cd(struct sftp_command *cmd)
struct sftp_request *req;
char *dir;
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1164,7 +1163,7 @@ int sftp_cmd_cd(struct sftp_command *cmd)
*/
int sftp_cmd_pwd(struct sftp_command *cmd)
{
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1188,7 +1187,7 @@ int sftp_general_get(struct sftp_command *cmd, int restart, int multiple)
int i, ret;
int recurse = FALSE;
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1304,7 +1303,7 @@ int sftp_general_put(struct sftp_command *cmd, int restart, int multiple)
int i, ret;
int recurse = FALSE;
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1405,7 +1404,7 @@ int sftp_cmd_mkdir(struct sftp_command *cmd)
int result;
int i, ret;
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1463,7 +1462,7 @@ int sftp_cmd_rmdir(struct sftp_command *cmd)
{
int i, ret;
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1504,7 +1503,7 @@ int sftp_cmd_rm(struct sftp_command *cmd)
{
int i, ret;
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1597,7 +1596,7 @@ int sftp_cmd_mv(struct sftp_command *cmd)
struct sftp_context_mv actx, *ctx = &actx;
int i, ret;
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1689,7 +1688,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd)
int i, ret;
struct sftp_context_chmod actx, *ctx = &actx;
- if (back == NULL) {
+ if (!backend) {
not_connected();
return 0;
}
@@ -1815,7 +1814,7 @@ static int sftp_cmd_open(struct sftp_command *cmd)
{
int portnumber;
- if (back != NULL) {
+ if (backend) {
printf("psftp: already connected\n");
return 0;
}
@@ -1835,7 +1834,7 @@ static int sftp_cmd_open(struct sftp_command *cmd)
portnumber = 0;
if (psftp_connect(cmd->words[1], NULL, portnumber)) {
- back = NULL; /* connection is already closed */
+ backend = NULL; /* connection is already closed */
return -1; /* this is fatal */
}
do_sftp_init();
@@ -2214,7 +2213,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
printf("psftp> ");
line = fgetline(fp);
} else {
- line = ssh_sftp_get_cmdline("psftp> ", back == NULL);
+ line = ssh_sftp_get_cmdline("psftp> ", !backend);
}
if (!line || !*line) {
@@ -2355,14 +2354,13 @@ static int do_sftp_init(void)
void do_sftp_cleanup()
{
char ch;
- if (back) {
- back->special(backhandle, TS_EOF);
+ if (backend) {
+ backend_special(backend, TS_EOF);
sent_eof = TRUE;
sftp_recvdata(&ch, 1);
- back->free(backhandle);
+ backend_free(backend);
sftp_cleanup_request();
- back = NULL;
- backhandle = NULL;
+ backend = NULL;
}
if (pwd) {
sfree(pwd);
@@ -2602,7 +2600,7 @@ int sftp_recvdata(char *buf, int len)
}
while (outlen > 0) {
- if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0)
+ if (backend_exitcode(backend) >= 0 || ssh_sftp_loop_iteration() < 0)
return 0; /* doom */
}
@@ -2610,12 +2608,12 @@ int sftp_recvdata(char *buf, int len)
}
int sftp_senddata(char *buf, int len)
{
- back->send(backhandle, buf, len);
+ backend_send(backend, buf, len);
return 1;
}
int sftp_sendbuffer(void)
{
- return back->sendbuffer(backhandle);
+ return backend_sendbuffer(backend);
}
/*
@@ -2826,25 +2824,23 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
"exec sftp-server");
conf_set_int(conf, CONF_ssh_subsys2, FALSE);
- back = &ssh_backend;
-
logctx = log_init(NULL, conf);
console_provide_logctx(logctx);
platform_psftp_pre_conn_setup();
- err = back->init(NULL, &backhandle, conf,
- conf_get_str(conf, CONF_host),
- conf_get_int(conf, CONF_port),
- &realhost, 0,
- conf_get_int(conf, CONF_tcp_keepalives));
+ err = backend_init(&ssh_backend, NULL, &backend, conf,
+ conf_get_str(conf, CONF_host),
+ conf_get_int(conf, CONF_port),
+ &realhost, 0,
+ conf_get_int(conf, CONF_tcp_keepalives));
if (err != NULL) {
fprintf(stderr, "ssh_init: %s\n", err);
return 1;
}
- back->provide_logctx(backhandle, logctx);
- while (!back->sendok(backhandle)) {
- if (back->exitcode(backhandle) >= 0)
+ backend_provide_logctx(backend, logctx);
+ while (!backend_sendok(backend)) {
+ if (backend_exitcode(backend) >= 0)
return 1;
if (ssh_sftp_loop_iteration() < 0) {
fprintf(stderr, "ssh_init: error during SSH connection setup\n");
@@ -2945,7 +2941,7 @@ int psftp_main(int argc, char *argv[])
}
argc -= i;
argv += i;
- back = NULL;
+ backend = NULL;
/*
* If the loaded session provides a hostname, and a hostname has not
@@ -2975,9 +2971,9 @@ int psftp_main(int argc, char *argv[])
ret = do_sftp(mode, modeflags, batchfile);
- if (back != NULL && back->connected(backhandle)) {
+ if (backend && backend_connected(backend)) {
char ch;
- back->special(backhandle, TS_EOF);
+ backend_special(backend, TS_EOF);
sent_eof = TRUE;
sftp_recvdata(&ch, 1);
}
diff --git a/putty.h b/putty.h
index 14451756..2f53d645 100644
--- a/putty.h
+++ b/putty.h
@@ -441,43 +441,67 @@ enum {
ADDRTYPE_UNSPEC, ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_NAME
};
-struct backend_tag {
- const char *(*init) (void *frontend_handle, void **backend_handle,
+struct Backend {
+ const Backend_vtable *vt;
+};
+struct Backend_vtable {
+ const char *(*init) (void *frontend_handle, Backend **backend_out,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive);
- void (*free) (void *handle);
- /* back->reconfig() passes in a replacement configuration. */
- void (*reconfig) (void *handle, Conf *conf);
- /* back->send() returns the current amount of buffered data. */
- int (*send) (void *handle, const char *buf, int len);
- /* back->sendbuffer() does the same thing but without attempting a send */
- int (*sendbuffer) (void *handle);
- void (*size) (void *handle, int width, int height);
- void (*special) (void *handle, Telnet_Special code);
- const struct telnet_special *(*get_specials) (void *handle);
- int (*connected) (void *handle);
- int (*exitcode) (void *handle);
- /* If back->sendok() returns FALSE, data sent to it from the frontend
- * may be lost. */
- int (*sendok) (void *handle);
- int (*ldisc) (void *handle, int);
- void (*provide_ldisc) (void *handle, Ldisc *ldisc);
- void (*provide_logctx) (void *handle, LogContext *logctx);
- /*
- * back->unthrottle() tells the back end that the front end
- * buffer is clearing.
- */
- void (*unthrottle) (void *handle, int);
- int (*cfg_info) (void *handle);
+
+ void (*free) (Backend *be);
+ /* Pass in a replacement configuration. */
+ void (*reconfig) (Backend *be, Conf *conf);
+ /* send() returns the current amount of buffered data. */
+ int (*send) (Backend *be, const char *buf, int len);
+ /* sendbuffer() does the same thing but without attempting a send */
+ int (*sendbuffer) (Backend *be);
+ void (*size) (Backend *be, int width, int height);
+ void (*special) (Backend *be, Telnet_Special code);
+ const struct telnet_special *(*get_specials) (Backend *be);
+ int (*connected) (Backend *be);
+ int (*exitcode) (Backend *be);
+ /* If back->sendok() returns FALSE, the backend doesn't currently
+ * want input data, so the frontend should avoid acquiring any if
+ * possible (passing back-pressure on to its sender). */
+ int (*sendok) (Backend *be);
+ int (*ldisc_option_state) (Backend *be, int);
+ void (*provide_ldisc) (Backend *be, Ldisc *ldisc);
+ void (*provide_logctx) (Backend *be, LogContext *logctx);
+ /* Tells the back end that the front end buffer is clearing. */
+ void (*unthrottle) (Backend *be, int bufsize);
+ int (*cfg_info) (Backend *be);
+
/* Only implemented in the SSH protocol: check whether a
* connection-sharing upstream exists for a given configuration. */
int (*test_for_upstream)(const char *host, int port, Conf *conf);
+
const char *name;
int protocol;
int default_port;
};
-extern Backend *backends[];
+#define backend_init(vt, fe, out, conf, host, port, rhost, nd, ka) \
+ ((vt)->init(fe, out, conf, host, port, rhost, nd, ka))
+#define backend_free(be) ((be)->vt->free(be))
+#define backend_reconfig(be, conf) ((be)->vt->reconfig(be, conf))
+#define backend_send(be, buf, len) ((be)->vt->send(be, buf, len))
+#define backend_sendbuffer(be) ((be)->vt->sendbuffer(be))
+#define backend_size(be, w, h) ((be)->vt->size(be, w, h))
+#define backend_special(be, code) ((be)->vt->special(be, code))
+#define backend_get_specials(be) ((be)->vt->get_specials(be))
+#define backend_connected(be) ((be)->vt->connected(be))
+#define backend_exitcode(be) ((be)->vt->exitcode(be))
+#define backend_sendok(be) ((be)->vt->sendok(be))
+#define backend_ldisc_option_state(be, opt) \
+ ((be)->vt->ldisc_option_state(be, opt))
+#define backend_provide_ldisc(be, ldisc) ((be)->vt->provide_ldisc(be, ldisc))
+#define backend_provide_logctx(be, logctx) \
+ ((be)->vt->provide_logctx(be, logctx))
+#define backend_unthrottle(be, bufsize) ((be)->vt->unthrottle(be, bufsize))
+#define backend_cfg_info(be) ((be)->vt->cfg_info(be))
+
+extern const struct Backend_vtable *const backends[];
/*
* Suggested default protocol provided by the backend link module.
@@ -1046,8 +1070,8 @@ void random_destroy_seed(void);
/*
* Exports from settings.c.
*/
-Backend *backend_from_name(const char *name);
-Backend *backend_from_proto(int proto);
+const struct Backend_vtable *backend_vt_from_name(const char *name);
+const struct Backend_vtable *backend_vt_from_proto(int proto);
char *get_remote_username(Conf *conf); /* dynamically allocated */
char *save_settings(const char *section, Conf *conf);
void save_open_settings(void *sesskey, Conf *conf);
@@ -1107,9 +1131,7 @@ void term_request_paste(Terminal *, int clipboard);
void term_seen_key_event(Terminal *);
int term_data(Terminal *, int is_stderr, const void *data, int len);
int term_data_untrusted(Terminal *, const void *data, int len);
-void term_provide_resize_fn(Terminal *term,
- void (*resize_fn)(void *, int, int),
- void *resize_ctx);
+void term_provide_backend(Terminal *term, Backend *backend);
void term_provide_logctx(Terminal *term, LogContext *logctx);
void term_set_focus(Terminal *term, int has_focus);
char *term_get_ttymode(Terminal *term, const char *mode);
@@ -1145,36 +1167,36 @@ void log_packet(LogContext *logctx, int direction, int type,
* Exports from testback.c
*/
-extern Backend null_backend;
-extern Backend loop_backend;
+extern const struct Backend_vtable null_backend;
+extern const struct Backend_vtable loop_backend;
/*
* Exports from raw.c.
*/
-extern Backend raw_backend;
+extern const struct Backend_vtable raw_backend;
/*
* Exports from rlogin.c.
*/
-extern Backend rlogin_backend;
+extern const struct Backend_vtable rlogin_backend;
/*
* Exports from telnet.c.
*/
-extern Backend telnet_backend;
+extern const struct Backend_vtable telnet_backend;
/*
* Exports from ssh.c.
*/
-extern Backend ssh_backend;
+extern const struct Backend_vtable ssh_backend;
/*
* Exports from ldisc.c.
*/
-Ldisc *ldisc_create(Conf *, Terminal *, Backend *, void *, void *);
+Ldisc *ldisc_create(Conf *, Terminal *, Backend *, void *);
void ldisc_configure(Ldisc *, Conf *);
void ldisc_free(Ldisc *);
void ldisc_send(Ldisc *, const void *buf, int len, int interactive);
@@ -1205,7 +1227,7 @@ void random_unref(void);
* Exports from pinger.c.
*/
typedef struct pinger_tag *Pinger;
-Pinger pinger_new(Conf *conf, Backend *back, void *backhandle);
+Pinger pinger_new(Conf *conf, Backend *backend);
void pinger_reconfig(Pinger, Conf *oldconf, Conf *newconf);
void pinger_free(Pinger);
diff --git a/raw.c b/raw.c
index 658bd030..56eb0385 100644
--- a/raw.c
+++ b/raw.c
@@ -20,9 +20,10 @@ typedef struct raw_backend_data {
Conf *conf;
const Plug_vtable *plugvt;
+ Backend backend;
} *Raw;
-static void raw_size(void *handle, int width, int height);
+static void raw_size(Backend *be, int width, int height);
static void c_write(Raw raw, const void *buf, int len)
{
@@ -116,7 +117,7 @@ static const Plug_vtable Raw_plugvt = {
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *raw_init(void *frontend_handle, void **backend_handle,
+static const char *raw_init(void *frontend_handle, Backend **backend_handle,
Conf *conf,
const char *host, int port, char **realhost,
int nodelay, int keepalive)
@@ -129,9 +130,10 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
raw = snew(struct raw_backend_data);
raw->plugvt = &Raw_plugvt;
+ raw->backend.vt = &raw_backend;
raw->s = NULL;
raw->closed_on_socket_error = FALSE;
- *backend_handle = raw;
+ *backend_handle = &raw->backend;
raw->sent_console_eof = raw->sent_socket_eof = FALSE;
raw->bufsize = 0;
raw->session_started = FALSE;
@@ -176,9 +178,9 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
return NULL;
}
-static void raw_free(void *handle)
+static void raw_free(Backend *be)
{
- Raw raw = (Raw) handle;
+ Raw raw = FROMFIELD(be, struct raw_backend_data, backend);
if (raw->s)
sk_close(raw->s);
@@ -189,16 +191,16 @@ static void raw_free(void *handle)
/*
* Stub routine (we don't have any need to reconfigure this backend).
*/
-static void raw_reconfig(void *handle, Conf *conf)
+static void raw_reconfig(Backend *be, Conf *conf)
{
}
/*
* Called to send data down the raw connection.
*/
-static int raw_send(void *handle, const char *buf, int len)
+static int raw_send(Backend *be, const char *buf, int len)
{
- Raw raw = (Raw) handle;
+ Raw raw = FROMFIELD(be, struct raw_backend_data, backend);
if (raw->s == NULL)
return 0;
@@ -211,16 +213,16 @@ static int raw_send(void *handle, const char *buf, int len)
/*
* Called to query the current socket sendability status.
*/
-static int raw_sendbuffer(void *handle)
+static int raw_sendbuffer(Backend *be)
{
- Raw raw = (Raw) handle;
+ Raw raw = FROMFIELD(be, struct raw_backend_data, backend);
return raw->bufsize;
}
/*
* Called to set the size of the window
*/
-static void raw_size(void *handle, int width, int height)
+static void raw_size(Backend *be, int width, int height)
{
/* Do nothing! */
return;
@@ -229,9 +231,9 @@ static void raw_size(void *handle, int width, int height)
/*
* Send raw special codes. We only handle outgoing EOF here.
*/
-static void raw_special(void *handle, Telnet_Special code)
+static void raw_special(Backend *be, Telnet_Special code)
{
- Raw raw = (Raw) handle;
+ Raw raw = FROMFIELD(be, struct raw_backend_data, backend);
if (code == TS_EOF && raw->s) {
sk_write_eof(raw->s);
raw->sent_socket_eof= TRUE;
@@ -245,48 +247,48 @@ static void raw_special(void *handle, Telnet_Special code)
* Return a list of the special codes that make sense in this
* protocol.
*/
-static const struct telnet_special *raw_get_specials(void *handle)
+static const struct telnet_special *raw_get_specials(Backend *be)
{
return NULL;
}
-static int raw_connected(void *handle)
+static int raw_connected(Backend *be)
{
- Raw raw = (Raw) handle;
+ Raw raw = FROMFIELD(be, struct raw_backend_data, backend);
return raw->s != NULL;
}
-static int raw_sendok(void *handle)
+static int raw_sendok(Backend *be)
{
return 1;
}
-static void raw_unthrottle(void *handle, int backlog)
+static void raw_unthrottle(Backend *be, int backlog)
{
- Raw raw = (Raw) handle;
+ Raw raw = FROMFIELD(be, struct raw_backend_data, backend);
sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG);
}
-static int raw_ldisc(void *handle, int option)
+static int raw_ldisc(Backend *be, int option)
{
if (option == LD_EDIT || option == LD_ECHO)
return 1;
return 0;
}
-static void raw_provide_ldisc(void *handle, Ldisc *ldisc)
+static void raw_provide_ldisc(Backend *be, Ldisc *ldisc)
{
/* This is a stub. */
}
-static void raw_provide_logctx(void *handle, LogContext *logctx)
+static void raw_provide_logctx(Backend *be, LogContext *logctx)
{
/* This is a stub. */
}
-static int raw_exitcode(void *handle)
+static int raw_exitcode(Backend *be)
{
- Raw raw = (Raw) handle;
+ Raw raw = FROMFIELD(be, struct raw_backend_data, backend);
if (raw->s != NULL)
return -1; /* still connected */
else if (raw->closed_on_socket_error)
@@ -299,12 +301,12 @@ static int raw_exitcode(void *handle)
/*
* cfg_info for Raw does nothing at all.
*/
-static int raw_cfg_info(void *handle)
+static int raw_cfg_info(Backend *be)
{
return 0;
}
-Backend raw_backend = {
+const struct Backend_vtable raw_backend = {
raw_init,
raw_free,
raw_reconfig,
diff --git a/rlogin.c b/rlogin.c
index dcba9c0c..978e3084 100644
--- a/rlogin.c
+++ b/rlogin.c
@@ -26,10 +26,9 @@ typedef struct rlogin_tag {
prompts_t *prompt;
const Plug_vtable *plugvt;
+ Backend backend;
} *Rlogin;
-static void rlogin_size(void *handle, int width, int height);
-
static void c_write(Rlogin rlogin, const void *buf, int len)
{
int backlog = from_backend(rlogin->frontend, 0, buf, len);
@@ -80,7 +79,8 @@ static void rlogin_receive(Plug plug, int urgent, char *data, int len)
len--;
if (c == '\x80') {
rlogin->cansize = 1;
- rlogin_size(rlogin, rlogin->term_width, rlogin->term_height);
+ backend_size(&rlogin->backend,
+ rlogin->term_width, rlogin->term_height);
}
/*
* We should flush everything (aka Telnet SYNCH) if we see
@@ -148,7 +148,7 @@ static const Plug_vtable Rlogin_plugvt = {
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *rlogin_init(void *frontend_handle, void **backend_handle,
+static const char *rlogin_init(void *frontend_handle, Backend **backend_handle,
Conf *conf,
const char *host, int port, char **realhost,
int nodelay, int keepalive)
@@ -162,6 +162,7 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
rlogin = snew(struct rlogin_tag);
rlogin->plugvt = &Rlogin_plugvt;
+ rlogin->backend.vt = &rlogin_backend;
rlogin->s = NULL;
rlogin->closed_on_socket_error = FALSE;
rlogin->frontend = frontend_handle;
@@ -171,7 +172,7 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
rlogin->cansize = 0;
rlogin->prompt = NULL;
rlogin->conf = conf_copy(conf);
- *backend_handle = rlogin;
+ *backend_handle = &rlogin->backend;
addressfamily = conf_get_int(conf, CONF_addressfamily);
/*
@@ -232,9 +233,9 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
return NULL;
}
-static void rlogin_free(void *handle)
+static void rlogin_free(Backend *be)
{
- Rlogin rlogin = (Rlogin) handle;
+ Rlogin rlogin = FROMFIELD(be, struct rlogin_tag, backend);
if (rlogin->prompt)
free_prompts(rlogin->prompt);
@@ -247,16 +248,16 @@ static void rlogin_free(void *handle)
/*
* Stub routine (we don't have any need to reconfigure this backend).
*/
-static void rlogin_reconfig(void *handle, Conf *conf)
+static void rlogin_reconfig(Backend *be, Conf *conf)
{
}
/*
* Called to send data down the rlogin connection.
*/
-static int rlogin_send(void *handle, const char *buf, int len)
+static int rlogin_send(Backend *be, const char *buf, int len)
{
- Rlogin rlogin = (Rlogin) handle;
+ Rlogin rlogin = FROMFIELD(be, struct rlogin_tag, backend);
bufchain bc;
if (rlogin->s == NULL)
@@ -296,18 +297,18 @@ static int rlogin_send(void *handle, const char *buf, int len)
/*
* Called to query the current socket sendability status.
*/
-static int rlogin_sendbuffer(void *handle)
+static int rlogin_sendbuffer(Backend *be)
{
- Rlogin rlogin = (Rlogin) handle;
+ Rlogin rlogin = FROMFIELD(be, struct rlogin_tag, backend);
return rlogin->bufsize;
}
/*
* Called to set the size of the window
*/
-static void rlogin_size(void *handle, int width, int height)
+static void rlogin_size(Backend *be, int width, int height)
{
- Rlogin rlogin = (Rlogin) handle;
+ Rlogin rlogin = FROMFIELD(be, struct rlogin_tag, backend);
char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 };
rlogin->term_width = width;
@@ -327,7 +328,7 @@ static void rlogin_size(void *handle, int width, int height)
/*
* Send rlogin special codes.
*/
-static void rlogin_special(void *handle, Telnet_Special code)
+static void rlogin_special(Backend *be, Telnet_Special code)
{
/* Do nothing! */
return;
@@ -337,48 +338,48 @@ static void rlogin_special(void *handle, Telnet_Special code)
* Return a list of the special codes that make sense in this
* protocol.
*/
-static const struct telnet_special *rlogin_get_specials(void *handle)
+static const struct telnet_special *rlogin_get_specials(Backend *be)
{
return NULL;
}
-static int rlogin_connected(void *handle)
+static int rlogin_connected(Backend *be)
{
- Rlogin rlogin = (Rlogin) handle;
+ Rlogin rlogin = FROMFIELD(be, struct rlogin_tag, backend);
return rlogin->s != NULL;
}
-static int rlogin_sendok(void *handle)
+static int rlogin_sendok(Backend *be)
{
- /* Rlogin rlogin = (Rlogin) handle; */
+ /* Rlogin rlogin = FROMFIELD(be, struct rlogin_tag, backend); */
return 1;
}
-static void rlogin_unthrottle(void *handle, int backlog)
+static void rlogin_unthrottle(Backend *be, int backlog)
{
- Rlogin rlogin = (Rlogin) handle;
+ Rlogin rlogin = FROMFIELD(be, struct rlogin_tag, backend);
sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
}
-static int rlogin_ldisc(void *handle, int option)
+static int rlogin_ldisc(Backend *be, int option)
{
- /* Rlogin rlogin = (Rlogin) handle; */
+ /* Rlogin rlogin = FROMFIELD(be, struct rlogin_tag, backend); */
return 0;
}
-static void rlogin_provide_ldisc(void *handle, Ldisc *ldisc)
+static void rlogin_provide_ldisc(Backend *be, Ldisc *ldisc)
{
/* This is a stub. */
}
-static void rlogin_provide_logctx(void *handle, LogContext *logctx)
+static void rlogin_provide_logctx(Backend *be, LogContext *logctx)
{
/* This is a stub. */
}
-static int rlogin_exitcode(void *handle)
+static int rlogin_exitcode(Backend *be)
{
- Rlogin rlogin = (Rlogin) handle;
+ Rlogin rlogin = FROMFIELD(be, struct rlogin_tag, backend);
if (rlogin->s != NULL)
return -1; /* still connected */
else if (rlogin->closed_on_socket_error)
@@ -391,12 +392,12 @@ static int rlogin_exitcode(void *handle)
/*
* cfg_info for rlogin does nothing at all.
*/
-static int rlogin_cfg_info(void *handle)
+static int rlogin_cfg_info(Backend *be)
{
return 0;
}
-Backend rlogin_backend = {
+const struct Backend_vtable rlogin_backend = {
rlogin_init,
rlogin_free,
rlogin_reconfig,
diff --git a/settings.c b/settings.c
index d7e65e2d..ecdb3a4b 100644
--- a/settings.c
+++ b/settings.c
@@ -74,18 +74,18 @@ const char *const ttymodes[] = {
* (which is only present in tools that manage settings).
*/
-Backend *backend_from_name(const char *name)
+const struct Backend_vtable *backend_vt_from_name(const char *name)
{
- Backend **p;
+ const struct Backend_vtable *const *p;
for (p = backends; *p != NULL; p++)
if (!strcmp((*p)->name, name))
return *p;
return NULL;
}
-Backend *backend_from_proto(int proto)
+const struct Backend_vtable *backend_vt_from_proto(int proto)
{
- Backend **p;
+ const struct Backend_vtable *const *p;
for (p = backends; *p != NULL; p++)
if ((*p)->protocol == proto)
return *p;
@@ -528,9 +528,10 @@ void save_open_settings(void *sesskey, Conf *conf)
write_setting_i(sesskey, "SSHLogOmitData", conf_get_int(conf, CONF_logomitdata));
p = "raw";
{
- const Backend *b = backend_from_proto(conf_get_int(conf, CONF_protocol));
- if (b)
- p = b->name;
+ const struct Backend_vtable *vt =
+ backend_vt_from_proto(conf_get_int(conf, CONF_protocol));
+ if (vt)
+ p = vt->name;
}
write_setting_s(sesskey, "Protocol", p);
write_setting_i(sesskey, "PortNumber", conf_get_int(conf, CONF_port));
@@ -791,9 +792,9 @@ void load_open_settings(void *sesskey, Conf *conf)
conf_set_int(conf, CONF_protocol, default_protocol);
conf_set_int(conf, CONF_port, default_port);
{
- const Backend *b = backend_from_name(prot);
- if (b) {
- conf_set_int(conf, CONF_protocol, b->protocol);
+ const struct Backend_vtable *vt = backend_vt_from_name(prot);
+ if (vt) {
+ conf_set_int(conf, CONF_protocol, vt->protocol);
gppi(sesskey, "PortNumber", default_port, conf, CONF_port);
}
}
diff --git a/ssh.c b/ssh.c
index 1b75fb84..952b901f 100644
--- a/ssh.c
+++ b/ssh.c
@@ -561,14 +561,14 @@ struct ssh_portfwd {
static void ssh1_protocol_setup(Ssh ssh);
static void ssh2_protocol_setup(Ssh ssh);
static void ssh2_bare_connection_protocol_setup(Ssh ssh);
-static void ssh_size(void *handle, int width, int height);
-static void ssh_special(void *handle, Telnet_Special);
+static void ssh_size(Backend *be, int width, int height);
+static void ssh_special(Backend *be, Telnet_Special);
static int ssh2_try_send(struct ssh_channel *c);
static int ssh_send_channel_data(struct ssh_channel *c,
const char *buf, int len);
static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, int newwin);
-static int ssh_sendbuffer(void *handle);
+static int ssh_sendbuffer(Backend *be);
static int ssh_do_close(Ssh ssh, int notify_exit);
static void ssh2_timer(void *ctx, unsigned long now);
static int ssh2_timer_update(Ssh ssh, unsigned long rekey_time);
@@ -699,6 +699,7 @@ struct ssh_tag {
Socket s;
const Plug_vtable *plugvt;
+ Backend backend;
Ldisc *ldisc;
LogContext *logctx;
@@ -1877,7 +1878,7 @@ static void do_ssh_init(Ssh ssh)
update_specials_menu(ssh->frontend);
ssh->state = SSH_STATE_BEFORE_SIZE;
- ssh->pinger = pinger_new(ssh->conf, &ssh_backend, ssh);
+ ssh->pinger = pinger_new(ssh->conf, &ssh->backend);
sfree(s->vstring);
@@ -2029,7 +2030,7 @@ static void do_ssh_connection_init(Ssh ssh)
update_specials_menu(ssh->frontend);
ssh->state = SSH_STATE_BEFORE_SIZE;
- ssh->pinger = pinger_new(ssh->conf, &ssh_backend, ssh);
+ ssh->pinger = pinger_new(ssh->conf, &ssh->backend);
/*
* Get connection protocol under way.
@@ -4726,9 +4727,9 @@ static void do_ssh1_connection(void *vctx)
ssh->state = SSH_STATE_SESSION;
if (ssh->size_needed)
- ssh_size(ssh, ssh->term_width, ssh->term_height);
+ backend_size(&ssh->backend, ssh->term_width, ssh->term_height);
if (ssh->eof_needed)
- ssh_special(ssh, TS_EOF);
+ backend_special(&ssh->backend, TS_EOF);
if (ssh->ldisc)
ldisc_echoedit_update(ssh->ldisc); /* cause ldisc to notice changes */
@@ -10110,9 +10111,9 @@ static void do_ssh2_connection(void *vctx)
ssh->state = SSH_STATE_SESSION;
if (ssh->size_needed)
- ssh_size(ssh, ssh->term_width, ssh->term_height);
+ backend_size(&ssh->backend, ssh->term_width, ssh->term_height);
if (ssh->eof_needed)
- ssh_special(ssh, TS_EOF);
+ backend_special(&ssh->backend, TS_EOF);
/*
* Transfer data!
@@ -10664,7 +10665,7 @@ static void ssh_cache_conf_values(Ssh ssh)
*
* Returns an error message, or NULL on success.
*/
-static const char *ssh_init(void *frontend_handle, void **backend_handle,
+static const char *ssh_init(void *frontend_handle, Backend **backend_handle,
Conf *conf,
const char *host, int port, char **realhost,
int nodelay, int keepalive)
@@ -10777,7 +10778,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
#endif
ssh->gss_kex_used = FALSE;
- *backend_handle = ssh;
+ ssh->backend.vt = &ssh_backend;
+ *backend_handle = &ssh->backend;
ssh->frontend = frontend_handle;
ssh->term_width = conf_get_int(ssh->conf, CONF_width);
@@ -10826,9 +10828,9 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
return NULL;
}
-static void ssh_free(void *handle)
+static void ssh_free(Backend *be)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
struct ssh_channel *c;
struct ssh_rportfwd *pf;
struct X11FakeAuth *auth;
@@ -10937,9 +10939,9 @@ static void ssh_free(void *handle)
/*
* Reconfigure the SSH backend.
*/
-static void ssh_reconfig(void *handle, Conf *conf)
+static void ssh_reconfig(Backend *be, Conf *conf)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
const char *rekeying = NULL;
int rekey_mandatory = FALSE;
unsigned long old_max_data_size;
@@ -10999,9 +11001,9 @@ static void ssh_reconfig(void *handle, Conf *conf)
/*
* Called to send data down the SSH connection.
*/
-static int ssh_send(void *handle, const char *buf, int len)
+static int ssh_send(Backend *be, const char *buf, int len)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
if (ssh == NULL || ssh->s == NULL)
return 0;
@@ -11009,15 +11011,15 @@ static int ssh_send(void *handle, const char *buf, int len)
bufchain_add(&ssh->user_input, buf, len);
queue_idempotent_callback(&ssh->user_input_consumer);
- return ssh_sendbuffer(ssh);
+ return backend_sendbuffer(&ssh->backend);
}
/*
* Called to query the current amount of buffered stdin data.
*/
-static int ssh_sendbuffer(void *handle)
+static int ssh_sendbuffer(Backend *be)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
int override_value;
if (ssh == NULL || ssh->s == NULL)
@@ -11047,9 +11049,9 @@ static int ssh_sendbuffer(void *handle)
/*
* Called to set the size of the window from SSH's POV.
*/
-static void ssh_size(void *handle, int width, int height)
+static void ssh_size(Backend *be, int width, int height)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
PktOut *pktout;
ssh->term_width = width;
@@ -11090,7 +11092,7 @@ static void ssh_size(void *handle, int width, int height)
* Return a list of the special codes that make sense in this
* protocol.
*/
-static const struct telnet_special *ssh_get_specials(void *handle)
+static const struct telnet_special *ssh_get_specials(Backend *be)
{
static const struct telnet_special ssh1_ignore_special[] = {
{"IGNORE message", TS_NOP}
@@ -11126,7 +11128,7 @@ static const struct telnet_special *ssh_get_specials(void *handle)
struct telnet_special *specials = NULL;
int nspecials = 0, specialsize = 0;
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
sfree(ssh->specials);
@@ -11196,9 +11198,9 @@ static const struct telnet_special *ssh_get_specials(void *handle)
* can send an EOF and collect resulting output (e.g. `plink
* hostname sort').
*/
-static void ssh_special(void *handle, Telnet_Special code)
+static void ssh_special(Backend *be, Telnet_Special code)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
PktOut *pktout;
if (code == TS_EOF) {
@@ -11344,9 +11346,9 @@ void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type,
* This is called when stdout/stderr (the entity to which
* from_backend sends data) manages to clear some backlog.
*/
-static void ssh_unthrottle(void *handle, int bufsize)
+static void ssh_unthrottle(Backend *be, int bufsize)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
if (ssh->version == 1) {
if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
@@ -11405,21 +11407,21 @@ void ssh_send_port_open(void *channel, const char *hostname, int port,
}
}
-static int ssh_connected(void *handle)
+static int ssh_connected(Backend *be)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
return ssh->s != NULL;
}
-static int ssh_sendok(void *handle)
+static int ssh_sendok(Backend *be)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
return ssh->send_ok;
}
-static int ssh_ldisc(void *handle, int option)
+static int ssh_ldisc(Backend *be, int option)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
if (option == LD_ECHO)
return ssh->echoing;
if (option == LD_EDIT)
@@ -11427,21 +11429,21 @@ static int ssh_ldisc(void *handle, int option)
return FALSE;
}
-static void ssh_provide_ldisc(void *handle, Ldisc *ldisc)
+static void ssh_provide_ldisc(Backend *be, Ldisc *ldisc)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
ssh->ldisc = ldisc;
}
-static void ssh_provide_logctx(void *handle, LogContext *logctx)
+static void ssh_provide_logctx(Backend *be, LogContext *logctx)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
ssh->logctx = logctx;
}
-static int ssh_return_exitcode(void *handle)
+static int ssh_return_exitcode(Backend *be)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
if (ssh->s != NULL)
return -1;
else
@@ -11453,9 +11455,9 @@ static int ssh_return_exitcode(void *handle)
* (1 or 2 for the full SSH-1 or SSH-2 protocol; -1 for the bare
* SSH-2 connection protocol, i.e. a downstream; 0 for not-decided-yet.)
*/
-static int ssh_cfg_info(void *handle)
+static int ssh_cfg_info(Backend *be)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
if (ssh->version == 0)
return 0; /* don't know yet */
else if (ssh->bare_connection)
@@ -11469,13 +11471,13 @@ static int ssh_cfg_info(void *handle)
* that fails. This variable is the means by which scp.c can reach
* into the SSH code and find out which one it got.
*/
-extern int ssh_fallback_cmd(void *handle)
+extern int ssh_fallback_cmd(Backend *be)
{
- Ssh ssh = (Ssh) handle;
+ Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
return ssh->fallback_cmd;
}
-Backend ssh_backend = {
+const struct Backend_vtable ssh_backend = {
ssh_init,
ssh_free,
ssh_reconfig,
diff --git a/ssh.h b/ssh.h
index 6c606abb..4529bbcc 100644
--- a/ssh.h
+++ b/ssh.h
@@ -629,7 +629,7 @@ extern const char sshver[];
* that fails. This variable is the means by which scp.c can reach
* into the SSH code and find out which one it got.
*/
-extern int ssh_fallback_cmd(void *handle);
+extern int ssh_fallback_cmd(Backend *backend);
void SHATransform(word32 * digest, word32 * data);
diff --git a/telnet.c b/telnet.c
index ed585276..673c152f 100644
--- a/telnet.c
+++ b/telnet.c
@@ -117,8 +117,6 @@ static const char *telopt(int opt)
#undef telnet_str
}
-static void telnet_size(void *handle, int width, int height);
-
struct Opt {
int send; /* what we initially send */
int nsend; /* -ve send if requested to stop it */
@@ -199,6 +197,7 @@ typedef struct telnet_tag {
Pinger pinger;
const Plug_vtable *plugvt;
+ Backend backend;
} *Telnet;
#define TELNET_MAX_BACKLOG 4096
@@ -280,7 +279,8 @@ static void option_side_effects(Telnet telnet, const struct Opt *o, int enabled)
static void activate_option(Telnet telnet, const struct Opt *o)
{
if (o->send == WILL && o->option == TELOPT_NAWS)
- telnet_size(telnet, telnet->term_width, telnet->term_height);
+ backend_size(&telnet->backend,
+ telnet->term_width, telnet->term_height);
if (o->send == WILL &&
(o->option == TELOPT_NEW_ENVIRON ||
o->option == TELOPT_OLD_ENVIRON)) {
@@ -705,7 +705,7 @@ static const Plug_vtable Telnet_plugvt = {
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *telnet_init(void *frontend_handle, void **backend_handle,
+static const char *telnet_init(void *frontend_handle, Backend **backend_handle,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive)
{
@@ -717,6 +717,7 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
telnet = snew(struct telnet_tag);
telnet->plugvt = &Telnet_plugvt;
+ telnet->backend.vt = &telnet_backend;
telnet->conf = conf_copy(conf);
telnet->s = NULL;
telnet->closed_on_socket_error = FALSE;
@@ -732,7 +733,7 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
telnet->ldisc = NULL;
telnet->pinger = NULL;
telnet->session_started = TRUE;
- *backend_handle = telnet;
+ *backend_handle = &telnet->backend;
/*
* Try to find host.
@@ -756,7 +757,7 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
if ((err = sk_socket_error(telnet->s)) != NULL)
return err;
- telnet->pinger = pinger_new(telnet->conf, &telnet_backend, telnet);
+ telnet->pinger = pinger_new(telnet->conf, &telnet->backend);
/*
* Initialise option states.
@@ -805,9 +806,9 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
return NULL;
}
-static void telnet_free(void *handle)
+static void telnet_free(Backend *be)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
sfree(telnet->sb_buf);
if (telnet->s)
@@ -822,9 +823,9 @@ static void telnet_free(void *handle)
* necessary, in this backend: we just save the fresh config for
* any subsequent negotiations.
*/
-static void telnet_reconfig(void *handle, Conf *conf)
+static void telnet_reconfig(Backend *be, Conf *conf)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
pinger_reconfig(telnet->pinger, telnet->conf, conf);
conf_free(telnet->conf);
telnet->conf = conf_copy(conf);
@@ -833,9 +834,9 @@ static void telnet_reconfig(void *handle, Conf *conf)
/*
* Called to send data down the Telnet connection.
*/
-static int telnet_send(void *handle, const char *buf, int len)
+static int telnet_send(Backend *be, const char *buf, int len)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
unsigned char *p, *end;
static const unsigned char iac[2] = { IAC, IAC };
static const unsigned char cr[2] = { CR, NUL };
@@ -868,18 +869,18 @@ static int telnet_send(void *handle, const char *buf, int len)
/*
* Called to query the current socket sendability status.
*/
-static int telnet_sendbuffer(void *handle)
+static int telnet_sendbuffer(Backend *be)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
return telnet->bufsize;
}
/*
* Called to set the size of the window from Telnet's POV.
*/
-static void telnet_size(void *handle, int width, int height)
+static void telnet_size(Backend *be, int width, int height)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
unsigned char b[24];
int n;
char *logbuf;
@@ -913,9 +914,9 @@ static void telnet_size(void *handle, int width, int height)
/*
* Send Telnet special codes.
*/
-static void telnet_special(void *handle, Telnet_Special code)
+static void telnet_special(Backend *be, Telnet_Special code)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
unsigned char b[2];
if (telnet->s == NULL)
@@ -1008,7 +1009,7 @@ static void telnet_special(void *handle, Telnet_Special code)
}
}
-static const struct telnet_special *telnet_get_specials(void *handle)
+static const struct telnet_special *telnet_get_specials(Backend *be)
{
static const struct telnet_special specials[] = {
{"Are You There", TS_AYT},
@@ -1031,27 +1032,27 @@ static const struct telnet_special *telnet_get_specials(void *handle)
return specials;
}
-static int telnet_connected(void *handle)
+static int telnet_connected(Backend *be)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
return telnet->s != NULL;
}
-static int telnet_sendok(void *handle)
+static int telnet_sendok(Backend *be)
{
- /* Telnet telnet = (Telnet) handle; */
+ /* Telnet telnet = FROMFIELD(be, struct telnet_tag, backend); */
return 1;
}
-static void telnet_unthrottle(void *handle, int backlog)
+static void telnet_unthrottle(Backend *be, int backlog)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG);
}
-static int telnet_ldisc(void *handle, int option)
+static int telnet_ldisc(Backend *be, int option)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
if (option == LD_ECHO)
return telnet->echoing;
if (option == LD_EDIT)
@@ -1059,20 +1060,20 @@ static int telnet_ldisc(void *handle, int option)
return FALSE;
}
-static void telnet_provide_ldisc(void *handle, Ldisc *ldisc)
+static void telnet_provide_ldisc(Backend *be, Ldisc *ldisc)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
telnet->ldisc = ldisc;
}
-static void telnet_provide_logctx(void *handle, LogContext *logctx)
+static void telnet_provide_logctx(Backend *be, LogContext *logctx)
{
/* This is a stub. */
}
-static int telnet_exitcode(void *handle)
+static int telnet_exitcode(Backend *be)
{
- Telnet telnet = (Telnet) handle;
+ Telnet telnet = FROMFIELD(be, struct telnet_tag, backend);
if (telnet->s != NULL)
return -1; /* still connected */
else if (telnet->closed_on_socket_error)
@@ -1085,12 +1086,12 @@ static int telnet_exitcode(void *handle)
/*
* cfg_info for Telnet does nothing at all.
*/
-static int telnet_cfg_info(void *handle)
+static int telnet_cfg_info(Backend *be)
{
return 0;
}
-Backend telnet_backend = {
+const struct Backend_vtable telnet_backend = {
telnet_init,
telnet_free,
telnet_reconfig,
diff --git a/terminal.c b/terminal.c
index 04e222d0..1dc83dc1 100644
--- a/terminal.c
+++ b/terminal.c
@@ -1693,8 +1693,7 @@ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata,
term->lastbeep = FALSE;
term->beep_overloaded = FALSE;
term->attr_mask = 0xffffffff;
- term->resize_fn = NULL;
- term->resize_ctx = NULL;
+ term->backend = NULL;
term->in_term_out = FALSE;
term->ltemp = NULL;
term->ltemp_size = 0;
@@ -1964,22 +1963,18 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
update_sbar(term);
term_update(term);
- if (term->resize_fn)
- term->resize_fn(term->resize_ctx, term->cols, term->rows);
+ if (term->backend)
+ backend_size(term->backend, term->cols, term->rows);
}
/*
- * Hand a function and context pointer to the terminal which it can
- * use to notify a back end of resizes.
+ * Hand a backend to the terminal, so it can be notified of resizes.
*/
-void term_provide_resize_fn(Terminal *term,
- void (*resize_fn)(void *, int, int),
- void *resize_ctx)
+void term_provide_backend(Terminal *term, Backend *backend)
{
- term->resize_fn = resize_fn;
- term->resize_ctx = resize_ctx;
- if (resize_fn && term->cols > 0 && term->rows > 0)
- resize_fn(resize_ctx, term->cols, term->rows);
+ term->backend = backend;
+ if (term->backend && term->cols > 0 && term->rows > 0)
+ backend_size(term->backend, term->cols, term->rows);
}
/* Find the bottom line on the screen that has any content.
diff --git a/terminal.h b/terminal.h
index 244ff3bc..8ec256d4 100644
--- a/terminal.h
+++ b/terminal.h
@@ -227,8 +227,7 @@ struct terminal_tag {
wchar_t *paste_buffer;
int paste_len, paste_pos;
- void (*resize_fn)(void *, int, int);
- void *resize_ctx;
+ Backend *backend;
Ldisc *ldisc;
diff --git a/testback.c b/testback.c
index 78b89333..f3e9e35f 100644
--- a/testback.c
+++ b/testback.c
@@ -32,36 +32,36 @@
#include "putty.h"
-static const char *null_init(void *, void **, Conf *, const char *, int,
+static const char *null_init(void *, Backend **, Conf *, const char *, int,
char **, int, int);
-static const char *loop_init(void *, void **, Conf *, const char *, int,
+static const char *loop_init(void *, Backend **, Conf *, const char *, int,
char **, int, int);
-static void null_free(void *);
-static void loop_free(void *);
-static void null_reconfig(void *, Conf *);
-static int null_send(void *, const char *, int);
-static int loop_send(void *, const char *, int);
-static int null_sendbuffer(void *);
-static void null_size(void *, int, int);
-static void null_special(void *, Telnet_Special);
-static const struct telnet_special *null_get_specials(void *handle);
-static int null_connected(void *);
-static int null_exitcode(void *);
-static int null_sendok(void *);
-static int null_ldisc(void *, int);
-static void null_provide_ldisc(void *, Ldisc *);
-static void null_provide_logctx(void *, LogContext *);
-static void null_unthrottle(void *, int);
-static int null_cfg_info(void *);
-
-Backend null_backend = {
+static void null_free(Backend *);
+static void loop_free(Backend *);
+static void null_reconfig(Backend *, Conf *);
+static int null_send(Backend *, const char *, int);
+static int loop_send(Backend *, const char *, int);
+static int null_sendbuffer(Backend *);
+static void null_size(Backend *, int, int);
+static void null_special(Backend *, Telnet_Special);
+static const struct telnet_special *null_get_specials(Backend *);
+static int null_connected(Backend *);
+static int null_exitcode(Backend *);
+static int null_sendok(Backend *);
+static int null_ldisc(Backend *, int);
+static void null_provide_ldisc(Backend *, Ldisc *);
+static void null_provide_logctx(Backend *, LogContext *);
+static void null_unthrottle(Backend *, int);
+static int null_cfg_info(Backend *);
+
+const struct Backend_vtable null_backend = {
null_init, null_free, null_reconfig, null_send, null_sendbuffer, null_size,
null_special, null_get_specials, null_connected, null_exitcode, null_sendok,
null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle,
null_cfg_info, NULL /* test_for_upstream */, "null", -1, 0
};
-Backend loop_backend = {
+const struct Backend_vtable loop_backend = {
loop_init, loop_free, null_reconfig, loop_send, null_sendbuffer, null_size,
null_special, null_get_specials, null_connected, null_exitcode, null_sendok,
null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle,
@@ -70,102 +70,104 @@ Backend loop_backend = {
struct loop_state {
Terminal *term;
+ Backend backend;
};
-static const char *null_init(void *frontend_handle, void **backend_handle,
+static const char *null_init(void *frontend_handle, Backend **backend_handle,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive) {
-
+ *backend_handle = NULL;
return NULL;
}
-static const char *loop_init(void *frontend_handle, void **backend_handle,
+static const char *loop_init(void *frontend_handle, Backend **backend_handle,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive) {
struct loop_state *st = snew(struct loop_state);
st->term = frontend_handle;
- *backend_handle = st;
+ *backend_handle = &st->backend;
return NULL;
}
-static void null_free(void *handle)
+static void null_free(Backend *be)
{
}
-static void loop_free(void *handle)
+static void loop_free(Backend *be)
{
+ struct loop_state *st = FROMFIELD(be, struct loop_state, backend);
- sfree(handle);
+ sfree(st);
}
-static void null_reconfig(void *handle, Conf *conf) {
+static void null_reconfig(Backend *be, Conf *conf) {
}
-static int null_send(void *handle, const char *buf, int len) {
+static int null_send(Backend *be, const char *buf, int len) {
return 0;
}
-static int loop_send(void *handle, const char *buf, int len) {
- struct loop_state *st = handle;
+static int loop_send(Backend *be, const char *buf, int len) {
+ struct loop_state *st = FROMFIELD(be, struct loop_state, backend);
return from_backend(st->term, 0, buf, len);
}
-static int null_sendbuffer(void *handle) {
+static int null_sendbuffer(Backend *be) {
return 0;
}
-static void null_size(void *handle, int width, int height) {
+static void null_size(Backend *be, int width, int height) {
}
-static void null_special(void *handle, Telnet_Special code) {
+static void null_special(Backend *be, Telnet_Special code) {
}
-static const struct telnet_special *null_get_specials (void *handle) {
+static const struct telnet_special *null_get_specials (Backend *be) {
return NULL;
}
-static int null_connected(void *handle) {
+static int null_connected(Backend *be) {
return 0;
}
-static int null_exitcode(void *handle) {
+static int null_exitcode(Backend *be) {
return 0;
}
-static int null_sendok(void *handle) {
+static int null_sendok(Backend *be) {
return 1;
}
-static void null_unthrottle(void *handle, int backlog) {
+static void null_unthrottle(Backend *be, int backlog) {
}
-static int null_ldisc(void *handle, int option) {
+static int null_ldisc(Backend *be, int option) {
return 0;
}
-static void null_provide_ldisc (void *handle, Ldisc *ldisc) {
+static void null_provide_ldisc (Backend *be, Ldisc *ldisc) {
}
-static void null_provide_logctx(void *handle, LogContext *logctx) {
+static void null_provide_logctx(Backend *be, LogContext *logctx) {
}
-static int null_cfg_info(void *handle)
+static int null_cfg_info(Backend *be)
{
return 0;
}
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 18bea32f..6867ac07 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -159,8 +159,7 @@ struct gui_data {
char *icontitle;
int master_fd, master_func_id;
Ldisc *ldisc;
- Backend *back;
- void *backhandle;
+ Backend *backend;
Terminal *term;
LogContext *logctx;
int exited;
@@ -1609,8 +1608,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
#ifdef KEY_EVENT_DIAGNOSTICS
debug((" - Ctrl-Break special case, sending TS_BRK\n"));
#endif
- if (inst->back)
- inst->back->special(inst->backhandle, TS_BRK);
+ if (inst->backend)
+ backend_special(inst->backend, TS_BRK);
return TRUE;
}
@@ -2399,7 +2398,7 @@ static void exit_callback(void *vinst)
int exitcode, close_on_exit;
if (!inst->exited &&
- (exitcode = inst->back->exitcode(inst->backhandle)) >= 0) {
+ (exitcode = backend_exitcode(inst->backend)) >= 0) {
destroy_inst_connection(inst);
close_on_exit = conf_get_int(inst->conf, CONF_close_on_exit);
@@ -2424,13 +2423,12 @@ static void destroy_inst_connection(struct gui_data *inst)
ldisc_free(inst->ldisc);
inst->ldisc = NULL;
}
- if (inst->backhandle) {
- inst->back->free(inst->backhandle);
- inst->backhandle = NULL;
- inst->back = NULL;
+ if (inst->backend) {
+ backend_free(inst->backend);
+ inst->backend = NULL;
}
if (inst->term)
- term_provide_resize_fn(inst->term, NULL, NULL);
+ term_provide_backend(inst->term, NULL);
if (inst->menu) {
update_specials_menu(inst);
gtk_widget_set_sensitive(inst->restartitem, TRUE);
@@ -4480,8 +4478,8 @@ void special_menuitem(GtkMenuItem *item, gpointer data)
int code = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item),
"user-data"));
- if (inst->back)
- inst->back->special(inst->backhandle, code);
+ if (inst->backend)
+ backend_special(inst->backend, code);
}
void about_menuitem(GtkMenuItem *item, gpointer data)
@@ -4582,7 +4580,7 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
dialog = create_config_box(
title, ctx->newconf, 1,
- inst->back ? inst->back->cfg_info(inst->backhandle) : 0,
+ inst->backend ? backend_cfg_info(inst->backend) : 0,
after_change_settings_dialog, ctx);
register_dialog(inst, DIALOG_SLOT_RECONFIGURE, dialog);
@@ -4633,8 +4631,8 @@ static void after_change_settings_dialog(void *vctx, int retval)
term_reconfig(inst->term, inst->conf);
setup_clipboards(inst, inst->term, inst->conf);
/* Pass new config data to the back end */
- if (inst->back)
- inst->back->reconfig(inst->backhandle, inst->conf);
+ if (inst->backend)
+ backend_reconfig(inst->backend, inst->conf);
cache_conf_values(inst);
@@ -4851,7 +4849,7 @@ void restart_session_menuitem(GtkMenuItem *item, gpointer data)
{
struct gui_data *inst = (struct gui_data *)data;
- if (!inst->back) {
+ if (!inst->backend) {
logevent(inst, "----- Session restarted -----");
term_pwron(inst->term, FALSE);
start_backend(inst);
@@ -4984,8 +4982,8 @@ void update_specials_menu(void *frontend)
const struct telnet_special *specials;
- if (inst->back)
- specials = inst->back->get_specials(inst->backhandle);
+ if (inst->backend)
+ specials = backend_get_specials(inst->backend);
else
specials = NULL;
@@ -5045,20 +5043,20 @@ void update_specials_menu(void *frontend)
static void start_backend(struct gui_data *inst)
{
- extern Backend *select_backend(Conf *conf);
+ const struct Backend_vtable *vt;
char *realhost;
const char *error;
char *s;
- inst->back = select_backend(inst->conf);
+ vt = select_backend(inst->conf);
- error = inst->back->init((void *)inst, &inst->backhandle,
- inst->conf,
- conf_get_str(inst->conf, CONF_host),
- conf_get_int(inst->conf, CONF_port),
- &realhost,
- conf_get_int(inst->conf, CONF_tcp_nodelay),
- conf_get_int(inst->conf, CONF_tcp_keepalives));
+ error = backend_init(vt, (void *)inst, &inst->backend,
+ inst->conf,
+ conf_get_str(inst->conf, CONF_host),
+ conf_get_int(inst->conf, CONF_port),
+ &realhost,
+ conf_get_int(inst->conf, CONF_tcp_nodelay),
+ conf_get_int(inst->conf, CONF_tcp_keepalives));
if (error) {
char *msg = dupprintf("Unable to open connection to %s:\n%s",
@@ -5079,13 +5077,11 @@ static void start_backend(struct gui_data *inst)
}
sfree(realhost);
- inst->back->provide_logctx(inst->backhandle, inst->logctx);
+ backend_provide_logctx(inst->backend, inst->logctx);
- term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
+ term_provide_backend(inst->term, inst->backend);
- inst->ldisc =
- ldisc_create(inst->conf, inst->term, inst->back, inst->backhandle,
- inst);
+ inst->ldisc = ldisc_create(inst->conf, inst->term, inst->backend, inst);
gtk_widget_set_sensitive(inst->restartitem, FALSE);
}
diff --git a/unix/unix.h b/unix/unix.h
index 14ce470a..f9ff3c68 100644
--- a/unix/unix.h
+++ b/unix/unix.h
@@ -67,7 +67,7 @@ struct FontSpec *fontspec_new(const char *name);
typedef void *Context; /* FIXME: probably needs changing */
-extern Backend pty_backend;
+extern const struct Backend_vtable pty_backend;
#define BROKEN_PIPE_ERROR_CODE EPIPE /* used in sshshare.c */
@@ -178,6 +178,8 @@ void window_setup_error(const char *errmsg);
GtkWidget *make_gtk_toplevel_window(void *frontend);
#endif
+const struct Backend_vtable *select_backend(Conf *conf);
+
/* Defined in gtkcomm.c */
void gtkcomm_setup(void);
@@ -324,9 +326,9 @@ void *sk_getxdmdata(Socket sock, int *lenp);
} while (0)
/*
- * Exports from winser.c.
+ * Exports from uxser.c.
*/
-extern Backend serial_backend;
+extern const struct Backend_vtable serial_backend;
/*
* uxpeer.c, wrapping getsockopt(SO_PEERCRED).
diff --git a/unix/uxplink.c b/unix/uxplink.c
index 64e29c31..7bcac362 100644
--- a/unix/uxplink.c
+++ b/unix/uxplink.c
@@ -91,8 +91,7 @@ void cmdline_error(const char *p, ...)
static int local_tty = FALSE; /* do we have a local tty? */
-static Backend *back;
-static void *backhandle;
+static Backend *backend;
static Conf *conf;
/*
@@ -459,13 +458,13 @@ static void from_tty(void *vbuf, unsigned len)
} else {
q = memchr(p, '\xff', end - p);
if (q == NULL) q = end;
- back->send(backhandle, p, q - p);
+ backend_send(backend, p, q - p);
p = q;
}
break;
case FF:
if (*p == '\xff') {
- back->send(backhandle, p, 1);
+ backend_send(backend, p, 1);
p++;
state = NORMAL;
} else if (*p == '\0') {
@@ -475,7 +474,7 @@ static void from_tty(void *vbuf, unsigned len)
break;
case FF00:
if (*p == '\0') {
- back->special(backhandle, TS_BRK);
+ backend_special(backend, TS_BRK);
} else {
/*
* Pretend that PARMRK wasn't set. This involves
@@ -494,11 +493,11 @@ static void from_tty(void *vbuf, unsigned len)
if (!(orig_termios.c_iflag & IGNPAR)) {
/* PE/FE get passed on as NUL. */
*p = 0;
- back->send(backhandle, p, 1);
+ backend_send(backend, p, 1);
}
} else {
/* INPCK not set. Assume we got a parity error. */
- back->send(backhandle, p, 1);
+ backend_send(backend, p, 1);
}
}
p++;
@@ -606,6 +605,7 @@ int main(int argc, char **argv)
int just_test_share_exists = FALSE;
unsigned long now;
struct winsize size;
+ const struct Backend_vtable *backvt;
fdlist = NULL;
fdcount = fdsize = 0;
@@ -643,10 +643,10 @@ int main(int argc, char **argv)
*/
char *p = getenv("PLINK_PROTOCOL");
if (p) {
- const Backend *b = backend_from_name(p);
- if (b) {
- default_protocol = b->protocol;
- default_port = b->default_port;
+ const struct Backend_vtable *vt = backend_vt_from_name(p);
+ if (vt) {
+ default_protocol = vt->protocol;
+ default_port = vt->default_port;
conf_set_int(conf, CONF_protocol, default_protocol);
conf_set_int(conf, CONF_port, default_port);
}
@@ -766,8 +766,8 @@ int main(int argc, char **argv)
* Select protocol. This is farmed out into a table in a
* separate file to enable an ssh-free variant.
*/
- back = backend_from_proto(conf_get_int(conf, CONF_protocol));
- if (back == NULL) {
+ backvt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol));
+ if (!backvt) {
fprintf(stderr,
"Internal fault: Unsupported protocol found\n");
return 1;
@@ -817,13 +817,13 @@ int main(int argc, char **argv)
conf_set_int(conf, CONF_ssh_simple, TRUE);
if (just_test_share_exists) {
- if (!back->test_for_upstream) {
+ if (!backvt->test_for_upstream) {
fprintf(stderr, "Connection sharing not supported for connection "
- "type '%s'\n", back->name);
+ "type '%s'\n", backvt->name);
return 1;
}
- if (back->test_for_upstream(conf_get_str(conf, CONF_host),
- conf_get_int(conf, CONF_port), conf))
+ if (backvt->test_for_upstream(conf_get_str(conf, CONF_host),
+ conf_get_int(conf, CONF_port), conf))
return 0;
else
return 1;
@@ -845,17 +845,17 @@ int main(int argc, char **argv)
__AFL_INIT();
#endif
- error = back->init(NULL, &backhandle, conf,
- conf_get_str(conf, CONF_host),
- conf_get_int(conf, CONF_port),
- &realhost, nodelay,
- conf_get_int(conf, CONF_tcp_keepalives));
+ error = backend_init(backvt, NULL, &backend, conf,
+ conf_get_str(conf, CONF_host),
+ conf_get_int(conf, CONF_port),
+ &realhost, nodelay,
+ conf_get_int(conf, CONF_tcp_keepalives));
if (error) {
fprintf(stderr, "Unable to open connection:\n%s\n", error);
return 1;
}
- back->provide_logctx(backhandle, logctx);
- ldisc_create(conf, NULL, back, backhandle, NULL);
+ backend_provide_logctx(backend, logctx);
+ ldisc_create(conf, NULL, backend, NULL);
sfree(realhost);
}
@@ -885,9 +885,9 @@ int main(int argc, char **argv)
FD_SET_MAX(signalpipe[0], maxfd, rset);
if (!sending &&
- back->connected(backhandle) &&
- back->sendok(backhandle) &&
- back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {
+ backend_connected(backend) &&
+ backend_sendok(backend) &&
+ backend_sendbuffer(backend) < MAX_STDIN_BACKLOG) {
/* If we're OK to send, then try to read from stdin. */
FD_SET_MAX(STDIN_FILENO, maxfd, rset);
}
@@ -988,46 +988,46 @@ int main(int argc, char **argv)
/* ignore error */;
/* ignore its value; it'll be `x' */
if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0)
- back->size(backhandle, size.ws_col, size.ws_row);
+ backend_size(backend, size.ws_col, size.ws_row);
}
if (FD_ISSET(STDIN_FILENO, &rset)) {
char buf[4096];
int ret;
- if (back->connected(backhandle)) {
+ if (backend_connected(backend)) {
ret = read(STDIN_FILENO, buf, sizeof(buf));
if (ret < 0) {
perror("stdin: read");
exit(1);
} else if (ret == 0) {
- back->special(backhandle, TS_EOF);
+ backend_special(backend, TS_EOF);
sending = FALSE; /* send nothing further after this */
} else {
if (local_tty)
from_tty(buf, ret);
else
- back->send(backhandle, buf, ret);
+ backend_send(backend, buf, ret);
}
}
}
if (FD_ISSET(STDOUT_FILENO, &wset)) {
- back->unthrottle(backhandle, try_output(FALSE));
+ backend_unthrottle(backend, try_output(FALSE));
}
if (FD_ISSET(STDERR_FILENO, &wset)) {
- back->unthrottle(backhandle, try_output(TRUE));
+ backend_unthrottle(backend, try_output(TRUE));
}
run_toplevel_callbacks();
- if (!back->connected(backhandle) &&
+ if (!backend_connected(backend) &&
bufchain_size(&stdout_data) == 0 &&
bufchain_size(&stderr_data) == 0)
break; /* we closed the connection */
}
- exitcode = back->exitcode(backhandle);
+ exitcode = backend_exitcode(backend);
if (exitcode < 0) {
fprintf(stderr, "Remote process exit code unavailable\n");
exitcode = 1; /* this is an error condition */
diff --git a/unix/uxpterm.c b/unix/uxpterm.c
index d80c3780..4df8b164 100644
--- a/unix/uxpterm.c
+++ b/unix/uxpterm.c
@@ -13,7 +13,7 @@ const int new_session = 0, saved_sessions = 0; /* or these */
const int dup_check_launchable = 0; /* no need to check host name in conf */
const int use_pty_argv = TRUE;
-Backend *select_backend(Conf *conf)
+const struct Backend_vtable *select_backend(Conf *conf)
{
return &pty_backend;
}
diff --git a/unix/uxpty.c b/unix/uxpty.c
index b1204a10..870e5156 100644
--- a/unix/uxpty.c
+++ b/unix/uxpty.c
@@ -78,6 +78,7 @@ struct pty_tag {
int child_dead, finished;
int exit_code;
bufchain output_data;
+ Backend backend;
};
/*
@@ -728,9 +729,9 @@ static void pty_uxsel_setup(Pty pty)
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *pty_init(void *frontend, void **backend_handle, Conf *conf,
- const char *host, int port, char **realhost,
- int nodelay, int keepalive)
+static const char *pty_init(void *frontend, Backend **backend_handle,
+ Conf *conf, const char *host, int port,
+ char **realhost, int nodelay, int keepalive)
{
int slavefd;
pid_t pid, pgrp;
@@ -752,7 +753,8 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf,
}
pty->frontend = frontend;
- *backend_handle = NULL; /* we can't sensibly use this, sadly */
+ pty->backend.vt = &pty_backend;
+ *backend_handle = &pty->backend;
pty->conf = conf_copy(conf);
pty->term_width = conf_get_int(conf, CONF_width);
@@ -1025,16 +1027,14 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf,
}
pty_uxsel_setup(pty);
- *backend_handle = pty;
-
*realhost = dupstr("");
return NULL;
}
-static void pty_reconfig(void *handle, Conf *conf)
+static void pty_reconfig(Backend *be, Conf *conf)
{
- Pty pty = (Pty)handle;
+ Pty pty = FROMFIELD(be, struct pty_tag, backend);
/*
* We don't have much need to reconfigure this backend, but
* unfortunately we do need to pick up the setting of Close On
@@ -1046,9 +1046,9 @@ static void pty_reconfig(void *handle, Conf *conf)
/*
* Stub routine (never called in pterm).
*/
-static void pty_free(void *handle)
+static void pty_free(Backend *be)
{
- Pty pty = (Pty)handle;
+ Pty pty = FROMFIELD(be, struct pty_tag, backend);
/* Either of these may fail `not found'. That's fine with us. */
del234(ptys_by_pid, pty);
@@ -1099,9 +1099,9 @@ static void pty_try_write(Pty pty)
/*
* Called to send data down the pty.
*/
-static int pty_send(void *handle, const char *buf, int len)
+static int pty_send(Backend *be, const char *buf, int len)
{
- Pty pty = (Pty)handle;
+ Pty pty = FROMFIELD(be, struct pty_tag, backend);
if (pty->master_fd < 0)
return 0; /* ignore all writes if fd closed */
@@ -1129,18 +1129,18 @@ static void pty_close(Pty pty)
/*
* Called to query the current socket sendability status.
*/
-static int pty_sendbuffer(void *handle)
+static int pty_sendbuffer(Backend *be)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
return 0;
}
/*
* Called to set the size of the window
*/
-static void pty_size(void *handle, int width, int height)
+static void pty_size(Backend *be, int width, int height)
{
- Pty pty = (Pty)handle;
+ Pty pty = FROMFIELD(be, struct pty_tag, backend);
struct winsize size;
pty->term_width = width;
@@ -1159,9 +1159,9 @@ static void pty_size(void *handle, int width, int height)
/*
* Send special codes.
*/
-static void pty_special(void *handle, Telnet_Special code)
+static void pty_special(Backend *be, Telnet_Special code)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
/* Do nothing! */
return;
}
@@ -1170,9 +1170,9 @@ static void pty_special(void *handle, Telnet_Special code)
* Return a list of the special codes that make sense in this
* protocol.
*/
-static const struct telnet_special *pty_get_specials(void *handle)
+static const struct telnet_special *pty_get_specials(Backend *be)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
/*
* Hmm. When I get round to having this actually usable, it
* might be quite nice to have the ability to deliver a few
@@ -1182,58 +1182,58 @@ static const struct telnet_special *pty_get_specials(void *handle)
return NULL;
}
-static int pty_connected(void *handle)
+static int pty_connected(Backend *be)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
return TRUE;
}
-static int pty_sendok(void *handle)
+static int pty_sendok(Backend *be)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
return 1;
}
-static void pty_unthrottle(void *handle, int backlog)
+static void pty_unthrottle(Backend *be, int backlog)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
/* do nothing */
}
-static int pty_ldisc(void *handle, int option)
+static int pty_ldisc(Backend *be, int option)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
return 0; /* neither editing nor echoing */
}
-static void pty_provide_ldisc(void *handle, Ldisc *ldisc)
+static void pty_provide_ldisc(Backend *be, Ldisc *ldisc)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
/* This is a stub. */
}
-static void pty_provide_logctx(void *handle, LogContext *logctx)
+static void pty_provide_logctx(Backend *be, LogContext *logctx)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
/* This is a stub. */
}
-static int pty_exitcode(void *handle)
+static int pty_exitcode(Backend *be)
{
- Pty pty = (Pty)handle;
+ Pty pty = FROMFIELD(be, struct pty_tag, backend);
if (!pty->finished)
return -1; /* not dead yet */
else
return pty->exit_code;
}
-static int pty_cfg_info(void *handle)
+static int pty_cfg_info(Backend *be)
{
- /* Pty pty = (Pty)handle; */
+ /* Pty pty = FROMFIELD(be, struct pty_tag, backend); */
return 0;
}
-Backend pty_backend = {
+const struct Backend_vtable pty_backend = {
pty_init,
pty_free,
pty_reconfig,
diff --git a/unix/uxputty.c b/unix/uxputty.c
index fb280187..52e4cef1 100644
--- a/unix/uxputty.c
+++ b/unix/uxputty.c
@@ -37,11 +37,12 @@ void cleanup_exit(int code)
exit(code);
}
-Backend *select_backend(Conf *conf)
+const struct Backend_vtable *select_backend(Conf *conf)
{
- Backend *back = backend_from_proto(conf_get_int(conf, CONF_protocol));
- assert(back != NULL);
- return back;
+ const struct Backend_vtable *vt =
+ backend_vt_from_proto(conf_get_int(conf, CONF_protocol));
+ assert(vt != NULL);
+ return vt;
}
void initial_config_box(Conf *conf, post_dialog_fn_t after, void *afterctx)
@@ -83,9 +84,10 @@ void setup(int single)
default_protocol = be_default_protocol;
/* Find the appropriate default port. */
{
- Backend *b = backend_from_proto(default_protocol);
+ const struct Backend_vtable *vt =
+ backend_vt_from_proto(default_protocol);
default_port = 0; /* illegal */
- if (b)
- default_port = b->default_port;
+ if (vt)
+ default_port = vt->default_port;
}
}
diff --git a/unix/uxser.c b/unix/uxser.c
index 50eb83e6..e352f049 100644
--- a/unix/uxser.c
+++ b/unix/uxser.c
@@ -23,6 +23,7 @@ typedef struct serial_backend_data {
int finished;
int inbufsize;
bufchain output_data;
+ Backend backend;
} *Serial;
/*
@@ -287,7 +288,7 @@ static const char *serial_configure(Serial serial, Conf *conf)
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *serial_init(void *frontend_handle, void **backend_handle,
+static const char *serial_init(void *frontend_handle, Backend **backend_handle,
Conf *conf,
const char *host, int port, char **realhost,
int nodelay, int keepalive)
@@ -297,7 +298,8 @@ static const char *serial_init(void *frontend_handle, void **backend_handle,
char *line;
serial = snew(struct serial_backend_data);
- *backend_handle = serial;
+ serial->backend.vt = &serial_backend;
+ *backend_handle = &serial->backend;
serial->frontend = frontend_handle;
serial->finished = FALSE;
@@ -345,9 +347,9 @@ static void serial_close(Serial serial)
}
}
-static void serial_free(void *handle)
+static void serial_free(Backend *be)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
serial_close(serial);
@@ -356,9 +358,9 @@ static void serial_free(void *handle)
sfree(serial);
}
-static void serial_reconfig(void *handle, Conf *conf)
+static void serial_reconfig(Backend *be, Conf *conf)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
/*
* FIXME: what should we do if this returns an error?
@@ -460,9 +462,9 @@ static void serial_try_write(Serial serial)
/*
* Called to send data down the serial connection.
*/
-static int serial_send(void *handle, const char *buf, int len)
+static int serial_send(Backend *be, const char *buf, int len)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
if (serial->fd < 0)
return 0;
@@ -476,16 +478,16 @@ static int serial_send(void *handle, const char *buf, int len)
/*
* Called to query the current sendability status.
*/
-static int serial_sendbuffer(void *handle)
+static int serial_sendbuffer(Backend *be)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
return bufchain_size(&serial->output_data);
}
/*
* Called to set the size of the window
*/
-static void serial_size(void *handle, int width, int height)
+static void serial_size(Backend *be, int width, int height)
{
/* Do nothing! */
return;
@@ -494,9 +496,9 @@ static void serial_size(void *handle, int width, int height)
/*
* Send serial special codes.
*/
-static void serial_special(void *handle, Telnet_Special code)
+static void serial_special(Backend *be, Telnet_Special code)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
if (serial->fd >= 0 && code == TS_BRK) {
tcsendbreak(serial->fd, 0);
@@ -510,7 +512,7 @@ static void serial_special(void *handle, Telnet_Special code)
* Return a list of the special codes that make sense in this
* protocol.
*/
-static const struct telnet_special *serial_get_specials(void *handle)
+static const struct telnet_special *serial_get_specials(Backend *be)
{
static const struct telnet_special specials[] = {
{"Break", TS_BRK},
@@ -519,24 +521,24 @@ static const struct telnet_special *serial_get_specials(void *handle)
return specials;
}
-static int serial_connected(void *handle)
+static int serial_connected(Backend *be)
{
return 1; /* always connected */
}
-static int serial_sendok(void *handle)
+static int serial_sendok(Backend *be)
{
return 1;
}
-static void serial_unthrottle(void *handle, int backlog)
+static void serial_unthrottle(Backend *be, int backlog)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
serial->inbufsize = backlog;
serial_uxsel_setup(serial);
}
-static int serial_ldisc(void *handle, int option)
+static int serial_ldisc(Backend *be, int option)
{
/*
* Local editing and local echo are off by default.
@@ -544,19 +546,19 @@ static int serial_ldisc(void *handle, int option)
return 0;
}
-static void serial_provide_ldisc(void *handle, Ldisc *ldisc)
+static void serial_provide_ldisc(Backend *be, Ldisc *ldisc)
{
/* This is a stub. */
}
-static void serial_provide_logctx(void *handle, LogContext *logctx)
+static void serial_provide_logctx(Backend *be, LogContext *logctx)
{
/* This is a stub. */
}
-static int serial_exitcode(void *handle)
+static int serial_exitcode(Backend *be)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
if (serial->fd >= 0)
return -1; /* still connected */
else
@@ -567,12 +569,12 @@ static int serial_exitcode(void *handle)
/*
* cfg_info for Serial does nothing at all.
*/
-static int serial_cfg_info(void *handle)
+static int serial_cfg_info(Backend *be)
{
return 0;
}
-Backend serial_backend = {
+const struct Backend_vtable serial_backend = {
serial_init,
serial_free,
serial_reconfig,
diff --git a/windows/wincfg.c b/windows/wincfg.c
index 950cef11..9e242875 100644
--- a/windows/wincfg.c
+++ b/windows/wincfg.c
@@ -393,7 +393,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
* $XAUTHORITY is not reliable on Windows, so we provide a
* means to override it.
*/
- if (!midsession && backend_from_proto(PROT_SSH)) {
+ if (!midsession && backend_vt_from_proto(PROT_SSH)) {
s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
ctrl_filesel(s, "X authority file for local display", 't',
NULL, FALSE, "Select X authority file",
diff --git a/windows/window.c b/windows/window.c
index 80b94201..0d29b136 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -126,8 +126,7 @@ static int caret_x = -1, caret_y = -1;
static int kbd_codepage;
static Ldisc *ldisc;
-static Backend *back;
-static void *backhandle;
+static Backend *backend;
static struct unicode_data ucsdata;
static int session_closed;
@@ -248,6 +247,7 @@ char *get_ttymode(void *frontend, const char *mode)
static void start_backend(void)
{
+ const struct Backend_vtable *vt;
const char *error;
char msg[1024], *title;
char *realhost;
@@ -257,8 +257,8 @@ static void start_backend(void)
* Select protocol. This is farmed out into a table in a
* separate file to enable an ssh-free variant.
*/
- back = backend_from_proto(conf_get_int(conf, CONF_protocol));
- if (back == NULL) {
+ vt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol));
+ if (!vt) {
char *str = dupprintf("%s Internal Error", appname);
MessageBox(NULL, "Unsupported protocol number found",
str, MB_OK | MB_ICONEXCLAMATION);
@@ -266,13 +266,13 @@ static void start_backend(void)
cleanup_exit(1);
}
- error = back->init(NULL, &backhandle, conf,
- conf_get_str(conf, CONF_host),
- conf_get_int(conf, CONF_port),
- &realhost,
- conf_get_int(conf, CONF_tcp_nodelay),
- conf_get_int(conf, CONF_tcp_keepalives));
- back->provide_logctx(backhandle, logctx);
+ error = backend_init(vt, NULL, &backend, conf,
+ conf_get_str(conf, CONF_host),
+ conf_get_int(conf, CONF_port),
+ &realhost,
+ conf_get_int(conf, CONF_tcp_nodelay),
+ conf_get_int(conf, CONF_tcp_keepalives));
+ backend_provide_logctx(backend, logctx);
if (error) {
char *str = dupprintf("%s Error", appname);
sprintf(msg, "Unable to open connection to\n"
@@ -294,12 +294,12 @@ static void start_backend(void)
/*
* Connect the terminal to the backend for resize purposes.
*/
- term_provide_resize_fn(term, back->size, backhandle);
+ term_provide_backend(term, backend);
/*
* Set up a line discipline.
*/
- ldisc = ldisc_create(conf, term, back, backhandle, NULL);
+ ldisc = ldisc_create(conf, term, backend, NULL);
/*
* Destroy the Restart Session menu item. (This will return
@@ -329,11 +329,10 @@ static void close_session(void *ignored_context)
ldisc_free(ldisc);
ldisc = NULL;
}
- if (back) {
- back->free(backhandle);
- backhandle = NULL;
- back = NULL;
- term_provide_resize_fn(term, NULL, NULL);
+ if (backend) {
+ backend_free(backend);
+ backend = NULL;
+ term_provide_backend(term, NULL);
update_specials_menu(NULL);
}
@@ -413,10 +412,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
default_protocol = be_default_protocol;
/* Find the appropriate default port. */
{
- Backend *b = backend_from_proto(default_protocol);
+ const struct Backend_vtable *vt =
+ backend_vt_from_proto(default_protocol);
default_port = 0; /* illegal */
- if (b)
- default_port = b->default_port;
+ if (vt)
+ default_port = vt->default_port;
}
conf_set_int(conf, CONF_logtype, LGTYP_NONE);
@@ -951,8 +951,8 @@ void update_specials_menu(void *frontend)
HMENU new_menu;
int i, j;
- if (back)
- specials = back->get_specials(backhandle);
+ if (backend)
+ specials = backend_get_specials(backend);
else
specials = NULL;
@@ -1975,7 +1975,7 @@ void notify_remote_exit(void *fe)
int exitcode, close_on_exit;
if (!session_closed &&
- (exitcode = back->exitcode(backhandle)) >= 0) {
+ (exitcode = backend_exitcode(backend)) >= 0) {
close_on_exit = conf_get_int(conf, CONF_close_on_exit);
/* Abnormal exits will already have set session_closed and taken
* appropriate action. */
@@ -2161,7 +2161,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
}
break;
case IDM_RESTART:
- if (!back) {
+ if (!backend) {
logevent(NULL, "----- Session restarted -----");
term_pwron(term, FALSE);
start_backend();
@@ -2190,7 +2190,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
prev_conf = conf_copy(conf);
reconfig_result =
- do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0);
+ do_reconfig(hwnd, backend ? backend_cfg_info(backend) : 0);
reconfiguring = FALSE;
if (!reconfig_result) {
conf_free(prev_conf);
@@ -2237,8 +2237,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
setup_clipboards(term, conf);
/* Pass new config data to the back end */
- if (back)
- back->reconfig(backhandle, conf);
+ if (backend)
+ backend_reconfig(backend, conf);
/* Screen size changed ? */
if (conf_get_int(conf, CONF_height) !=
@@ -2414,8 +2414,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
*/
if (i >= n_specials)
break;
- if (back)
- back->special(backhandle, specials[i].code);
+ if (backend)
+ backend_special(backend, specials[i].code);
}
}
break;
@@ -4405,8 +4405,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
return p - output;
}
if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
- if (back)
- back->special(backhandle, TS_BRK);
+ if (backend)
+ backend_special(backend, TS_BRK);
return 0;
}
if (wParam == VK_PAUSE) { /* Break/Pause */
diff --git a/windows/winplink.c b/windows/winplink.c
index a940677f..455b18a7 100644
--- a/windows/winplink.c
+++ b/windows/winplink.c
@@ -76,8 +76,7 @@ DWORD orig_console_mode;
WSAEVENT netevent;
-static Backend *back;
-static void *backhandle;
+static Backend *backend;
static Conf *conf;
int term_ldisc(Terminal *term, int mode)
@@ -254,11 +253,11 @@ int stdin_gotdata(struct handle *h, void *data, int len)
cleanup_exit(0);
}
noise_ultralight(len);
- if (back->connected(backhandle)) {
+ if (backend_connected(backend)) {
if (len > 0) {
- return back->send(backhandle, data, len);
+ return backend_send(backend, data, len);
} else {
- back->special(backhandle, TS_EOF);
+ backend_special(backend, TS_EOF);
return 0;
}
} else
@@ -281,9 +280,9 @@ void stdouterr_sent(struct handle *h, int new_backlog)
(h == stdout_handle ? "output" : "error"), buf);
cleanup_exit(0);
}
- if (back->connected(backhandle)) {
- back->unthrottle(backhandle, (handle_backlog(stdout_handle) +
- handle_backlog(stderr_handle)));
+ if (backend_connected(backend)) {
+ backend_unthrottle(backend, (handle_backlog(stdout_handle) +
+ handle_backlog(stderr_handle)));
}
}
@@ -300,6 +299,7 @@ int main(int argc, char **argv)
int use_subsystem = 0;
int just_test_share_exists = FALSE;
unsigned long now, next, then;
+ const struct Backend_vtable *vt;
dll_hijacking_protection();
@@ -334,10 +334,10 @@ int main(int argc, char **argv)
*/
char *p = getenv("PLINK_PROTOCOL");
if (p) {
- const Backend *b = backend_from_name(p);
- if (b) {
- default_protocol = b->protocol;
- default_port = b->default_port;
+ const struct Backend_vtable *vt = backend_vt_from_name(p);
+ if (vt) {
+ default_protocol = vt->protocol;
+ default_port = vt->default_port;
conf_set_int(conf, CONF_protocol, default_protocol);
conf_set_int(conf, CONF_port, default_port);
}
@@ -432,8 +432,8 @@ int main(int argc, char **argv)
* Select protocol. This is farmed out into a table in a
* separate file to enable an ssh-free variant.
*/
- back = backend_from_proto(conf_get_int(conf, CONF_protocol));
- if (back == NULL) {
+ vt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol));
+ if (vt == NULL) {
fprintf(stderr,
"Internal fault: Unsupported protocol found\n");
return 1;
@@ -460,13 +460,13 @@ int main(int argc, char **argv)
console_provide_logctx(logctx);
if (just_test_share_exists) {
- if (!back->test_for_upstream) {
+ if (!vt->test_for_upstream) {
fprintf(stderr, "Connection sharing not supported for connection "
- "type '%s'\n", back->name);
+ "type '%s'\n", vt->name);
return 1;
}
- if (back->test_for_upstream(conf_get_str(conf, CONF_host),
- conf_get_int(conf, CONF_port), conf))
+ if (vt->test_for_upstream(conf_get_str(conf, CONF_host),
+ conf_get_int(conf, CONF_port), conf))
return 0;
else
return 1;
@@ -487,16 +487,16 @@ int main(int argc, char **argv)
int nodelay = conf_get_int(conf, CONF_tcp_nodelay) &&
(GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);
- error = back->init(NULL, &backhandle, conf,
- conf_get_str(conf, CONF_host),
- conf_get_int(conf, CONF_port),
- &realhost, nodelay,
- conf_get_int(conf, CONF_tcp_keepalives));
+ error = backend_init(vt, NULL, &backend, conf,
+ conf_get_str(conf, CONF_host),
+ conf_get_int(conf, CONF_port),
+ &realhost, nodelay,
+ conf_get_int(conf, CONF_tcp_keepalives));
if (error) {
fprintf(stderr, "Unable to open connection:\n%s", error);
return 1;
}
- back->provide_logctx(backhandle, logctx);
+ backend_provide_logctx(backend, logctx);
sfree(realhost);
}
@@ -532,7 +532,7 @@ int main(int argc, char **argv)
int n;
DWORD ticks;
- if (!sending && back->sendok(backhandle)) {
+ if (!sending && backend_sendok(backend)) {
stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL,
0);
sending = TRUE;
@@ -643,13 +643,13 @@ int main(int argc, char **argv)
sfree(handles);
if (sending)
- handle_unthrottle(stdin_handle, back->sendbuffer(backhandle));
+ handle_unthrottle(stdin_handle, backend_sendbuffer(backend));
- if (!back->connected(backhandle) &&
+ if (!backend_connected(backend) &&
handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0)
break; /* we closed the connection */
}
- exitcode = back->exitcode(backhandle);
+ exitcode = backend_exitcode(backend);
if (exitcode < 0) {
fprintf(stderr, "Remote process exit code unavailable\n");
exitcode = 1; /* this is an error condition */
diff --git a/windows/winser.c b/windows/winser.c
index f0ea94bd..1efac931 100644
--- a/windows/winser.c
+++ b/windows/winser.c
@@ -17,6 +17,7 @@ typedef struct serial_backend_data {
int bufsize;
long clearbreak_time;
int break_in_progress;
+ Backend backend;
} *Serial;
static void serial_terminate(Serial serial)
@@ -198,7 +199,7 @@ static const char *serial_configure(Serial serial, HANDLE serport, Conf *conf)
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *serial_init(void *frontend_handle, void **backend_handle,
+static const char *serial_init(void *frontend_handle, Backend **backend_handle,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive)
{
@@ -212,7 +213,8 @@ static const char *serial_init(void *frontend_handle, void **backend_handle,
serial->out = serial->in = NULL;
serial->bufsize = 0;
serial->break_in_progress = FALSE;
- *backend_handle = serial;
+ serial->backend.vt = &serial_backend;
+ *backend_handle = &serial->backend;
serial->frontend = frontend_handle;
@@ -279,18 +281,18 @@ static const char *serial_init(void *frontend_handle, void **backend_handle,
return NULL;
}
-static void serial_free(void *handle)
+static void serial_free(Backend *be)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
serial_terminate(serial);
expire_timer_context(serial);
sfree(serial);
}
-static void serial_reconfig(void *handle, Conf *conf)
+static void serial_reconfig(Backend *be, Conf *conf)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
serial_configure(serial, serial->port, conf);
@@ -303,9 +305,9 @@ static void serial_reconfig(void *handle, Conf *conf)
/*
* Called to send data down the serial connection.
*/
-static int serial_send(void *handle, const char *buf, int len)
+static int serial_send(Backend *be, const char *buf, int len)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
if (serial->out == NULL)
return 0;
@@ -317,16 +319,16 @@ static int serial_send(void *handle, const char *buf, int len)
/*
* Called to query the current sendability status.
*/
-static int serial_sendbuffer(void *handle)
+static int serial_sendbuffer(Backend *be)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
return serial->bufsize;
}
/*
* Called to set the size of the window
*/
-static void serial_size(void *handle, int width, int height)
+static void serial_size(Backend *be, int width, int height)
{
/* Do nothing! */
return;
@@ -346,9 +348,9 @@ static void serbreak_timer(void *ctx, unsigned long now)
/*
* Send serial special codes.
*/
-static void serial_special(void *handle, Telnet_Special code)
+static void serial_special(Backend *be, Telnet_Special code)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
if (serial->port && code == TS_BRK) {
logevent(serial->frontend, "Starting serial break at user request");
@@ -375,7 +377,7 @@ static void serial_special(void *handle, Telnet_Special code)
* Return a list of the special codes that make sense in this
* protocol.
*/
-static const struct telnet_special *serial_get_specials(void *handle)
+static const struct telnet_special *serial_get_specials(Backend *be)
{
static const struct telnet_special specials[] = {
{"Break", TS_BRK},
@@ -384,24 +386,24 @@ static const struct telnet_special *serial_get_specials(void *handle)
return specials;
}
-static int serial_connected(void *handle)
+static int serial_connected(Backend *be)
{
return 1; /* always connected */
}
-static int serial_sendok(void *handle)
+static int serial_sendok(Backend *be)
{
return 1;
}
-static void serial_unthrottle(void *handle, int backlog)
+static void serial_unthrottle(Backend *be, int backlog)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
if (serial->in)
handle_unthrottle(serial->in, backlog);
}
-static int serial_ldisc(void *handle, int option)
+static int serial_ldisc(Backend *be, int option)
{
/*
* Local editing and local echo are off by default.
@@ -409,19 +411,19 @@ static int serial_ldisc(void *handle, int option)
return 0;
}
-static void serial_provide_ldisc(void *handle, Ldisc *ldisc)
+static void serial_provide_ldisc(Backend *be, Ldisc *ldisc)
{
/* This is a stub. */
}
-static void serial_provide_logctx(void *handle, LogContext *logctx)
+static void serial_provide_logctx(Backend *be, LogContext *logctx)
{
/* This is a stub. */
}
-static int serial_exitcode(void *handle)
+static int serial_exitcode(Backend *be)
{
- Serial serial = (Serial) handle;
+ Serial serial = FROMFIELD(be, struct serial_backend_data, backend);
if (serial->port != INVALID_HANDLE_VALUE)
return -1; /* still connected */
else
@@ -432,12 +434,12 @@ static int serial_exitcode(void *handle)
/*
* cfg_info for Serial does nothing at all.
*/
-static int serial_cfg_info(void *handle)
+static int serial_cfg_info(Backend *be)
{
return 0;
}
-Backend serial_backend = {
+const struct Backend_vtable serial_backend = {
serial_init,
serial_free,
serial_reconfig,
diff --git a/windows/winstuff.h b/windows/winstuff.h
index 727c4b06..87dc48b6 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -596,7 +596,7 @@ void agent_schedule_callback(void (*callback)(void *, void *, int),
/*
* Exports from winser.c.
*/
-extern Backend serial_backend;
+extern const struct Backend_vtable serial_backend;
/*
* Exports from winjump.c.
From 8dfb2a118618618fe38d129aa42ece6803ea1ac6 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 12 Sep 2018 09:10:51 +0100
Subject: [PATCH 393/607] Introduce a typedef for frontend handles.
This is another major source of unexplained 'void *' parameters
throughout the code.
In particular, the currently unused testback.c actually gave the wrong
pointer type to its internal store of the frontend handle - it cast
the input void * to a Terminal *, from which it got implicitly cast
back again when calling from_backend, and nobody noticed. Now it uses
the right type internally as well as externally.
---
be_misc.c | 2 +-
callback.c | 8 +-
defs.h | 2 +
fuzzterm.c | 52 ++++----
ldisc.c | 2 +-
ldisc.h | 2 +-
logging.c | 4 +-
misc.c | 2 +-
network.h | 5 +-
proxy.c | 2 +-
pscp.c | 9 +-
psftp.c | 9 +-
putty.h | 96 +++++++-------
raw.c | 6 +-
rlogin.c | 6 +-
ssh.c | 6 +-
ssh.h | 2 +-
telnet.c | 6 +-
terminal.c | 2 +-
terminal.h | 2 +-
testback.c | 14 +-
unix/gtkapp.c | 4 +-
unix/gtkcomm.c | 2 +-
unix/gtkdlg.c | 12 +-
unix/gtkmain.c | 2 +-
unix/gtkwin.c | 322 +++++++++++++++++++--------------------------
unix/unix.h | 16 +--
unix/uxcons.c | 18 +--
unix/uxpgnt.c | 4 +-
unix/uxplink.c | 12 +-
unix/uxpty.c | 4 +-
unix/uxser.c | 6 +-
unix/uxsftp.c | 2 +-
windows/wincons.c | 18 +--
windows/windlg.c | 10 +-
windows/window.c | 70 +++++-----
windows/winplink.c | 12 +-
windows/winser.c | 6 +-
windows/winsftp.c | 2 +-
windows/winstuff.h | 2 +-
40 files changed, 357 insertions(+), 406 deletions(-)
diff --git a/be_misc.c b/be_misc.c
index 9fcfec55..4774bd70 100644
--- a/be_misc.c
+++ b/be_misc.c
@@ -9,7 +9,7 @@
#include "putty.h"
#include "network.h"
-void backend_socket_log(void *frontend, int type, SockAddr addr, int port,
+void backend_socket_log(Frontend *frontend, int type, SockAddr addr, int port,
const char *error_msg, int error_code, Conf *conf,
int session_started)
{
diff --git a/callback.c b/callback.c
index 10f923d0..0a05f624 100644
--- a/callback.c
+++ b/callback.c
@@ -17,13 +17,13 @@ struct callback {
struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL;
toplevel_callback_notify_fn_t notify_frontend = NULL;
-void *frontend = NULL;
+void *notify_ctx = NULL;
void request_callback_notifications(toplevel_callback_notify_fn_t fn,
- void *fr)
+ void *ctx)
{
notify_frontend = fn;
- frontend = fr;
+ notify_ctx = ctx;
}
static void run_idempotent_callback(void *ctx)
@@ -87,7 +87,7 @@ void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
* callback keeps re-scheduling itself.
*/
if (notify_frontend && !cbhead && !cbcurr)
- notify_frontend(frontend);
+ notify_frontend(notify_ctx);
if (cbtail)
cbtail->next = cb;
diff --git a/defs.h b/defs.h
index 43f56dfe..53ded962 100644
--- a/defs.h
+++ b/defs.h
@@ -49,6 +49,8 @@ typedef struct Backend_vtable Backend_vtable;
typedef struct Ldisc_tag Ldisc;
typedef struct LogContext_tag LogContext;
+typedef struct Frontend Frontend;
+
typedef struct ssh_tag *Ssh;
/* Note indirection: for historical reasons (it used to be closer to
diff --git a/fuzzterm.c b/fuzzterm.c
index cdefb0d9..fda9f6c8 100644
--- a/fuzzterm.c
+++ b/fuzzterm.c
@@ -38,12 +38,12 @@ int main(int argc, char **argv)
return 0;
}
-int from_backend(void *frontend, int is_stderr, const void *data, int len)
+int from_backend(Frontend *frontend, int is_stderr, const void *data, int len)
{ return 0; }
/* functions required by terminal.c */
-void request_resize(void *frontend, int x, int y) { }
+void request_resize(Frontend *frontend, int x, int y) { }
void do_text(Context ctx, int x, int y, wchar_t * text, int len,
unsigned long attr, int lattr, truecolour tc)
{
@@ -67,40 +67,40 @@ void do_cursor(Context ctx, int x, int y, wchar_t * text, int len,
printf("\n");
}
int char_width(Context ctx, int uc) { return 1; }
-void set_title(void *frontend, char *t) { }
-void set_icon(void *frontend, char *t) { }
-void set_sbar(void *frontend, int a, int b, int c) { }
+void set_title(Frontend *frontend, char *t) { }
+void set_icon(Frontend *frontend, char *t) { }
+void set_sbar(Frontend *frontend, int a, int b, int c) { }
void ldisc_send(Ldisc *ldisc, const void *buf, int len, int interactive) {}
void ldisc_echoedit_update(Ldisc *ldisc) {}
-Context get_ctx(void *frontend) {
+Context get_ctx(Frontend *frontend) {
static char x;
return &x;
}
void free_ctx(Context ctx) { }
-void palette_set(void *frontend, int a, int b, int c, int d) { }
-void palette_reset(void *frontend) { }
-int palette_get(void *frontend, int n, int *r, int *g, int *b) {return FALSE;}
-void write_clip(void *frontend, int clipboard,
+void palette_set(Frontend *frontend, int a, int b, int c, int d) { }
+void palette_reset(Frontend *frontend) { }
+int palette_get(Frontend *frontend, int n, int *r, int *g, int *b) {return FALSE;}
+void write_clip(Frontend *frontend, int clipboard,
wchar_t *a, int *b, truecolour *c, int d, int e) { }
-void set_raw_mouse_mode(void *frontend, int m) { }
-void frontend_request_paste(void *frontend, int clipboard) { }
-void do_beep(void *frontend, int a) { }
-void sys_cursor(void *frontend, int x, int y) { }
+void set_raw_mouse_mode(Frontend *frontend, int m) { }
+void frontend_request_paste(Frontend *frontend, int clipboard) { }
+void do_beep(Frontend *frontend, int a) { }
+void sys_cursor(Frontend *frontend, int x, int y) { }
void modalfatalbox(const char *fmt, ...) { exit(0); }
void nonfatal(const char *fmt, ...) { }
-void set_iconic(void *frontend, int iconic) { }
-void move_window(void *frontend, int x, int y) { }
-void set_zorder(void *frontend, int top) { }
-void refresh_window(void *frontend) { }
-void set_zoomed(void *frontend, int zoomed) { }
-int is_iconic(void *frontend) { return 0; }
-void get_window_pos(void *frontend, int *x, int *y) { *x = 0; *y = 0; }
-void get_window_pixels(void *frontend, int *x, int *y) { *x = 0; *y = 0; }
-char *get_window_title(void *frontend, int icon) { return "moo"; }
-int frontend_is_utf8(void *frontend) { return TRUE; }
+void set_iconic(Frontend *frontend, int iconic) { }
+void move_window(Frontend *frontend, int x, int y) { }
+void set_zorder(Frontend *frontend, int top) { }
+void refresh_window(Frontend *frontend) { }
+void set_zoomed(Frontend *frontend, int zoomed) { }
+int is_iconic(Frontend *frontend) { return 0; }
+void get_window_pos(Frontend *frontend, int *x, int *y) { *x = 0; *y = 0; }
+void get_window_pixels(Frontend *frontend, int *x, int *y) { *x = 0; *y = 0; }
+char *get_window_title(Frontend *frontend, int icon) { return "moo"; }
+int frontend_is_utf8(Frontend *frontend) { return TRUE; }
/* needed by timing.c */
void timer_change_notify(unsigned long next) { }
@@ -142,8 +142,8 @@ int dlg_coloursel_results(union control *ctrl, void *dlg,
void dlg_refresh(union control *ctrl, void *dlg) { }
/* miscellany */
-void logevent(void *frontend, const char *msg) { }
-int askappend(void *frontend, Filename *filename,
+void logevent(Frontend *frontend, const char *msg) { }
+int askappend(Frontend *frontend, Filename *filename,
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
const char *const appname = "FuZZterm";
diff --git a/ldisc.c b/ldisc.c
index 3596ea63..e17b13c7 100644
--- a/ldisc.c
+++ b/ldisc.c
@@ -78,7 +78,7 @@ static void bsb(Ldisc *ldisc, int n)
#define KCTRL(x) ((x^'@') | 0x100)
Ldisc *ldisc_create(Conf *conf, Terminal *term,
- Backend *backend, void *frontend)
+ Backend *backend, Frontend *frontend)
{
Ldisc *ldisc = snew(Ldisc);
diff --git a/ldisc.h b/ldisc.h
index af7afde3..e3d043b5 100644
--- a/ldisc.h
+++ b/ldisc.h
@@ -11,7 +11,7 @@
struct Ldisc_tag {
Terminal *term;
Backend *backend;
- void *frontend;
+ Frontend *frontend;
/*
* Values cached out of conf.
diff --git a/logging.c b/logging.c
index 75c457e3..b1fb638b 100644
--- a/logging.c
+++ b/logging.c
@@ -17,7 +17,7 @@ struct LogContext_tag {
enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state;
bufchain queue;
Filename *currlogfilename;
- void *frontend;
+ Frontend *frontend;
Conf *conf;
int logtype; /* cached out of conf */
};
@@ -367,7 +367,7 @@ void log_packet(LogContext *ctx, int direction, int type,
logflush(ctx);
}
-LogContext *log_init(void *frontend, Conf *conf)
+LogContext *log_init(Frontend *frontend, Conf *conf)
{
LogContext *ctx = snew(LogContext);
ctx->lgfp = NULL;
diff --git a/misc.c b/misc.c
index 92890322..fdd5635c 100644
--- a/misc.c
+++ b/misc.c
@@ -216,7 +216,7 @@ char *host_strduptrim(const char *s)
return dupstr(s);
}
-prompts_t *new_prompts(void *frontend)
+prompts_t *new_prompts(Frontend *frontend)
{
prompts_t *p = snew(prompts_t);
p->prompts = NULL;
diff --git a/network.h b/network.h
index 9914ce34..79f44f5e 100644
--- a/network.h
+++ b/network.h
@@ -95,7 +95,8 @@ Socket new_connection(SockAddr addr, const char *hostname,
Socket new_listener(const char *srcaddr, int port, Plug plug,
int local_host_only, Conf *conf, int addressfamily);
SockAddr name_lookup(const char *host, int port, char **canonicalname,
- Conf *conf, int addressfamily, void *frontend_for_logging,
+ Conf *conf, int addressfamily,
+ Frontend *frontend_for_logging,
const char *lookup_reason_for_logging);
int proxy_for_destination (SockAddr addr, const char *hostname, int port,
Conf *conf);
@@ -221,7 +222,7 @@ extern Plug nullplug;
/*
* Exports from be_misc.c.
*/
-void backend_socket_log(void *frontend, int type, SockAddr addr, int port,
+void backend_socket_log(Frontend *frontend, int type, SockAddr addr, int port,
const char *error_msg, int error_code, Conf *conf,
int session_started);
void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len);
diff --git a/proxy.c b/proxy.c
index 6ccd4025..f55973fc 100644
--- a/proxy.c
+++ b/proxy.c
@@ -368,7 +368,7 @@ static char *dns_log_msg(const char *host, int addressfamily,
}
SockAddr name_lookup(const char *host, int port, char **canonicalname,
- Conf *conf, int addressfamily, void *frontend,
+ Conf *conf, int addressfamily, Frontend *frontend,
const char *reason)
{
char *logmsg;
diff --git a/pscp.c b/pscp.c
index 854e6815..594f119f 100644
--- a/pscp.c
+++ b/pscp.c
@@ -118,7 +118,7 @@ void nonfatal(const char *fmt, ...)
sfree(str2);
errs++;
}
-void connection_fatal(void *frontend, const char *fmt, ...)
+void connection_fatal(Frontend *frontend, const char *fmt, ...)
{
char *str, *str2;
va_list ap;
@@ -157,7 +157,8 @@ static unsigned char *outptr; /* where to put the data */
static unsigned outlen; /* how much data required */
static unsigned char *pending = NULL; /* any spare data */
static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
-int from_backend(void *frontend, int is_stderr, const void *data, int datalen)
+int from_backend(Frontend *frontend, int is_stderr,
+ const void *data, int datalen)
{
unsigned char *p = (unsigned char *) data;
unsigned len = (unsigned) datalen;
@@ -195,7 +196,7 @@ int from_backend(void *frontend, int is_stderr, const void *data, int datalen)
return 0;
}
-int from_backend_untrusted(void *frontend_handle, const void *data, int len)
+int from_backend_untrusted(Frontend *frontend, const void *data, int len)
{
/*
* No "untrusted" output should get here (the way the code is
@@ -204,7 +205,7 @@ int from_backend_untrusted(void *frontend_handle, const void *data, int len)
assert(!"Unexpected call to from_backend_untrusted()");
return 0; /* not reached */
}
-int from_backend_eof(void *frontend)
+int from_backend_eof(Frontend *frontend)
{
/*
* We usually expect to be the party deciding when to close the
diff --git a/psftp.c b/psftp.c
index c2d23832..d57b118e 100644
--- a/psftp.c
+++ b/psftp.c
@@ -2466,7 +2466,7 @@ void nonfatal(const char *fmt, ...)
fputs(str2, stderr);
sfree(str2);
}
-void connection_fatal(void *frontend, const char *fmt, ...)
+void connection_fatal(Frontend *frontend, const char *fmt, ...)
{
char *str, *str2;
va_list ap;
@@ -2506,7 +2506,8 @@ static unsigned char *outptr; /* where to put the data */
static unsigned outlen; /* how much data required */
static unsigned char *pending = NULL; /* any spare data */
static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
-int from_backend(void *frontend, int is_stderr, const void *data, int datalen)
+int from_backend(Frontend *frontend, int is_stderr,
+ const void *data, int datalen)
{
unsigned char *p = (unsigned char *) data;
unsigned len = (unsigned) datalen;
@@ -2550,7 +2551,7 @@ int from_backend(void *frontend, int is_stderr, const void *data, int datalen)
return 0;
}
-int from_backend_untrusted(void *frontend_handle, const void *data, int len)
+int from_backend_untrusted(Frontend *frontend, const void *data, int len)
{
/*
* No "untrusted" output should get here (the way the code is
@@ -2559,7 +2560,7 @@ int from_backend_untrusted(void *frontend_handle, const void *data, int len)
assert(!"Unexpected call to from_backend_untrusted()");
return 0; /* not reached */
}
-int from_backend_eof(void *frontend)
+int from_backend_eof(Frontend *frontend)
{
/*
* We expect to be the party deciding when to close the
diff --git a/putty.h b/putty.h
index 2f53d645..1a8c30ce 100644
--- a/putty.h
+++ b/putty.h
@@ -445,7 +445,7 @@ struct Backend {
const Backend_vtable *vt;
};
struct Backend_vtable {
- const char *(*init) (void *frontend_handle, Backend **backend_out,
+ const char *(*init) (Frontend *frontend, Backend **backend_out,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive);
@@ -608,11 +608,11 @@ typedef struct {
size_t n_prompts; /* May be zero (in which case display the foregoing,
* if any, and return success) */
prompt_t **prompts;
- void *frontend;
+ Frontend *frontend;
void *data; /* slot for housekeeping data, managed by
* get_userpass_input(); initially NULL */
} prompts_t;
-prompts_t *new_prompts(void *frontend);
+prompts_t *new_prompts(Frontend *frontend);
void add_prompt(prompts_t *p, char *promptstr, int echo);
void prompt_set_result(prompt_t *pr, const char *newstr);
void prompt_ensure_result_size(prompt_t *pr, int len);
@@ -673,7 +673,7 @@ enum { ALL_CLIPBOARDS(CLIP_ID) N_CLIPBOARDS };
/*
* Exports from the front end.
*/
-void request_resize(void *frontend, int, int);
+void request_resize(Frontend *frontend, int, int);
void do_text(Context, int, int, wchar_t *, int, unsigned long, int,
truecolour);
void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int,
@@ -682,46 +682,46 @@ int char_width(Context ctx, int uc);
#ifdef OPTIMISE_SCROLL
void do_scroll(Context, int, int, int);
#endif
-void set_title(void *frontend, char *);
-void set_icon(void *frontend, char *);
-void set_sbar(void *frontend, int, int, int);
-Context get_ctx(void *frontend);
+void set_title(Frontend *frontend, char *);
+void set_icon(Frontend *frontend, char *);
+void set_sbar(Frontend *frontend, int, int, int);
+Context get_ctx(Frontend *frontend);
void free_ctx(Context);
-void palette_set(void *frontend, int, int, int, int);
-void palette_reset(void *frontend);
-int palette_get(void *frontend, int n, int *r, int *g, int *b);
-void write_clip(void *frontend, int clipboard, wchar_t *, int *,
+void palette_set(Frontend *frontend, int, int, int, int);
+void palette_reset(Frontend *frontend);
+int palette_get(Frontend *frontend, int n, int *r, int *g, int *b);
+void write_clip(Frontend *frontend, int clipboard, wchar_t *, int *,
truecolour *, int, int);
-void optimised_move(void *frontend, int, int, int);
-void set_raw_mouse_mode(void *frontend, int);
-void connection_fatal(void *frontend, const char *, ...);
+void optimised_move(Frontend *frontend, int, int, int);
+void set_raw_mouse_mode(Frontend *frontend, int);
+void connection_fatal(Frontend *frontend, const char *, ...);
void nonfatal(const char *, ...);
void modalfatalbox(const char *, ...);
#ifdef macintosh
#pragma noreturn(modalfatalbox)
#endif
-void do_beep(void *frontend, int);
-void begin_session(void *frontend);
-void sys_cursor(void *frontend, int x, int y);
-void frontend_request_paste(void *frontend, int clipboard);
-void frontend_keypress(void *frontend);
-void frontend_echoedit_update(void *frontend, int echo, int edit);
+void do_beep(Frontend *frontend, int);
+void begin_session(Frontend *frontend);
+void sys_cursor(Frontend *frontend, int x, int y);
+void frontend_request_paste(Frontend *frontend, int clipboard);
+void frontend_keypress(Frontend *frontend);
+void frontend_echoedit_update(Frontend *frontend, int echo, int edit);
/* It's the backend's responsibility to invoke this at the start of a
* connection, if necessary; it can also invoke it later if the set of
* special commands changes. It does not need to invoke it at session
* shutdown. */
-void update_specials_menu(void *frontend);
-int from_backend(void *frontend, int is_stderr, const void *data, int len);
-int from_backend_untrusted(void *frontend, const void *data, int len);
+void update_specials_menu(Frontend *frontend);
+int from_backend(Frontend *frontend, int is_stderr, const void *data, int len);
+int from_backend_untrusted(Frontend *frontend, const void *data, int len);
/* Called when the back end wants to indicate that EOF has arrived on
* the server-to-client stream. Returns FALSE to indicate that we
* intend to keep the session open in the other direction, or TRUE to
* indicate that if they're closing so are we. */
-int from_backend_eof(void *frontend);
-void notify_remote_exit(void *frontend);
+int from_backend_eof(Frontend *frontend);
+void notify_remote_exit(Frontend *frontend);
/* Get a sensible value for a tty mode. NULL return = don't set.
* Otherwise, returned value should be freed by caller. */
-char *get_ttymode(void *frontend, const char *mode);
+char *get_ttymode(Frontend *frontend, const char *mode);
/*
* >0 = `got all results, carry on'
* 0 = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?)
@@ -730,15 +730,15 @@ char *get_ttymode(void *frontend, const char *mode);
int get_userpass_input(prompts_t *p, bufchain *input);
#define OPTIMISE_IS_SCROLL 1
-void set_iconic(void *frontend, int iconic);
-void move_window(void *frontend, int x, int y);
-void set_zorder(void *frontend, int top);
-void refresh_window(void *frontend);
-void set_zoomed(void *frontend, int zoomed);
-int is_iconic(void *frontend);
-void get_window_pos(void *frontend, int *x, int *y);
-void get_window_pixels(void *frontend, int *x, int *y);
-char *get_window_title(void *frontend, int icon);
+void set_iconic(Frontend *frontend, int iconic);
+void move_window(Frontend *frontend, int x, int y);
+void set_zorder(Frontend *frontend, int top);
+void refresh_window(Frontend *frontend);
+void set_zoomed(Frontend *frontend, int zoomed);
+int is_iconic(Frontend *frontend);
+void get_window_pos(Frontend *frontend, int *x, int *y);
+void get_window_pixels(Frontend *frontend, int *x, int *y);
+char *get_window_title(Frontend *frontend, int icon);
/* Hint from backend to frontend about time-consuming operations.
* Initial state is assumed to be BUSY_NOT. */
enum {
@@ -748,8 +748,8 @@ enum {
stuff is suspended */
BUSY_CPU /* Locally busy (e.g. crypto); user interaction suspended */
};
-void set_busy_status(void *frontend, int status);
-int frontend_is_utf8(void *frontend);
+void set_busy_status(Frontend *frontend, int status);
+int frontend_is_utf8(Frontend *frontend);
void cleanup_exit(int);
@@ -1105,7 +1105,7 @@ FontSpec *platform_default_fontspec(const char *name);
* Exports from terminal.c.
*/
-Terminal *term_init(Conf *, struct unicode_data *, void *);
+Terminal *term_init(Conf *, struct unicode_data *, Frontend *);
void term_free(Terminal *);
void term_size(Terminal *, int, int, int);
void term_paint(Terminal *, Context, int, int, int, int, int);
@@ -1142,7 +1142,7 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);
/*
* Exports from logging.c.
*/
-LogContext *log_init(void *frontend, Conf *conf);
+LogContext *log_init(Frontend *frontend, Conf *conf);
void log_free(LogContext *logctx);
void log_reconfig(LogContext *logctx, Conf *conf);
void logfopen(LogContext *logctx);
@@ -1196,7 +1196,7 @@ extern const struct Backend_vtable ssh_backend;
/*
* Exports from ldisc.c.
*/
-Ldisc *ldisc_create(Conf *, Terminal *, Backend *, void *);
+Ldisc *ldisc_create(Conf *, Terminal *, Backend *, Frontend *);
void ldisc_configure(Ldisc *, Conf *);
void ldisc_free(Ldisc *);
void ldisc_send(Ldisc *, const void *buf, int len, int interactive);
@@ -1324,7 +1324,7 @@ int wc_unescape(char *output, const char *wildcard);
/*
* Exports from frontend (windlg.c etc)
*/
-void logevent(void *frontend, const char *);
+void logevent(Frontend *frontend, const char *);
void pgp_fingerprints(void);
/*
* verify_ssh_host_key() can return one of three values:
@@ -1338,7 +1338,7 @@ void pgp_fingerprints(void);
* back via the provided function with a result that's either 0
* or +1'.
*/
-int verify_ssh_host_key(void *frontend, char *host, int port,
+int verify_ssh_host_key(Frontend *frontend, char *host, int port,
const char *keytype, char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx);
/*
@@ -1354,9 +1354,9 @@ int have_ssh_host_key(const char *host, int port, const char *keytype);
* warning threshold because that's all we have cached, but at least
* one acceptable algorithm is available that we don't have cached.)
*/
-int askalg(void *frontend, const char *algtype, const char *algname,
+int askalg(Frontend *frontend, const char *algtype, const char *algname,
void (*callback)(void *ctx, int result), void *ctx);
-int askhk(void *frontend, const char *algname, const char *betteralgs,
+int askhk(Frontend *frontend, const char *algname, const char *betteralgs,
void (*callback)(void *ctx, int result), void *ctx);
/*
* askappend can return four values:
@@ -1366,7 +1366,7 @@ int askhk(void *frontend, const char *algname, const char *betteralgs,
* - 0 means cancel logging for this session
* - -1 means please wait.
*/
-int askappend(void *frontend, Filename *filename,
+int askappend(Frontend *frontend, Filename *filename,
void (*callback)(void *ctx, int result), void *ctx);
/*
@@ -1631,9 +1631,9 @@ struct IdempotentCallback {
};
void queue_idempotent_callback(struct IdempotentCallback *ic);
-typedef void (*toplevel_callback_notify_fn_t)(void *frontend);
+typedef void (*toplevel_callback_notify_fn_t)(void *ctx);
void request_callback_notifications(toplevel_callback_notify_fn_t notify,
- void *frontend);
+ void *ctx);
/*
* Define no-op macros for the jump list functions, on platforms that
diff --git a/raw.c b/raw.c
index 56eb0385..df280139 100644
--- a/raw.c
+++ b/raw.c
@@ -14,7 +14,7 @@ typedef struct raw_backend_data {
Socket s;
int closed_on_socket_error;
int bufsize;
- void *frontend;
+ Frontend *frontend;
int sent_console_eof, sent_socket_eof, session_started;
Conf *conf;
@@ -117,7 +117,7 @@ static const Plug_vtable Raw_plugvt = {
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *raw_init(void *frontend_handle, Backend **backend_handle,
+static const char *raw_init(Frontend *frontend, Backend **backend_handle,
Conf *conf,
const char *host, int port, char **realhost,
int nodelay, int keepalive)
@@ -139,7 +139,7 @@ static const char *raw_init(void *frontend_handle, Backend **backend_handle,
raw->session_started = FALSE;
raw->conf = conf_copy(conf);
- raw->frontend = frontend_handle;
+ raw->frontend = frontend;
addressfamily = conf_get_int(conf, CONF_addressfamily);
/*
diff --git a/rlogin.c b/rlogin.c
index 978e3084..05dd7dbb 100644
--- a/rlogin.c
+++ b/rlogin.c
@@ -18,7 +18,7 @@ typedef struct rlogin_tag {
int firstbyte;
int cansize;
int term_width, term_height;
- void *frontend;
+ Frontend *frontend;
Conf *conf;
@@ -148,7 +148,7 @@ static const Plug_vtable Rlogin_plugvt = {
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *rlogin_init(void *frontend_handle, Backend **backend_handle,
+static const char *rlogin_init(Frontend *frontend, Backend **backend_handle,
Conf *conf,
const char *host, int port, char **realhost,
int nodelay, int keepalive)
@@ -165,7 +165,7 @@ static const char *rlogin_init(void *frontend_handle, Backend **backend_handle,
rlogin->backend.vt = &rlogin_backend;
rlogin->s = NULL;
rlogin->closed_on_socket_error = FALSE;
- rlogin->frontend = frontend_handle;
+ rlogin->frontend = frontend;
rlogin->term_width = conf_get_int(conf, CONF_width);
rlogin->term_height = conf_get_int(conf, CONF_height);
rlogin->firstbyte = 1;
diff --git a/ssh.c b/ssh.c
index 952b901f..1009e038 100644
--- a/ssh.c
+++ b/ssh.c
@@ -729,7 +729,7 @@ struct ssh_tag {
int echoing, editing;
int session_started;
- void *frontend;
+ Frontend *frontend;
int ospeed, ispeed; /* temporaries */
int term_width, term_height;
@@ -10665,7 +10665,7 @@ static void ssh_cache_conf_values(Ssh ssh)
*
* Returns an error message, or NULL on success.
*/
-static const char *ssh_init(void *frontend_handle, Backend **backend_handle,
+static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
Conf *conf,
const char *host, int port, char **realhost,
int nodelay, int keepalive)
@@ -10781,7 +10781,7 @@ static const char *ssh_init(void *frontend_handle, Backend **backend_handle,
ssh->backend.vt = &ssh_backend;
*backend_handle = &ssh->backend;
- ssh->frontend = frontend_handle;
+ ssh->frontend = frontend;
ssh->term_width = conf_get_int(ssh->conf, CONF_width);
ssh->term_height = conf_get_int(ssh->conf, CONF_height);
diff --git a/ssh.h b/ssh.h
index 4529bbcc..3fe8266b 100644
--- a/ssh.h
+++ b/ssh.h
@@ -660,7 +660,7 @@ int random_byte(void);
void random_add_noise(void *noise, int length);
void random_add_heavynoise(void *noise, int length);
-void logevent(void *, const char *);
+void logevent(Frontend *, const char *);
struct PortForwarding;
diff --git a/telnet.c b/telnet.c
index 673c152f..7e62e301 100644
--- a/telnet.c
+++ b/telnet.c
@@ -172,7 +172,7 @@ typedef struct telnet_tag {
Socket s;
int closed_on_socket_error;
- void *frontend;
+ Frontend *frontend;
Ldisc *ldisc;
int term_width, term_height;
@@ -705,7 +705,7 @@ static const Plug_vtable Telnet_plugvt = {
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *telnet_init(void *frontend_handle, Backend **backend_handle,
+static const char *telnet_init(Frontend *frontend, Backend **backend_handle,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive)
{
@@ -726,7 +726,7 @@ static const char *telnet_init(void *frontend_handle, Backend **backend_handle,
telnet->activated = FALSE;
telnet->sb_buf = NULL;
telnet->sb_size = 0;
- telnet->frontend = frontend_handle;
+ telnet->frontend = frontend;
telnet->term_width = conf_get_int(telnet->conf, CONF_width);
telnet->term_height = conf_get_int(telnet->conf, CONF_height);
telnet->state = TOP_LEVEL;
diff --git a/terminal.c b/terminal.c
index 1dc83dc1..a8bd6fc7 100644
--- a/terminal.c
+++ b/terminal.c
@@ -1639,7 +1639,7 @@ const optionalrgb optionalrgb_none = {0, 0, 0, 0};
* Initialise the terminal.
*/
Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata,
- void *frontend)
+ Frontend *frontend)
{
Terminal *term;
diff --git a/terminal.h b/terminal.h
index 8ec256d4..f0727cf2 100644
--- a/terminal.h
+++ b/terminal.h
@@ -231,7 +231,7 @@ struct terminal_tag {
Ldisc *ldisc;
- void *frontend;
+ Frontend *frontend;
LogContext *logctx;
diff --git a/testback.c b/testback.c
index f3e9e35f..01a7e6e0 100644
--- a/testback.c
+++ b/testback.c
@@ -32,9 +32,9 @@
#include "putty.h"
-static const char *null_init(void *, Backend **, Conf *, const char *, int,
+static const char *null_init(Frontend *, Backend **, Conf *, const char *, int,
char **, int, int);
-static const char *loop_init(void *, Backend **, Conf *, const char *, int,
+static const char *loop_init(Frontend *, Backend **, Conf *, const char *, int,
char **, int, int);
static void null_free(Backend *);
static void loop_free(Backend *);
@@ -69,23 +69,23 @@ const struct Backend_vtable loop_backend = {
};
struct loop_state {
- Terminal *term;
+ Frontend *frontend;
Backend backend;
};
-static const char *null_init(void *frontend_handle, Backend **backend_handle,
+static const char *null_init(Frontend *frontend, Backend **backend_handle,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive) {
*backend_handle = NULL;
return NULL;
}
-static const char *loop_init(void *frontend_handle, Backend **backend_handle,
+static const char *loop_init(Frontend *frontend, Backend **backend_handle,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive) {
struct loop_state *st = snew(struct loop_state);
- st->term = frontend_handle;
+ st->frontend = frontend;
*backend_handle = &st->backend;
return NULL;
}
@@ -114,7 +114,7 @@ static int null_send(Backend *be, const char *buf, int len) {
static int loop_send(Backend *be, const char *buf, int len) {
struct loop_state *st = FROMFIELD(be, struct loop_state, backend);
- return from_backend(st->term, 0, buf, len);
+ return from_backend(st->frontend, 0, buf, len);
}
static int null_sendbuffer(Backend *be) {
diff --git a/unix/gtkapp.c b/unix/gtkapp.c
index d520565d..f79e5e9a 100644
--- a/unix/gtkapp.c
+++ b/unix/gtkapp.c
@@ -99,7 +99,7 @@ int main(int argc, char **argv)
fprintf(stderr, "GtkApplication frontend doesn't work pre-GTK3\n");
return 1;
}
-GtkWidget *make_gtk_toplevel_window(void *frontend) { return NULL; }
+GtkWidget *make_gtk_toplevel_window(Frontend *frontend) { return NULL; }
void launch_duplicate_session(Conf *conf) {}
void launch_new_session(void) {}
void launch_saved_session(const char *str) {}
@@ -204,7 +204,7 @@ WIN_ACTION_LIST(WIN_ACTION_ENTRY)
};
static GtkApplication *app;
-GtkWidget *make_gtk_toplevel_window(void *frontend)
+GtkWidget *make_gtk_toplevel_window(Frontend *frontend)
{
GtkWidget *win = gtk_application_window_new(app);
g_action_map_add_action_entries(G_ACTION_MAP(win),
diff --git a/unix/gtkcomm.c b/unix/gtkcomm.c
index 9c3d1c53..6b6d19bb 100644
--- a/unix/gtkcomm.c
+++ b/unix/gtkcomm.c
@@ -221,7 +221,7 @@ static gint idle_toplevel_callback_func(gpointer data)
return TRUE;
}
-static void notify_toplevel_callback(void *frontend)
+static void notify_toplevel_callback(void *vctx)
{
if (!idle_fn_scheduled) {
toplevel_callback_idle_id =
diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c
index 737163f2..f1425daf 100644
--- a/unix/gtkdlg.c
+++ b/unix/gtkdlg.c
@@ -3442,7 +3442,7 @@ struct verify_ssh_host_key_result_ctx {
char *keystr;
void (*callback)(void *callback_ctx, int result);
void *callback_ctx;
- void *frontend;
+ Frontend *frontend;
};
static void verify_ssh_host_key_result_callback(void *vctx, int result)
@@ -3483,7 +3483,7 @@ static void verify_ssh_host_key_result_callback(void *vctx, int result)
sfree(ctx);
}
-int verify_ssh_host_key(void *frontend, char *host, int port,
+int verify_ssh_host_key(Frontend *frontend, char *host, int port,
const char *keytype, char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx)
{
@@ -3560,7 +3560,7 @@ int verify_ssh_host_key(void *frontend, char *host, int port,
struct simple_prompt_result_ctx {
void (*callback)(void *callback_ctx, int result);
void *callback_ctx;
- void *frontend;
+ Frontend *frontend;
enum DialogSlot dialog_slot;
};
@@ -3584,7 +3584,7 @@ static void simple_prompt_result_callback(void *vctx, int result)
* Ask whether the selected algorithm is acceptable (since it was
* below the configured 'warn' threshold).
*/
-int askalg(void *frontend, const char *algtype, const char *algname,
+int askalg(Frontend *frontend, const char *algtype, const char *algname,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char msg[] =
@@ -3616,7 +3616,7 @@ int askalg(void *frontend, const char *algtype, const char *algname,
return -1; /* dialog still in progress */
}
-int askhk(void *frontend, const char *algname, const char *betteralgs,
+int askhk(Frontend *frontend, const char *algname, const char *betteralgs,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char msg[] =
@@ -4059,7 +4059,7 @@ void logevent_dlg(void *estuff, const char *string)
}
}
-int askappend(void *frontend, Filename *filename,
+int askappend(Frontend *frontend, Filename *filename,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char msgtemplate[] =
diff --git a/unix/gtkmain.c b/unix/gtkmain.c
index 906d029a..a97f7415 100644
--- a/unix/gtkmain.c
+++ b/unix/gtkmain.c
@@ -549,7 +549,7 @@ int do_cmdline(int argc, char **argv, int do_everything, Conf *conf)
return err;
}
-GtkWidget *make_gtk_toplevel_window(void *frontend)
+GtkWidget *make_gtk_toplevel_window(Frontend *frontend)
{
return gtk_window_new(GTK_WINDOW_TOPLEVEL);
}
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 6867ac07..aebeff8f 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -76,7 +76,7 @@ struct clipboard_data_instance {
#endif
struct clipboard_state {
- struct gui_data *inst;
+ Frontend *inst;
int clipboard;
GdkAtom atom;
#ifdef JUST_USE_GTK_CLIPBOARD_UTF8
@@ -88,7 +88,7 @@ struct clipboard_state {
#endif
};
-struct gui_data {
+struct Frontend {
GtkWidget *window, *area, *sbar;
gboolean sbar_visible;
gboolean drawing_area_got_size, drawing_area_realised;
@@ -182,7 +182,7 @@ struct gui_data {
#endif
};
-static void cache_conf_values(struct gui_data *inst)
+static void cache_conf_values(Frontend *inst)
{
inst->bold_style = conf_get_int(inst->conf, CONF_bold_style);
inst->window_border = conf_get_int(inst->conf, CONF_window_border);
@@ -200,31 +200,31 @@ static void cache_conf_values(struct gui_data *inst)
}
struct draw_ctx {
- struct gui_data *inst;
+ Frontend *inst;
unifont_drawctx uctx;
};
static int send_raw_mouse;
-static void start_backend(struct gui_data *inst);
+static void start_backend(Frontend *inst);
static void exit_callback(void *vinst);
-static void destroy_inst_connection(struct gui_data *inst);
-static void delete_inst(struct gui_data *inst);
+static void destroy_inst_connection(Frontend *inst);
+static void delete_inst(Frontend *inst);
static void post_fatal_message_box_toplevel(void *vctx)
{
- struct gui_data *inst = (struct gui_data *)vctx;
+ Frontend *inst = (Frontend *)vctx;
gtk_widget_destroy(inst->window);
}
static void post_fatal_message_box(void *vctx, int result)
{
- struct gui_data *inst = (struct gui_data *)vctx;
+ Frontend *inst = (Frontend *)vctx;
unregister_dialog(inst, DIALOG_SLOT_CONNECTION_FATAL);
queue_toplevel_callback(post_fatal_message_box_toplevel, inst);
}
-void fatal_message_box(struct gui_data *inst, const char *msg)
+void fatal_message_box(Frontend *inst, const char *msg)
{
char *title = dupcat(appname, " Fatal Error", NULL);
GtkWidget *dialog = create_message_box(
@@ -235,16 +235,14 @@ void fatal_message_box(struct gui_data *inst, const char *msg)
sfree(title);
}
-static void connection_fatal_callback(void *vinst)
+static void connection_fatal_callback(void *vctx)
{
- struct gui_data *inst = (struct gui_data *)vinst;
+ Frontend *inst = (Frontend *)vctx;
destroy_inst_connection(inst);
}
-void connection_fatal(void *frontend, const char *p, ...)
+void connection_fatal(Frontend *inst, const char *p, ...)
{
- struct gui_data *inst = (struct gui_data *)frontend;
-
va_list ap;
char *msg;
va_start(ap, p);
@@ -254,7 +252,7 @@ void connection_fatal(void *frontend, const char *p, ...)
sfree(msg);
inst->exited = TRUE; /* suppress normal exit handling */
- queue_toplevel_callback(connection_fatal_callback, frontend);
+ queue_toplevel_callback(connection_fatal_callback, inst);
}
/*
@@ -293,36 +291,33 @@ int platform_default_i(const char *name, int def)
}
/* Dummy routine, only required in plink. */
-void frontend_echoedit_update(void *frontend, int echo, int edit)
+void frontend_echoedit_update(Frontend *inst, int echo, int edit)
{
}
-char *get_ttymode(void *frontend, const char *mode)
+char *get_ttymode(Frontend *inst, const char *mode)
{
- struct gui_data *inst = (struct gui_data *)frontend;
return term_get_ttymode(inst->term, mode);
}
-int from_backend(void *frontend, int is_stderr, const void *data, int len)
+int from_backend(Frontend *inst, int is_stderr, const void *data, int len)
{
- struct gui_data *inst = (struct gui_data *)frontend;
return term_data(inst->term, is_stderr, data, len);
}
-int from_backend_untrusted(void *frontend, const void *data, int len)
+int from_backend_untrusted(Frontend *inst, const void *data, int len)
{
- struct gui_data *inst = (struct gui_data *)frontend;
return term_data_untrusted(inst->term, data, len);
}
-int from_backend_eof(void *frontend)
+int from_backend_eof(Frontend *inst)
{
return TRUE; /* do respond to incoming EOF with outgoing */
}
int get_userpass_input(prompts_t *p, bufchain *input)
{
- struct gui_data *inst = (struct gui_data *)p->frontend;
+ Frontend *inst = p->frontend;
int ret;
ret = cmdline_get_passwd_input(p);
if (ret == -1)
@@ -330,19 +325,15 @@ int get_userpass_input(prompts_t *p, bufchain *input)
return ret;
}
-void logevent(void *frontend, const char *string)
+void logevent(Frontend *inst, const char *string)
{
- struct gui_data *inst = (struct gui_data *)frontend;
-
log_eventlog(inst->logctx, string);
logevent_dlg(inst->eventlogstuff, string);
}
-int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */
+int font_dimension(Frontend *inst, int which) /* 0 for width, 1 for height */
{
- struct gui_data *inst = (struct gui_data *)frontend;
-
if (which)
return inst->font_height;
else
@@ -360,8 +351,6 @@ int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */
*/
static Mouse_Button translate_button(Mouse_Button button)
{
- /* struct gui_data *inst = (struct gui_data *)frontend; */
-
if (button == MBT_LEFT)
return MBT_SELECT;
if (button == MBT_MIDDLE)
@@ -375,9 +364,8 @@ static Mouse_Button translate_button(Mouse_Button button)
* Return the top-level GtkWindow associated with a particular
* front end instance.
*/
-GtkWidget *get_window(void *frontend)
+GtkWidget *get_window(Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)frontend;
return inst->window;
}
@@ -386,16 +374,14 @@ GtkWidget *get_window(void *frontend)
* network code wanting to ask an asynchronous user question (e.g.
* 'what about this dodgy host key, then?').
*/
-void register_dialog(void *frontend, enum DialogSlot slot, GtkWidget *dialog)
+void register_dialog(Frontend *inst, enum DialogSlot slot, GtkWidget *dialog)
{
- struct gui_data *inst = (struct gui_data *)frontend;
assert(slot < DIALOG_SLOT_LIMIT);
assert(!inst->dialogs[slot]);
inst->dialogs[slot] = dialog;
}
-void unregister_dialog(void *frontend, enum DialogSlot slot)
+void unregister_dialog(Frontend *inst, enum DialogSlot slot)
{
- struct gui_data *inst = (struct gui_data *)frontend;
assert(slot < DIALOG_SLOT_LIMIT);
assert(inst->dialogs[slot]);
inst->dialogs[slot] = NULL;
@@ -405,13 +391,12 @@ void unregister_dialog(void *frontend, enum DialogSlot slot)
* Minimise or restore the window in response to a server-side
* request.
*/
-void set_iconic(void *frontend, int iconic)
+void set_iconic(Frontend *inst, int iconic)
{
/*
* GTK 1.2 doesn't know how to do this.
*/
#if GTK_CHECK_VERSION(2,0,0)
- struct gui_data *inst = (struct gui_data *)frontend;
if (iconic)
gtk_window_iconify(GTK_WINDOW(inst->window));
else
@@ -422,9 +407,8 @@ void set_iconic(void *frontend, int iconic)
/*
* Move the window in response to a server-side request.
*/
-void move_window(void *frontend, int x, int y)
+void move_window(Frontend *inst, int x, int y)
{
- struct gui_data *inst = (struct gui_data *)frontend;
/*
* I assume that when the GTK version of this call is available
* we should use it. Not sure how it differs from the GDK one,
@@ -443,9 +427,8 @@ void move_window(void *frontend, int x, int y)
* Move the window to the top or bottom of the z-order in response
* to a server-side request.
*/
-void set_zorder(void *frontend, int top)
+void set_zorder(Frontend *inst, int top)
{
- struct gui_data *inst = (struct gui_data *)frontend;
if (top)
gdk_window_raise(gtk_widget_get_window(inst->window));
else
@@ -455,9 +438,8 @@ void set_zorder(void *frontend, int top)
/*
* Refresh the window in response to a server-side request.
*/
-void refresh_window(void *frontend)
+void refresh_window(Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)frontend;
term_invalidate(inst->term);
}
@@ -465,13 +447,12 @@ void refresh_window(void *frontend)
* Maximise or restore the window in response to a server-side
* request.
*/
-void set_zoomed(void *frontend, int zoomed)
+void set_zoomed(Frontend *inst, int zoomed)
{
/*
* GTK 1.2 doesn't know how to do this.
*/
#if GTK_CHECK_VERSION(2,0,0)
- struct gui_data *inst = (struct gui_data *)frontend;
if (zoomed)
gtk_window_maximize(GTK_WINDOW(inst->window));
else
@@ -482,18 +463,16 @@ void set_zoomed(void *frontend, int zoomed)
/*
* Report whether the window is iconic, for terminal reports.
*/
-int is_iconic(void *frontend)
+int is_iconic(Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)frontend;
return !gdk_window_is_viewable(gtk_widget_get_window(inst->window));
}
/*
* Report the window's position, for terminal reports.
*/
-void get_window_pos(void *frontend, int *x, int *y)
+void get_window_pos(Frontend *inst, int *x, int *y)
{
- struct gui_data *inst = (struct gui_data *)frontend;
/*
* I assume that when the GTK version of this call is available
* we should use it. Not sure how it differs from the GDK one,
@@ -509,9 +488,8 @@ void get_window_pos(void *frontend, int *x, int *y)
/*
* Report the window's pixel size, for terminal reports.
*/
-void get_window_pixels(void *frontend, int *x, int *y)
+void get_window_pixels(Frontend *inst, int *x, int *y)
{
- struct gui_data *inst = (struct gui_data *)frontend;
/*
* I assume that when the GTK version of this call is available
* we should use it. Not sure how it differs from the GDK one,
@@ -530,7 +508,7 @@ void get_window_pixels(void *frontend, int *x, int *y)
* raise it, so that the user realises they've already been asked this
* question.
*/
-static int find_and_raise_dialog(struct gui_data *inst, enum DialogSlot slot)
+static int find_and_raise_dialog(Frontend *inst, enum DialogSlot slot)
{
GtkWidget *dialog = inst->dialogs[slot];
if (!dialog)
@@ -546,15 +524,14 @@ static int find_and_raise_dialog(struct gui_data *inst, enum DialogSlot slot)
/*
* Return the window or icon title.
*/
-char *get_window_title(void *frontend, int icon)
+char *get_window_title(Frontend *inst, int icon)
{
- struct gui_data *inst = (struct gui_data *)frontend;
return icon ? inst->icontitle : inst->wintitle;
}
static void warn_on_close_callback(void *vctx, int result)
{
- struct gui_data *inst = (struct gui_data *)vctx;
+ Frontend *inst = (Frontend *)vctx;
unregister_dialog(inst, DIALOG_SLOT_WARN_ON_CLOSE);
if (result)
gtk_widget_destroy(inst->window);
@@ -570,9 +547,8 @@ static void warn_on_close_callback(void *vctx, int result)
* handler need not do anything', i.e. 'suppress default handler',
* i.e. 'do not close the window'.)
*/
-gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
+gint delete_window(GtkWidget *widget, GdkEvent *event, Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)data;
if (!inst->exited && conf_get_int(inst->conf, CONF_warn_on_close)) {
/*
* We're not going to exit right now. We must put up a
@@ -594,7 +570,7 @@ gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
return FALSE;
}
-static void update_mouseptr(struct gui_data *inst)
+static void update_mouseptr(Frontend *inst)
{
switch (inst->busy_status) {
case BUSY_NOT:
@@ -620,7 +596,7 @@ static void update_mouseptr(struct gui_data *inst)
}
}
-static void show_mouseptr(struct gui_data *inst, int show)
+static void show_mouseptr(Frontend *inst, int show)
{
if (!conf_get_int(inst->conf, CONF_hide_mouseptr))
show = 1;
@@ -628,9 +604,9 @@ static void show_mouseptr(struct gui_data *inst, int show)
update_mouseptr(inst);
}
-static void draw_backing_rect(struct gui_data *inst);
+static void draw_backing_rect(Frontend *inst);
-static void drawing_area_setup(struct gui_data *inst, int width, int height)
+static void drawing_area_setup(Frontend *inst, int width, int height)
{
int w, h, new_scale, need_size = 0;
@@ -719,7 +695,7 @@ static void drawing_area_setup(struct gui_data *inst, int width, int height)
#endif
}
-static void drawing_area_setup_simple(struct gui_data *inst)
+static void drawing_area_setup_simple(Frontend *inst)
{
/*
* Wrapper on drawing_area_setup which fetches the width and
@@ -736,10 +712,8 @@ static void drawing_area_setup_simple(struct gui_data *inst)
drawing_area_setup(inst, alloc.width, alloc.height);
}
-static void area_realised(GtkWidget *widget, gpointer data)
+static void area_realised(GtkWidget *widget, Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)data;
-
inst->drawing_area_realised = TRUE;
if (inst->drawing_area_realised && inst->drawing_area_got_size &&
inst->drawing_area_setup_needed)
@@ -747,17 +721,15 @@ static void area_realised(GtkWidget *widget, gpointer data)
}
static void area_size_allocate(
- GtkWidget *widget, GdkRectangle *alloc, gpointer data)
+ GtkWidget *widget, GdkRectangle *alloc, Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)data;
-
inst->drawing_area_got_size = TRUE;
if (inst->drawing_area_realised && inst->drawing_area_got_size)
drawing_area_setup(inst, alloc->width, alloc->height);
}
#if GTK_CHECK_VERSION(3,10,0)
-static void area_check_scale(struct gui_data *inst)
+static void area_check_scale(Frontend *inst)
{
if (!inst->drawing_area_setup_needed &&
inst->scale != gtk_widget_get_scale_factor(inst->area)) {
@@ -774,7 +746,7 @@ static void area_check_scale(struct gui_data *inst)
static gboolean area_configured(
GtkWidget *widget, GdkEventConfigure *event, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
area_check_scale(inst);
return FALSE;
}
@@ -799,7 +771,7 @@ static void cairo_setup_dctx(struct draw_ctx *dctx)
#if GTK_CHECK_VERSION(3,0,0)
static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
#if GTK_CHECK_VERSION(3,10,0)
/*
@@ -862,7 +834,7 @@ static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
#else
gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
#ifndef NO_BACKING_PIXMAPS
/*
@@ -912,11 +884,11 @@ char *dup_keyval_name(guint keyval)
}
#endif
-static void change_font_size(struct gui_data *inst, int increment);
+static void change_font_size(Frontend *inst, int increment);
gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
char output[256];
wchar_t ucsoutput[2];
int ucsval, start, end, special, output_charset, use_ucsoutput;
@@ -2146,7 +2118,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
#if GTK_CHECK_VERSION(2,0,0)
void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
#ifdef KEY_EVENT_DIAGNOSTICS
char *string_string = dupstr("");
@@ -2173,7 +2145,7 @@ void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data)
#define SCROLL_INCREMENT_LINES 5
#if GTK_CHECK_VERSION(3,4,0)
-gboolean scroll_internal(struct gui_data *inst, gdouble delta, guint state,
+gboolean scroll_internal(Frontend *inst, gdouble delta, guint state,
gdouble ex, gdouble ey)
{
int shift, ctrl, alt, x, y, raw_mouse_mode;
@@ -2225,7 +2197,7 @@ gboolean scroll_internal(struct gui_data *inst, gdouble delta, guint state,
}
#endif
-static gboolean button_internal(struct gui_data *inst, GdkEventButton *event)
+static gboolean button_internal(Frontend *inst, GdkEventButton *event)
{
int shift, ctrl, alt, x, y, button, act, raw_mouse_mode;
@@ -2298,7 +2270,7 @@ static gboolean button_internal(struct gui_data *inst, GdkEventButton *event)
gboolean button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
return button_internal(inst, event);
}
@@ -2310,7 +2282,7 @@ gboolean button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
*/
gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
#if GTK_CHECK_VERSION(3,4,0)
gdouble dx, dy;
@@ -2351,7 +2323,7 @@ gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data)
gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
int shift, ctrl, alt, x, y, button;
/* Remember the timestamp. */
@@ -2380,10 +2352,8 @@ gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
return TRUE;
}
-void frontend_keypress(void *handle)
+void frontend_keypress(Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)handle;
-
/*
* If our child process has exited but not closed, terminate on
* any keypress.
@@ -2392,9 +2362,9 @@ void frontend_keypress(void *handle)
gtk_widget_destroy(inst->window);
}
-static void exit_callback(void *vinst)
+static void exit_callback(void *vctx)
{
- struct gui_data *inst = (struct gui_data *)vinst;
+ Frontend *inst = (Frontend *)vctx;
int exitcode, close_on_exit;
if (!inst->exited &&
@@ -2409,14 +2379,12 @@ static void exit_callback(void *vinst)
}
}
-void notify_remote_exit(void *frontend)
+void notify_remote_exit(Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)frontend;
-
queue_toplevel_callback(exit_callback, inst);
}
-static void destroy_inst_connection(struct gui_data *inst)
+static void destroy_inst_connection(Frontend *inst)
{
inst->exited = TRUE;
if (inst->ldisc) {
@@ -2435,7 +2403,7 @@ static void destroy_inst_connection(struct gui_data *inst)
}
}
-static void delete_inst(struct gui_data *inst)
+static void delete_inst(Frontend *inst)
{
int dialog_slot;
for (dialog_slot = 0; dialog_slot < DIALOG_SLOT_LIMIT; dialog_slot++) {
@@ -2496,7 +2464,7 @@ static void delete_inst(struct gui_data *inst)
void destroy(GtkWidget *widget, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
inst->window = NULL;
delete_inst(inst);
session_window_closed();
@@ -2504,16 +2472,15 @@ void destroy(GtkWidget *widget, gpointer data)
gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
term_set_focus(inst->term, event->in);
term_update(inst->term);
show_mouseptr(inst, 1);
return FALSE;
}
-void set_busy_status(void *frontend, int status)
+void set_busy_status(Frontend *inst, int status)
{
- struct gui_data *inst = (struct gui_data *)frontend;
inst->busy_status = status;
update_mouseptr(inst);
}
@@ -2521,24 +2488,21 @@ void set_busy_status(void *frontend, int status)
/*
* set or clear the "raw mouse message" mode
*/
-void set_raw_mouse_mode(void *frontend, int activate)
+void set_raw_mouse_mode(Frontend *inst, int activate)
{
- struct gui_data *inst = (struct gui_data *)frontend;
activate = activate && !conf_get_int(inst->conf, CONF_no_mouse_rep);
send_raw_mouse = activate;
update_mouseptr(inst);
}
#if GTK_CHECK_VERSION(2,0,0)
-static void compute_whole_window_size(struct gui_data *inst,
+static void compute_whole_window_size(Frontend *inst,
int wchars, int hchars,
int *wpix, int *hpix);
#endif
-void request_resize(void *frontend, int w, int h)
+void request_resize(Frontend *inst, int w, int h)
{
- struct gui_data *inst = (struct gui_data *)frontend;
-
#if !GTK_CHECK_VERSION(3,0,0)
int large_x, large_y;
@@ -2624,7 +2588,7 @@ void request_resize(void *frontend, int w, int h)
}
-static void real_palette_set(struct gui_data *inst, int n, int r, int g, int b)
+static void real_palette_set(Frontend *inst, int n, int r, int g, int b)
{
inst->cols[n].red = r * 0x0101;
inst->cols[n].green = g * 0x0101;
@@ -2678,7 +2642,7 @@ void set_gtk_widget_background(GtkWidget *widget, const GdkColor *col)
#endif
}
-void set_window_background(struct gui_data *inst)
+void set_window_background(Frontend *inst)
{
if (inst->area)
set_gtk_widget_background(GTK_WIDGET(inst->area), &inst->cols[258]);
@@ -2686,9 +2650,8 @@ void set_window_background(struct gui_data *inst)
set_gtk_widget_background(GTK_WIDGET(inst->window), &inst->cols[258]);
}
-void palette_set(void *frontend, int n, int r, int g, int b)
+void palette_set(Frontend *inst, int n, int r, int g, int b)
{
- struct gui_data *inst = (struct gui_data *)frontend;
if (n >= 16)
n += 256 - 16;
if (n >= NALLCOLOURS)
@@ -2703,9 +2666,8 @@ void palette_set(void *frontend, int n, int r, int g, int b)
}
}
-int palette_get(void *frontend, int n, int *r, int *g, int *b)
+int palette_get(Frontend *inst, int n, int *r, int *g, int *b)
{
- struct gui_data *inst = (struct gui_data *)frontend;
if (n < 0 || n >= NALLCOLOURS)
return FALSE;
*r = inst->cols[n].red >> 8;
@@ -2714,9 +2676,8 @@ int palette_get(void *frontend, int n, int *r, int *g, int *b)
return TRUE;
}
-void palette_reset(void *frontend)
+void palette_reset(Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)frontend;
/* This maps colour indices in inst->conf to those used in inst->cols. */
static const int ww[] = {
256, 257, 258, 259, 260, 261,
@@ -2784,7 +2745,7 @@ void palette_reset(void *frontend)
}
static struct clipboard_state *clipboard_from_atom(
- struct gui_data *inst, GdkAtom atom)
+ Frontend *inst, GdkAtom atom)
{
int i;
@@ -2806,7 +2767,7 @@ static struct clipboard_state *clipboard_from_atom(
* formats it feels like.
*/
-void set_clipboard_atom(struct gui_data *inst, int clipboard, GdkAtom atom)
+void set_clipboard_atom(Frontend *inst, int clipboard, GdkAtom atom)
{
struct clipboard_state *state = &inst->clipstates[clipboard];
@@ -2823,7 +2784,7 @@ void set_clipboard_atom(struct gui_data *inst, int clipboard, GdkAtom atom)
}
}
-int init_clipboard(struct gui_data *inst)
+int init_clipboard(Frontend *inst)
{
set_clipboard_atom(inst, CLIP_PRIMARY, GDK_SELECTION_PRIMARY);
set_clipboard_atom(inst, CLIP_CLIPBOARD, clipboard_atom);
@@ -2861,11 +2822,10 @@ static void clipboard_clear(GtkClipboard *clipboard, gpointer data)
sfree(cdi);
}
-void write_clip(void *frontend, int clipboard,
+void write_clip(Frontend *inst, int clipboard,
wchar_t *data, int *attr, truecolour *truecolour, int len,
int must_deselect)
{
- struct gui_data *inst = (struct gui_data *)frontend;
struct clipboard_state *state = &inst->clipstates[clipboard];
struct clipboard_data_instance *cdi;
@@ -2922,7 +2882,7 @@ void write_clip(void *frontend, int clipboard,
static void clipboard_text_received(GtkClipboard *clipboard,
const gchar *text, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
wchar_t *paste;
int paste_len;
int length;
@@ -2940,9 +2900,8 @@ static void clipboard_text_received(GtkClipboard *clipboard,
sfree(paste);
}
-void frontend_request_paste(void *frontend, int clipboard)
+void frontend_request_paste(Frontend *inst, int clipboard)
{
- struct gui_data *inst = (struct gui_data *)frontend;
struct clipboard_state *state = &inst->clipstates[clipboard];
if (!state->gtkclipboard)
@@ -2975,7 +2934,7 @@ void frontend_request_paste(void *frontend, int clipboard)
*/
/* Store the data in a cut-buffer. */
-static void store_cutbuffer(struct gui_data *inst, char *ptr, int len)
+static void store_cutbuffer(Frontend *inst, char *ptr, int len)
{
#ifndef NOT_X_WINDOWS
if (inst->disp) {
@@ -2989,7 +2948,7 @@ static void store_cutbuffer(struct gui_data *inst, char *ptr, int len)
/* Retrieve data from a cut-buffer.
* Returned data needs to be freed with XFree().
*/
-static char *retrieve_cutbuffer(struct gui_data *inst, int *nbytes)
+static char *retrieve_cutbuffer(Frontend *inst, int *nbytes)
{
#ifndef NOT_X_WINDOWS
char *ptr;
@@ -3009,11 +2968,10 @@ static char *retrieve_cutbuffer(struct gui_data *inst, int *nbytes)
#endif
}
-void write_clip(void *frontend, int clipboard,
+void write_clip(Frontend *inst, int clipboard,
wchar_t *data, int *attr, truecolour *truecolour, int len,
int must_deselect)
{
- struct gui_data *inst = (struct gui_data *)frontend;
struct clipboard_state *state = &inst->clipstates[clipboard];
if (state->pasteout_data)
@@ -3115,7 +3073,7 @@ void write_clip(void *frontend, int clipboard,
static void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
guint info, guint time_stamp, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
GdkAtom target = gtk_selection_data_get_target(seldata);
struct clipboard_state *state = clipboard_from_atom(
inst, gtk_selection_data_get_selection(seldata));
@@ -3140,7 +3098,7 @@ static void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
static gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
struct clipboard_state *state = clipboard_from_atom(
inst, seldata->selection);
@@ -3163,9 +3121,8 @@ static gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
return TRUE;
}
-void frontend_request_paste(void *frontend, int clipboard)
+void frontend_request_paste(Frontend *inst, int clipboard)
{
- struct gui_data *inst = (struct gui_data *)frontend;
struct clipboard_state *state = &inst->clipstates[clipboard];
/*
@@ -3198,7 +3155,7 @@ void frontend_request_paste(void *frontend, int clipboard)
static void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
guint time, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
char *text;
int length;
#ifndef NOT_X_WINDOWS
@@ -3320,7 +3277,7 @@ static void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
#endif
}
-static void init_one_clipboard(struct gui_data *inst, int clipboard)
+static void init_one_clipboard(Frontend *inst, int clipboard)
{
struct clipboard_state *state = &inst->clipstates[clipboard];
@@ -3328,7 +3285,7 @@ static void init_one_clipboard(struct gui_data *inst, int clipboard)
state->clipboard = clipboard;
}
-void set_clipboard_atom(struct gui_data *inst, int clipboard, GdkAtom atom)
+void set_clipboard_atom(Frontend *inst, int clipboard, GdkAtom atom)
{
struct clipboard_state *state = &inst->clipstates[clipboard];
@@ -3338,7 +3295,7 @@ void set_clipboard_atom(struct gui_data *inst, int clipboard, GdkAtom atom)
state->atom = atom;
}
-void init_clipboard(struct gui_data *inst)
+void init_clipboard(Frontend *inst)
{
#ifndef NOT_X_WINDOWS
/*
@@ -3394,7 +3351,7 @@ void init_clipboard(struct gui_data *inst)
#endif /* JUST_USE_GTK_CLIPBOARD_UTF8 */
-static void set_window_titles(struct gui_data *inst)
+static void set_window_titles(Frontend *inst)
{
/*
* We must always call set_icon_name after calling set_title,
@@ -3407,25 +3364,22 @@ static void set_window_titles(struct gui_data *inst)
inst->icontitle);
}
-void set_title(void *frontend, char *title)
+void set_title(Frontend *inst, char *title)
{
- struct gui_data *inst = (struct gui_data *)frontend;
sfree(inst->wintitle);
inst->wintitle = dupstr(title);
set_window_titles(inst);
}
-void set_icon(void *frontend, char *title)
+void set_icon(Frontend *inst, char *title)
{
- struct gui_data *inst = (struct gui_data *)frontend;
sfree(inst->icontitle);
inst->icontitle = dupstr(title);
set_window_titles(inst);
}
-void set_title_and_icon(void *frontend, char *title, char *icon)
+void set_title_and_icon(Frontend *inst, char *title, char *icon)
{
- struct gui_data *inst = (struct gui_data *)frontend;
sfree(inst->wintitle);
inst->wintitle = dupstr(title);
sfree(inst->icontitle);
@@ -3433,9 +3387,8 @@ void set_title_and_icon(void *frontend, char *title, char *icon)
set_window_titles(inst);
}
-void set_sbar(void *frontend, int total, int start, int page)
+void set_sbar(Frontend *inst, int total, int start, int page)
{
- struct gui_data *inst = (struct gui_data *)frontend;
if (!conf_get_int(inst->conf, CONF_scrollbar))
return;
inst->ignore_sbar = TRUE;
@@ -3451,17 +3404,15 @@ void set_sbar(void *frontend, int total, int start, int page)
inst->ignore_sbar = FALSE;
}
-void scrollbar_moved(GtkAdjustment *adj, gpointer data)
+void scrollbar_moved(GtkAdjustment *adj, Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)data;
-
if (!conf_get_int(inst->conf, CONF_scrollbar))
return;
if (!inst->ignore_sbar)
term_scroll(inst->term, 1, (int)gtk_adjustment_get_value(adj));
}
-static void show_scrollbar(struct gui_data *inst, gboolean visible)
+static void show_scrollbar(Frontend *inst, gboolean visible)
{
inst->sbar_visible = visible;
if (visible)
@@ -3470,7 +3421,7 @@ static void show_scrollbar(struct gui_data *inst, gboolean visible)
gtk_widget_hide(inst->sbar);
}
-void sys_cursor(void *frontend, int x, int y)
+void sys_cursor(Frontend *frontend, int x, int y)
{
/*
* This is meaningless under X.
@@ -3483,7 +3434,7 @@ void sys_cursor(void *frontend, int x, int y)
* may want to perform additional actions on any kind of bell (for
* example, taskbar flashing in Windows).
*/
-void do_beep(void *frontend, int mode)
+void do_beep(Frontend *frontend, int mode)
{
if (mode == BELL_DEFAULT)
gdk_display_beep(gdk_display_get_default());
@@ -3498,9 +3449,8 @@ int char_width(Context ctx, int uc)
return 1;
}
-Context get_ctx(void *frontend)
+Context get_ctx(Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)frontend;
struct draw_ctx *dctx;
if (!gtk_widget_get_window(inst->area))
@@ -3534,7 +3484,7 @@ Context get_ctx(void *frontend)
void free_ctx(Context ctx)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
- /* struct gui_data *inst = dctx->inst; */
+ /* Frontend *inst = dctx->inst; */
#ifdef DRAW_TEXT_GDK
if (dctx->uctx.type == DRAWTYPE_GDK) {
gdk_gc_unref(dctx->uctx.u.gdk.gc);
@@ -3824,7 +3774,7 @@ static void draw_stretch_after(struct draw_ctx *dctx, int x, int y,
#endif
}
-static void draw_backing_rect(struct gui_data *inst)
+static void draw_backing_rect(Frontend *inst)
{
int w, h;
struct draw_ctx *dctx = get_ctx(inst);
@@ -3850,7 +3800,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr, truecolour truecolour)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
- struct gui_data *inst = dctx->inst;
+ Frontend *inst = dctx->inst;
int ncombining;
int nfg, nbg, t, fontid, shadow, rlen, widefactor, bold;
int monochrome =
@@ -4007,7 +3957,7 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr, truecolour truecolour)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
- struct gui_data *inst = dctx->inst;
+ Frontend *inst = dctx->inst;
int widefactor;
do_text_internal(ctx, x, y, text, len, attr, lattr, truecolour);
@@ -4037,7 +3987,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr, truecolour truecolour)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
- struct gui_data *inst = dctx->inst;
+ Frontend *inst = dctx->inst;
int active, passive, widefactor;
@@ -4149,7 +4099,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
#endif
}
-GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val)
+GdkCursor *make_mouse_ptr(Frontend *inst, int cursor_val)
{
if (cursor_val == -1) {
#if GTK_CHECK_VERSION(2,16,0)
@@ -4186,15 +4136,14 @@ void modalfatalbox(const char *p, ...)
exit(1);
}
-const char *get_x_display(void *frontend)
+const char *get_x_display(Frontend *frontend)
{
return gdk_get_display();
}
#ifndef NOT_X_WINDOWS
-int get_windowid(void *frontend, long *id)
+int get_windowid(Frontend *inst, long *id)
{
- struct gui_data *inst = (struct gui_data *)frontend;
GdkWindow *window = gtk_widget_get_window(inst->area);
if (!GDK_IS_X11_WINDOW(window))
return FALSE;
@@ -4203,13 +4152,12 @@ int get_windowid(void *frontend, long *id)
}
#endif
-int frontend_is_utf8(void *frontend)
+int frontend_is_utf8(Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)frontend;
return inst->ucsdata.line_codepage == CS_UTF8;
}
-char *setup_fonts_ucs(struct gui_data *inst)
+char *setup_fonts_ucs(Frontend *inst)
{
int shadowbold = conf_get_int(inst->conf, CONF_shadowbold);
int shadowboldoffset = conf_get_int(inst->conf, CONF_shadowboldoffset);
@@ -4312,7 +4260,7 @@ static void find_app_menu_bar(GtkWidget *widget, gpointer data)
}
#endif
-static void compute_geom_hints(struct gui_data *inst, GdkGeometry *geom)
+static void compute_geom_hints(Frontend *inst, GdkGeometry *geom)
{
/*
* Unused fields in geom.
@@ -4413,7 +4361,7 @@ static void compute_geom_hints(struct gui_data *inst, GdkGeometry *geom)
#endif
}
-void set_geom_hints(struct gui_data *inst)
+void set_geom_hints(Frontend *inst)
{
GdkGeometry geom;
gint flags = GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC;
@@ -4427,7 +4375,7 @@ void set_geom_hints(struct gui_data *inst)
}
#if GTK_CHECK_VERSION(2,0,0)
-static void compute_whole_window_size(struct gui_data *inst,
+static void compute_whole_window_size(Frontend *inst,
int wchars, int hchars,
int *wpix, int *hpix)
{
@@ -4440,13 +4388,13 @@ static void compute_whole_window_size(struct gui_data *inst,
void clear_scrollback_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
term_clrsb(inst->term);
}
void reset_terminal_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
term_pwron(inst->term, TRUE);
if (inst->ldisc)
ldisc_echoedit_update(inst->ldisc);
@@ -4454,27 +4402,27 @@ void reset_terminal_menuitem(GtkMenuItem *item, gpointer data)
void copy_clipboard_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
static const int clips[] = { MENU_CLIPBOARD };
term_request_copy(inst->term, clips, lenof(clips));
}
void paste_clipboard_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
term_request_paste(inst->term, MENU_CLIPBOARD);
}
void copy_all_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
static const int clips[] = { COPYALL_CLIPBOARDS };
term_copyall(inst->term, clips, lenof(clips));
}
void special_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
int code = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item),
"user-data"));
@@ -4484,17 +4432,17 @@ void special_menuitem(GtkMenuItem *item, gpointer data)
void about_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
about_box(inst->window);
}
void event_log_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
showeventlog(inst->eventlogstuff, inst->window);
}
-void setup_clipboards(struct gui_data *inst, Terminal *term, Conf *conf)
+void setup_clipboards(Frontend *inst, Terminal *term, Conf *conf)
{
assert(term->mouse_select_clipboards[0] == CLIP_LOCAL);
@@ -4556,7 +4504,7 @@ void setup_clipboards(struct gui_data *inst, Terminal *term, Conf *conf)
}
struct after_change_settings_dialog_ctx {
- struct gui_data *inst;
+ Frontend *inst;
Conf *newconf;
};
@@ -4564,7 +4512,7 @@ static void after_change_settings_dialog(void *vctx, int retval);
void change_settings_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
struct after_change_settings_dialog_ctx *ctx;
GtkWidget *dialog;
char *title;
@@ -4597,7 +4545,7 @@ static void after_change_settings_dialog(void *vctx, int retval)
};
struct after_change_settings_dialog_ctx ctx =
*(struct after_change_settings_dialog_ctx *)vctx;
- struct gui_data *inst = ctx.inst;
+ Frontend *inst = ctx.inst;
Conf *oldconf = inst->conf, *newconf = ctx.newconf;
int i, j, need_size;
@@ -4772,7 +4720,7 @@ static void after_change_settings_dialog(void *vctx, int retval)
}
}
-static void change_font_size(struct gui_data *inst, int increment)
+static void change_font_size(Frontend *inst, int increment)
{
static const int conf_keys[lenof(inst->fonts)] = {
CONF_font, CONF_boldfont, CONF_widefont, CONF_wideboldfont,
@@ -4835,7 +4783,7 @@ static void change_font_size(struct gui_data *inst, int increment)
void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)
{
- struct gui_data *inst = (struct gui_data *)gdata;
+ Frontend *inst = (Frontend *)gdata;
launch_duplicate_session(inst->conf);
}
@@ -4847,7 +4795,7 @@ void new_session_menuitem(GtkMenuItem *item, gpointer data)
void restart_session_menuitem(GtkMenuItem *item, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
if (!inst->backend) {
logevent(inst, "----- Session restarted -----");
@@ -4871,9 +4819,9 @@ void saved_session_freedata(GtkMenuItem *item, gpointer data)
sfree(str);
}
-void app_menu_action(void *frontend, enum MenuAction action)
+void app_menu_action(Frontend *frontend, enum MenuAction action)
{
- struct gui_data *inst = (struct gui_data *)frontend;
+ Frontend *inst = (Frontend *)frontend;
switch (action) {
case MA_COPY:
copy_clipboard_menuitem(NULL, inst);
@@ -4907,7 +4855,7 @@ void app_menu_action(void *frontend, enum MenuAction action)
static void update_savedsess_menu(GtkMenuItem *menuitem, gpointer data)
{
- struct gui_data *inst = (struct gui_data *)data;
+ Frontend *inst = (Frontend *)data;
struct sesslist sesslist;
int i;
@@ -4976,10 +4924,8 @@ void set_window_icon(GtkWidget *window, const char *const *const *icon,
#endif
}
-void update_specials_menu(void *frontend)
+void update_specials_menu(Frontend *inst)
{
- struct gui_data *inst = (struct gui_data *)frontend;
-
const struct telnet_special *specials;
if (inst->backend)
@@ -5041,7 +4987,7 @@ void update_specials_menu(void *frontend)
}
}
-static void start_backend(struct gui_data *inst)
+static void start_backend(Frontend *inst)
{
const struct Backend_vtable *vt;
char *realhost;
@@ -5114,14 +5060,14 @@ static void get_monitor_geometry(GtkWidget *widget, GdkRectangle *geometry)
void new_session_window(Conf *conf, const char *geometry_string)
{
- struct gui_data *inst;
+ Frontend *inst;
prepare_session(conf);
/*
* Create an instance structure and initialise to zeroes
*/
- inst = snew(struct gui_data);
+ inst = snew(Frontend);
memset(inst, 0, sizeof(*inst));
#ifdef JUST_USE_GTK_CLIPBOARD_UTF8
inst->cdi_headtail.next = inst->cdi_headtail.prev = &inst->cdi_headtail;
diff --git a/unix/unix.h b/unix/unix.h
index f9ff3c68..3dd2864a 100644
--- a/unix/unix.h
+++ b/unix/unix.h
@@ -175,7 +175,7 @@ void launch_saved_session(const char *str);
void session_window_closed(void);
void window_setup_error(const char *errmsg);
#ifdef MAY_REFER_TO_GTK_IN_HEADERS
-GtkWidget *make_gtk_toplevel_window(void *frontend);
+GtkWidget *make_gtk_toplevel_window(Frontend *frontend);
#endif
const struct Backend_vtable *select_backend(Conf *conf);
@@ -189,16 +189,16 @@ enum MenuAction {
MA_RESTART_SESSION, MA_CHANGE_SETTINGS, MA_CLEAR_SCROLLBACK,
MA_RESET_TERMINAL, MA_EVENT_LOG
};
-void app_menu_action(void *frontend, enum MenuAction);
+void app_menu_action(Frontend *frontend, enum MenuAction);
/* Things pty.c needs from pterm.c */
-const char *get_x_display(void *frontend);
-int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */
-int get_windowid(void *frontend, long *id);
+const char *get_x_display(Frontend *frontend);
+int font_dimension(Frontend *frontend, int which);/* 0 for width, 1 for height */
+int get_windowid(Frontend *frontend, long *id);
/* Things gtkdlg.c needs from pterm.c */
#ifdef MAY_REFER_TO_GTK_IN_HEADERS
-GtkWidget *get_window(void *frontend);
+GtkWidget *get_window(Frontend *frontend);
enum DialogSlot {
DIALOG_SLOT_RECONFIGURE,
DIALOG_SLOT_NETWORK_PROMPT,
@@ -207,8 +207,8 @@ enum DialogSlot {
DIALOG_SLOT_CONNECTION_FATAL,
DIALOG_SLOT_LIMIT /* must remain last */
};
-void register_dialog(void *frontend, enum DialogSlot slot, GtkWidget *dialog);
-void unregister_dialog(void *frontend, enum DialogSlot slot);
+void register_dialog(Frontend *frontend, enum DialogSlot slot, GtkWidget *dialog);
+void unregister_dialog(Frontend *frontend, enum DialogSlot slot);
#endif
/* Things pterm.c needs from gtkdlg.c */
diff --git a/unix/uxcons.c b/unix/uxcons.c
index e7f531dc..b4fecce1 100644
--- a/unix/uxcons.c
+++ b/unix/uxcons.c
@@ -63,15 +63,15 @@ void cleanup_exit(int code)
exit(code);
}
-void set_busy_status(void *frontend, int status)
+void set_busy_status(Frontend *frontend, int status)
{
}
-void update_specials_menu(void *frontend)
+void update_specials_menu(Frontend *frontend)
{
}
-void notify_remote_exit(void *frontend)
+void notify_remote_exit(Frontend *frontend)
{
}
@@ -113,7 +113,7 @@ static int block_and_read(int fd, void *buf, size_t len)
return ret;
}
-int verify_ssh_host_key(void *frontend, char *host, int port,
+int verify_ssh_host_key(Frontend *frontend, char *host, int port,
const char *keytype, char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx)
{
@@ -223,7 +223,7 @@ int verify_ssh_host_key(void *frontend, char *host, int port,
* Ask whether the selected algorithm is acceptable (since it was
* below the configured 'warn' threshold).
*/
-int askalg(void *frontend, const char *algtype, const char *algname,
+int askalg(Frontend *frontend, const char *algtype, const char *algname,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char msg[] =
@@ -270,7 +270,7 @@ int askalg(void *frontend, const char *algtype, const char *algname,
}
}
-int askhk(void *frontend, const char *algname, const char *betteralgs,
+int askhk(Frontend *frontend, const char *algname, const char *betteralgs,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char msg[] =
@@ -327,7 +327,7 @@ int askhk(void *frontend, const char *algname, const char *betteralgs,
* Ask whether to wipe a session log file before writing to it.
* Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
*/
-int askappend(void *frontend, Filename *filename,
+int askappend(Frontend *frontend, Filename *filename,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char msgtemplate[] =
@@ -410,7 +410,7 @@ void console_provide_logctx(LogContext *logctx)
console_logctx = logctx;
}
-void logevent(void *frontend, const char *string)
+void logevent(Frontend *frontend, const char *string)
{
struct termios cf;
if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE))
@@ -546,7 +546,7 @@ int console_get_userpass_input(prompts_t *p)
return 1; /* success */
}
-void frontend_keypress(void *handle)
+void frontend_keypress(Frontend *frontend)
{
/*
* This is nothing but a stub, in console code.
diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
index 07248d39..6445fd57 100644
--- a/unix/uxpgnt.c
+++ b/unix/uxpgnt.c
@@ -42,7 +42,7 @@ void nonfatal(const char *p, ...)
va_end(ap);
fputc('\n', stderr);
}
-void connection_fatal(void *frontend, const char *p, ...)
+void connection_fatal(Frontend *frontend, const char *p, ...)
{
va_list ap;
fprintf(stderr, "FATAL ERROR: ");
@@ -93,7 +93,7 @@ FontSpec *platform_default_fontspec(const char *name) { return fontspec_new("");
Filename *platform_default_filename(const char *name) { return filename_from_str(""); }
char *x_get_default(const char *key) { return NULL; }
void log_eventlog(LogContext *logctx, const char *event) {}
-int from_backend(void *frontend, int is_stderr, const void *data, int datalen)
+int from_backend(Frontend *fe, int is_stderr, const void *data, int datalen)
{ assert(!"only here to satisfy notional call from backend_socket_log"); }
/*
diff --git a/unix/uxplink.c b/unix/uxplink.c
index 7bcac362..56bdf533 100644
--- a/unix/uxplink.c
+++ b/unix/uxplink.c
@@ -58,7 +58,7 @@ void nonfatal(const char *p, ...)
fputc('\n', stderr);
postmsg(&cf);
}
-void connection_fatal(void *frontend, const char *p, ...)
+void connection_fatal(Frontend *frontend, const char *p, ...)
{
struct termios cf;
va_list ap;
@@ -132,7 +132,7 @@ int term_ldisc(Terminal *term, int mode)
{
return FALSE;
}
-void frontend_echoedit_update(void *frontend, int echo, int edit)
+void frontend_echoedit_update(Frontend *frontend, int echo, int edit)
{
/* Update stdin read mode to reflect changes in line discipline. */
struct termios mode;
@@ -190,7 +190,7 @@ static char *get_ttychar(struct termios *t, int index)
return dupprintf("^<%d>", c);
}
-char *get_ttymode(void *frontend, const char *mode)
+char *get_ttymode(Frontend *frontend, const char *mode)
{
/*
* Propagate appropriate terminal modes from the local terminal,
@@ -400,7 +400,7 @@ int try_output(int is_stderr)
return bufchain_size(&stdout_data) + bufchain_size(&stderr_data);
}
-int from_backend(void *frontend_handle, int is_stderr,
+int from_backend(Frontend *frontend, int is_stderr,
const void *data, int len)
{
if (is_stderr) {
@@ -413,7 +413,7 @@ int from_backend(void *frontend_handle, int is_stderr,
}
}
-int from_backend_untrusted(void *frontend_handle, const void *data, int len)
+int from_backend_untrusted(Frontend *frontend, const void *data, int len)
{
/*
* No "untrusted" output should get here (the way the code is
@@ -423,7 +423,7 @@ int from_backend_untrusted(void *frontend_handle, const void *data, int len)
return 0; /* not reached */
}
-int from_backend_eof(void *frontend_handle)
+int from_backend_eof(Frontend *frontend)
{
assert(outgoingeof == EOF_NO);
outgoingeof = EOF_PENDING;
diff --git a/unix/uxpty.c b/unix/uxpty.c
index 870e5156..a5c770d4 100644
--- a/unix/uxpty.c
+++ b/unix/uxpty.c
@@ -71,7 +71,7 @@ static int pty_signal_pipe[2] = { -1, -1 }; /* obviously bogus initial val */
struct pty_tag {
Conf *conf;
int master_fd, slave_fd;
- void *frontend;
+ Frontend *frontend;
char name[FILENAME_MAX];
pid_t child_pid;
int term_width, term_height;
@@ -729,7 +729,7 @@ static void pty_uxsel_setup(Pty pty)
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *pty_init(void *frontend, Backend **backend_handle,
+static const char *pty_init(Frontend *frontend, Backend **backend_handle,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive)
{
diff --git a/unix/uxser.c b/unix/uxser.c
index e352f049..b02eb320 100644
--- a/unix/uxser.c
+++ b/unix/uxser.c
@@ -18,7 +18,7 @@
#define SERIAL_MAX_BACKLOG 4096
typedef struct serial_backend_data {
- void *frontend;
+ Frontend *frontend;
int fd;
int finished;
int inbufsize;
@@ -288,7 +288,7 @@ static const char *serial_configure(Serial serial, Conf *conf)
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *serial_init(void *frontend_handle, Backend **backend_handle,
+static const char *serial_init(Frontend *frontend, Backend **backend_handle,
Conf *conf,
const char *host, int port, char **realhost,
int nodelay, int keepalive)
@@ -301,7 +301,7 @@ static const char *serial_init(void *frontend_handle, Backend **backend_handle,
serial->backend.vt = &serial_backend;
*backend_handle = &serial->backend;
- serial->frontend = frontend_handle;
+ serial->frontend = frontend;
serial->finished = FALSE;
serial->inbufsize = 0;
bufchain_init(&serial->output_data);
diff --git a/unix/uxsftp.c b/unix/uxsftp.c
index eb1329cf..a0d5c71e 100644
--- a/unix/uxsftp.c
+++ b/unix/uxsftp.c
@@ -66,7 +66,7 @@ Filename *platform_default_filename(const char *name)
return filename_from_str("");
}
-char *get_ttymode(void *frontend, const char *mode) { return NULL; }
+char *get_ttymode(Frontend *frontend, const char *mode) { return NULL; }
int get_userpass_input(prompts_t *p, bufchain *input)
{
diff --git a/windows/wincons.c b/windows/wincons.c
index a7cbeeac..10367be8 100644
--- a/windows/wincons.c
+++ b/windows/wincons.c
@@ -30,11 +30,11 @@ void cleanup_exit(int code)
exit(code);
}
-void set_busy_status(void *frontend, int status)
+void set_busy_status(Frontend *frontend, int status)
{
}
-void notify_remote_exit(void *frontend)
+void notify_remote_exit(Frontend *frontend)
{
}
@@ -42,7 +42,7 @@ void timer_change_notify(unsigned long next)
{
}
-int verify_ssh_host_key(void *frontend, char *host, int port,
+int verify_ssh_host_key(Frontend *frontend, char *host, int port,
const char *keytype, char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx)
{
@@ -147,7 +147,7 @@ int verify_ssh_host_key(void *frontend, char *host, int port,
}
}
-void update_specials_menu(void *frontend)
+void update_specials_menu(Frontend *frontend)
{
}
@@ -155,7 +155,7 @@ void update_specials_menu(void *frontend)
* Ask whether the selected algorithm is acceptable (since it was
* below the configured 'warn' threshold).
*/
-int askalg(void *frontend, const char *algtype, const char *algname,
+int askalg(Frontend *frontend, const char *algtype, const char *algname,
void (*callback)(void *ctx, int result), void *ctx)
{
HANDLE hin;
@@ -196,7 +196,7 @@ int askalg(void *frontend, const char *algtype, const char *algname,
}
}
-int askhk(void *frontend, const char *algname, const char *betteralgs,
+int askhk(Frontend *frontend, const char *algname, const char *betteralgs,
void (*callback)(void *ctx, int result), void *ctx)
{
HANDLE hin;
@@ -247,7 +247,7 @@ int askhk(void *frontend, const char *algname, const char *betteralgs,
* Ask whether to wipe a session log file before writing to it.
* Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
*/
-int askappend(void *frontend, Filename *filename,
+int askappend(Frontend *frontend, Filename *filename,
void (*callback)(void *ctx, int result), void *ctx)
{
HANDLE hin;
@@ -340,7 +340,7 @@ void console_provide_logctx(LogContext *logctx)
console_logctx = logctx;
}
-void logevent(void *frontend, const char *string)
+void logevent(Frontend *frontend, const char *string)
{
log_eventlog(console_logctx, string);
}
@@ -467,7 +467,7 @@ int console_get_userpass_input(prompts_t *p)
return 1; /* success */
}
-void frontend_keypress(void *handle)
+void frontend_keypress(Frontend *frontend)
{
/*
* This is nothing but a stub, in console code.
diff --git a/windows/windlg.c b/windows/windlg.c
index 352ae1fc..05ddfdd2 100644
--- a/windows/windlg.c
+++ b/windows/windlg.c
@@ -761,7 +761,7 @@ int do_reconfig(HWND hwnd, int protcfginfo)
return ret;
}
-void logevent(void *frontend, const char *string)
+void logevent(Frontend *frontend, const char *string)
{
char timebuf[40];
char **location;
@@ -813,7 +813,7 @@ void showabout(HWND hwnd)
DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
}
-int verify_ssh_host_key(void *frontend, char *host, int port,
+int verify_ssh_host_key(Frontend *frontend, char *host, int port,
const char *keytype, char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx)
{
@@ -897,7 +897,7 @@ int verify_ssh_host_key(void *frontend, char *host, int port,
* Ask whether the selected algorithm is acceptable (since it was
* below the configured 'warn' threshold).
*/
-int askalg(void *frontend, const char *algtype, const char *algname,
+int askalg(Frontend *frontend, const char *algtype, const char *algname,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char mbtitle[] = "%s Security Alert";
@@ -922,7 +922,7 @@ int askalg(void *frontend, const char *algtype, const char *algname,
return 0;
}
-int askhk(void *frontend, const char *algname, const char *betteralgs,
+int askhk(Frontend *frontend, const char *algname, const char *betteralgs,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char mbtitle[] = "%s Security Alert";
@@ -953,7 +953,7 @@ int askhk(void *frontend, const char *algname, const char *betteralgs,
* Ask whether to wipe a session log file before writing to it.
* Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
*/
-int askappend(void *frontend, Filename *filename,
+int askappend(Frontend *frontend, Filename *filename,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char msgtemplate[] =
diff --git a/windows/window.c b/windows/window.c
index 0d29b136..eb0f5e01 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -231,16 +231,16 @@ const int share_can_be_downstream = TRUE;
const int share_can_be_upstream = TRUE;
/* Dummy routine, only required in plink. */
-void frontend_echoedit_update(void *frontend, int echo, int edit)
+void frontend_echoedit_update(Frontend *frontend, int echo, int edit)
{
}
-int frontend_is_utf8(void *frontend)
+int frontend_is_utf8(Frontend *frontend)
{
return ucsdata.line_codepage == CP_UTF8;
}
-char *get_ttymode(void *frontend, const char *mode)
+char *get_ttymode(Frontend *frontend, const char *mode)
{
return term_get_ttymode(term, mode);
}
@@ -946,7 +946,7 @@ static void update_savedsess_menu(void)
/*
* Update the Special Commands submenu.
*/
-void update_specials_menu(void *frontend)
+void update_specials_menu(Frontend *frontend)
{
HMENU new_menu;
int i, j;
@@ -1052,7 +1052,7 @@ static void update_mouse_pointer(void)
}
}
-void set_busy_status(void *frontend, int status)
+void set_busy_status(Frontend *frontend, int status)
{
busy_status = status;
update_mouse_pointer();
@@ -1061,7 +1061,7 @@ void set_busy_status(void *frontend, int status)
/*
* set or clear the "raw mouse message" mode
*/
-void set_raw_mouse_mode(void *frontend, int activate)
+void set_raw_mouse_mode(Frontend *frontend, int activate)
{
activate = activate && !conf_get_int(conf, CONF_no_mouse_rep);
send_raw_mouse = activate;
@@ -1071,7 +1071,7 @@ void set_raw_mouse_mode(void *frontend, int activate)
/*
* Print a message box and close the connection.
*/
-void connection_fatal(void *frontend, const char *fmt, ...)
+void connection_fatal(Frontend *frontend, const char *fmt, ...)
{
va_list ap;
char *stuff, morestuff[100];
@@ -1622,7 +1622,7 @@ static void deinit_fonts(void)
}
}
-void request_resize(void *frontend, int w, int h)
+void request_resize(Frontend *frontend, int w, int h)
{
int width, height;
@@ -1970,7 +1970,7 @@ static int is_alt_pressed(void)
static int resizing;
-void notify_remote_exit(void *fe)
+void notify_remote_exit(Frontend *frontend)
{
int exitcode, close_on_exit;
@@ -3277,7 +3277,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
* helper software tracks the system caret, so we should arrange to
* have one.)
*/
-void sys_cursor(void *frontend, int x, int y)
+void sys_cursor(Frontend *frontend, int x, int y)
{
int cx, cy;
@@ -4813,7 +4813,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
return -1;
}
-void set_title(void *frontend, char *title)
+void set_title(Frontend *frontend, char *title)
{
sfree(window_name);
window_name = snewn(1 + strlen(title), char);
@@ -4822,7 +4822,7 @@ void set_title(void *frontend, char *title)
SetWindowText(hwnd, title);
}
-void set_icon(void *frontend, char *title)
+void set_icon(Frontend *frontend, char *title)
{
sfree(icon_name);
icon_name = snewn(1 + strlen(title), char);
@@ -4831,7 +4831,7 @@ void set_icon(void *frontend, char *title)
SetWindowText(hwnd, title);
}
-void set_sbar(void *frontend, int total, int start, int page)
+void set_sbar(Frontend *frontend, int total, int start, int page)
{
SCROLLINFO si;
@@ -4849,7 +4849,7 @@ void set_sbar(void *frontend, int total, int start, int page)
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
-Context get_ctx(void *frontend)
+Context get_ctx(Frontend *frontend)
{
HDC hdc;
if (hwnd) {
@@ -4879,7 +4879,7 @@ static void real_palette_set(int n, int r, int g, int b)
}
}
-int palette_get(void *frontend, int n, int *r, int *g, int *b)
+int palette_get(Frontend *frontend, int n, int *r, int *g, int *b)
{
if (n < 0 || n >= NALLCOLOURS)
return FALSE;
@@ -4889,7 +4889,7 @@ int palette_get(void *frontend, int n, int *r, int *g, int *b)
return TRUE;
}
-void palette_set(void *frontend, int n, int r, int g, int b)
+void palette_set(Frontend *frontend, int n, int r, int g, int b)
{
if (n >= 16)
n += 256 - 16;
@@ -4910,7 +4910,7 @@ void palette_set(void *frontend, int n, int r, int g, int b)
}
}
-void palette_reset(void *frontend)
+void palette_reset(Frontend *frontend)
{
int i;
@@ -4939,7 +4939,7 @@ void palette_reset(void *frontend)
}
}
-void write_aclip(void *frontend, int clipboard,
+void write_aclip(Frontend *frontend, int clipboard,
char *data, int len, int must_deselect)
{
HGLOBAL clipdata;
@@ -4987,7 +4987,7 @@ int cmpCOLORREF(void *va, void *vb)
/*
* Note: unlike write_aclip() this will not append a nul.
*/
-void write_clip(void *frontend, int clipboard,
+void write_clip(Frontend *frontend, int clipboard,
wchar_t *data, int *attr, truecolour *truecolour, int len,
int must_deselect)
{
@@ -5458,7 +5458,7 @@ static void process_clipdata(HGLOBAL clipdata, int unicode)
sfree(clipboard_contents);
}
-void frontend_request_paste(void *frontend, int clipboard)
+void frontend_request_paste(Frontend *frontend, int clipboard)
{
assert(clipboard == CLIP_SYSTEM);
@@ -5488,7 +5488,7 @@ void frontend_request_paste(void *frontend, int clipboard)
* Move `lines' lines from position `from' to position `to' in the
* window.
*/
-void optimised_move(void *frontend, int to, int from, int lines)
+void optimised_move(Frontend *frontend, int to, int from, int lines)
{
RECT r;
int min, max;
@@ -5618,7 +5618,7 @@ static void flash_window(int mode)
/*
* Beep.
*/
-void do_beep(void *frontend, int mode)
+void do_beep(Frontend *frontend, int mode)
{
if (mode == BELL_DEFAULT) {
/*
@@ -5680,7 +5680,7 @@ void do_beep(void *frontend, int mode)
* Minimise or restore the window in response to a server-side
* request.
*/
-void set_iconic(void *frontend, int iconic)
+void set_iconic(Frontend *frontend, int iconic)
{
if (IsIconic(hwnd)) {
if (!iconic)
@@ -5694,7 +5694,7 @@ void set_iconic(void *frontend, int iconic)
/*
* Move the window in response to a server-side request.
*/
-void move_window(void *frontend, int x, int y)
+void move_window(Frontend *frontend, int x, int y)
{
int resize_action = conf_get_int(conf, CONF_resize_action);
if (resize_action == RESIZE_DISABLED ||
@@ -5709,7 +5709,7 @@ void move_window(void *frontend, int x, int y)
* Move the window to the top or bottom of the z-order in response
* to a server-side request.
*/
-void set_zorder(void *frontend, int top)
+void set_zorder(Frontend *frontend, int top)
{
if (conf_get_int(conf, CONF_alwaysontop))
return; /* ignore */
@@ -5720,7 +5720,7 @@ void set_zorder(void *frontend, int top)
/*
* Refresh the window in response to a server-side request.
*/
-void refresh_window(void *frontend)
+void refresh_window(Frontend *frontend)
{
InvalidateRect(hwnd, NULL, TRUE);
}
@@ -5729,7 +5729,7 @@ void refresh_window(void *frontend)
* Maximise or restore the window in response to a server-side
* request.
*/
-void set_zoomed(void *frontend, int zoomed)
+void set_zoomed(Frontend *frontend, int zoomed)
{
if (IsZoomed(hwnd)) {
if (!zoomed)
@@ -5743,7 +5743,7 @@ void set_zoomed(void *frontend, int zoomed)
/*
* Report whether the window is iconic, for terminal reports.
*/
-int is_iconic(void *frontend)
+int is_iconic(Frontend *frontend)
{
return IsIconic(hwnd);
}
@@ -5751,7 +5751,7 @@ int is_iconic(void *frontend)
/*
* Report the window's position, for terminal reports.
*/
-void get_window_pos(void *frontend, int *x, int *y)
+void get_window_pos(Frontend *frontend, int *x, int *y)
{
RECT r;
GetWindowRect(hwnd, &r);
@@ -5762,7 +5762,7 @@ void get_window_pos(void *frontend, int *x, int *y)
/*
* Report the window's pixel size, for terminal reports.
*/
-void get_window_pixels(void *frontend, int *x, int *y)
+void get_window_pixels(Frontend *frontend, int *x, int *y)
{
RECT r;
GetWindowRect(hwnd, &r);
@@ -5773,7 +5773,7 @@ void get_window_pixels(void *frontend, int *x, int *y)
/*
* Return the window or icon title.
*/
-char *get_window_title(void *frontend, int icon)
+char *get_window_title(Frontend *frontend, int icon)
{
return icon ? icon_name : window_name;
}
@@ -5906,7 +5906,7 @@ static void flip_full_screen()
}
}
-void frontend_keypress(void *handle)
+void frontend_keypress(Frontend *frontend)
{
/*
* Keypress termination in non-Close-On-Exit mode is not
@@ -5917,17 +5917,17 @@ void frontend_keypress(void *handle)
return;
}
-int from_backend(void *frontend, int is_stderr, const void *data, int len)
+int from_backend(Frontend *frontend, int is_stderr, const void *data, int len)
{
return term_data(term, is_stderr, data, len);
}
-int from_backend_untrusted(void *frontend, const void *data, int len)
+int from_backend_untrusted(Frontend *frontend, const void *data, int len)
{
return term_data_untrusted(term, data, len);
}
-int from_backend_eof(void *frontend)
+int from_backend_eof(Frontend *frontend)
{
return TRUE; /* do respond to incoming EOF with outgoing */
}
diff --git a/windows/winplink.c b/windows/winplink.c
index 455b18a7..c156d740 100644
--- a/windows/winplink.c
+++ b/windows/winplink.c
@@ -45,7 +45,7 @@ void nonfatal(const char *p, ...)
va_end(ap);
fputc('\n', stderr);
}
-void connection_fatal(void *frontend, const char *p, ...)
+void connection_fatal(Frontend *frontend, const char *p, ...)
{
va_list ap;
fprintf(stderr, "FATAL ERROR: ");
@@ -83,7 +83,7 @@ int term_ldisc(Terminal *term, int mode)
{
return FALSE;
}
-void frontend_echoedit_update(void *frontend, int echo, int edit)
+void frontend_echoedit_update(Frontend *frontend, int echo, int edit)
{
/* Update stdin read mode to reflect changes in line discipline. */
DWORD mode;
@@ -100,9 +100,9 @@ void frontend_echoedit_update(void *frontend, int echo, int edit)
SetConsoleMode(inhandle, mode);
}
-char *get_ttymode(void *frontend, const char *mode) { return NULL; }
+char *get_ttymode(Frontend *frontend, const char *mode) { return NULL; }
-int from_backend(void *frontend_handle, int is_stderr,
+int from_backend(Frontend *frontend, int is_stderr,
const void *data, int len)
{
if (is_stderr) {
@@ -114,7 +114,7 @@ int from_backend(void *frontend_handle, int is_stderr,
return handle_backlog(stdout_handle) + handle_backlog(stderr_handle);
}
-int from_backend_untrusted(void *frontend_handle, const void *data, int len)
+int from_backend_untrusted(Frontend *frontend, const void *data, int len)
{
/*
* No "untrusted" output should get here (the way the code is
@@ -124,7 +124,7 @@ int from_backend_untrusted(void *frontend_handle, const void *data, int len)
return 0; /* not reached */
}
-int from_backend_eof(void *frontend_handle)
+int from_backend_eof(Frontend *frontend)
{
handle_write_eof(stdout_handle);
return FALSE; /* do not respond to incoming EOF with outgoing */
diff --git a/windows/winser.c b/windows/winser.c
index 1efac931..2a42ce21 100644
--- a/windows/winser.c
+++ b/windows/winser.c
@@ -13,7 +13,7 @@
typedef struct serial_backend_data {
HANDLE port;
struct handle *out, *in;
- void *frontend;
+ Frontend *frontend;
int bufsize;
long clearbreak_time;
int break_in_progress;
@@ -199,7 +199,7 @@ static const char *serial_configure(Serial serial, HANDLE serport, Conf *conf)
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *serial_init(void *frontend_handle, Backend **backend_handle,
+static const char *serial_init(Frontend *frontend, Backend **backend_handle,
Conf *conf, const char *host, int port,
char **realhost, int nodelay, int keepalive)
{
@@ -216,7 +216,7 @@ static const char *serial_init(void *frontend_handle, Backend **backend_handle,
serial->backend.vt = &serial_backend;
*backend_handle = &serial->backend;
- serial->frontend = frontend_handle;
+ serial->frontend = frontend;
serline = conf_get_str(conf, CONF_serline);
{
diff --git a/windows/winsftp.c b/windows/winsftp.c
index e9d5d4cb..580e8d95 100644
--- a/windows/winsftp.c
+++ b/windows/winsftp.c
@@ -13,7 +13,7 @@
#include "int64.h"
#include "winsecur.h"
-char *get_ttymode(void *frontend, const char *mode) { return NULL; }
+char *get_ttymode(Frontend *frontend, const char *mode) { return NULL; }
int get_userpass_input(prompts_t *p, bufchain *input)
{
diff --git a/windows/winstuff.h b/windows/winstuff.h
index 87dc48b6..1ecec333 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -247,7 +247,7 @@ GLOBAL LogContext *logctx;
* which takes the data string in the system code page instead of
* Unicode.
*/
-void write_aclip(void *frontend, int clipboard, char *, int, int);
+void write_aclip(Frontend *frontend, int clipboard, char *, int, int);
#define WM_NETEVENT (WM_APP + 5)
From 6a8b9d38130503917cb2d2a7f2a036f3b8e616fc Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 12 Sep 2018 15:03:47 +0100
Subject: [PATCH 394/607] Replace enum+union of local channel types with a
vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
---
Recipe | 2 +-
agentf.c | 235 +++++++++++++++
defs.h | 2 +
portfwd.c | 143 +++++----
ssh.c | 785 +++++++++++++++++++++-----------------------------
ssh.h | 31 +-
sshchan.h | 59 ++++
unix/uxpgnt.c | 6 +-
x11fwd.c | 93 +++---
9 files changed, 786 insertions(+), 570 deletions(-)
create mode 100644 agentf.c
create mode 100644 sshchan.h
diff --git a/Recipe b/Recipe
index 1b84885b..5af98117 100644
--- a/Recipe
+++ b/Recipe
@@ -254,7 +254,7 @@ SSH = ssh ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
+ sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf
- + sshgssc pgssapi sshshare sshecc aqsync marshal nullplug
+ + sshgssc pgssapi sshshare sshecc aqsync marshal nullplug agentf
WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc
+ winhsock errsock
UXSSH = SSH uxnoise uxagentc uxgss uxshare
diff --git a/agentf.c b/agentf.c
new file mode 100644
index 00000000..65a971da
--- /dev/null
+++ b/agentf.c
@@ -0,0 +1,235 @@
+/*
+ * SSH agent forwarding.
+ */
+
+#include
+#include
+#include
+
+#include "putty.h"
+#include "ssh.h"
+#include "pageant.h"
+#include "sshchan.h"
+
+typedef struct agentf {
+ struct ssh_channel *c;
+ bufchain inbuffer;
+ agent_pending_query *pending;
+ int input_wanted;
+ int rcvd_eof;
+
+ Channel chan;
+} agentf;
+
+static void agentf_got_response(agentf *af, void *reply, int replylen)
+{
+ af->pending = NULL;
+
+ if (!reply) {
+ /* The real agent didn't send any kind of reply at all for
+ * some reason, so fake an SSH_AGENT_FAILURE. */
+ reply = "\0\0\0\1\5";
+ replylen = 5;
+ }
+
+ sshfwd_write(af->c, reply, replylen);
+}
+
+static void agentf_callback(void *vctx, void *reply, int replylen);
+
+static void agentf_try_forward(agentf *af)
+{
+ unsigned datalen, length;
+ strbuf *message;
+ unsigned char msglen[4];
+ void *reply;
+ int replylen;
+
+ /*
+ * Don't try to parallelise agent requests. Wait for each one to
+ * return before attempting the next.
+ */
+ if (af->pending)
+ return;
+
+ /*
+ * If the outgoing side of the channel connection is currently
+ * throttled, don't submit any new forwarded requests to the real
+ * agent. This causes the input side of the agent forwarding not
+ * to be emptied, exerting the required back-pressure on the
+ * remote client, and encouraging it to read our responses before
+ * sending too many more requests.
+ */
+ if (!af->input_wanted)
+ return;
+
+ while (1) {
+ /*
+ * Try to extract a complete message from the input buffer.
+ */
+ datalen = bufchain_size(&af->inbuffer);
+ if (datalen < 4)
+ break; /* not even a length field available yet */
+
+ bufchain_fetch(&af->inbuffer, msglen, 4);
+ length = GET_32BIT(msglen);
+
+ if (length > AGENT_MAX_MSGLEN-4) {
+ /*
+ * If the remote has sent a message that's just _too_
+ * long, we should reject it in advance of seeing the rest
+ * of the incoming message, and also close the connection
+ * for good measure (which avoids us having to faff about
+ * with carefully ignoring just the right number of bytes
+ * from the overlong message).
+ */
+ agentf_got_response(af, NULL, 0);
+ sshfwd_write_eof(af->c);
+ return;
+ }
+
+ if (length > datalen - 4)
+ break; /* a whole message is not yet available */
+
+ bufchain_consume(&af->inbuffer, 4);
+
+ message = strbuf_new_for_agent_query();
+ bufchain_fetch_consume(
+ &af->inbuffer, strbuf_append(message, length), length);
+ af->pending = agent_query(
+ message, &reply, &replylen, agentf_callback, af);
+ strbuf_free(message);
+
+ if (af->pending)
+ return; /* agent_query promised to reply in due course */
+
+ /*
+ * If the agent gave us an answer immediately, pass it
+ * straight on and go round this loop again.
+ */
+ agentf_got_response(af, reply, replylen);
+ sfree(reply);
+ }
+
+ /*
+ * If we get here (i.e. we left the above while loop via 'break'
+ * rather than 'return'), that means we've determined that the
+ * input buffer for the agent forwarding connection doesn't
+ * contain a complete request.
+ *
+ * So if there's potentially more data to come, we can return now,
+ * and wait for the remote client to send it. But if the remote
+ * has sent EOF, it would be a mistake to do that, because we'd be
+ * waiting a long time. So this is the moment to check for EOF,
+ * and respond appropriately.
+ */
+ if (af->rcvd_eof)
+ sshfwd_write_eof(af->c);
+}
+
+static void agentf_callback(void *vctx, void *reply, int replylen)
+{
+ agentf *af = (agentf *)vctx;
+
+ agentf_got_response(af, reply, replylen);
+ sfree(reply);
+
+ /*
+ * Now try to extract and send further messages from the channel's
+ * input-side buffer.
+ */
+ agentf_try_forward(af);
+}
+
+static void agentf_free(Channel *chan);
+static int agentf_send(Channel *chan, int is_stderr, const void *, int);
+static void agentf_send_eof(Channel *chan);
+static char *agentf_log_close_msg(Channel *chan);
+static void agentf_set_input_wanted(Channel *chan, int wanted);
+
+static const struct ChannelVtable agentf_channelvt = {
+ agentf_free,
+ chan_remotely_opened_confirmation,
+ chan_remotely_opened_failure,
+ agentf_send,
+ agentf_send_eof,
+ agentf_set_input_wanted,
+ agentf_log_close_msg,
+ chan_no_eager_close,
+};
+
+Channel *agentf_new(struct ssh_channel *c)
+{
+ agentf *af = snew(agentf);
+ af->c = c;
+ af->chan.vt = &agentf_channelvt;
+ af->chan.initial_fixed_window_size = 0;
+ af->rcvd_eof = TRUE;
+ bufchain_init(&af->inbuffer);
+ af->pending = NULL;
+ af->input_wanted = TRUE;
+ return &af->chan;
+}
+
+static void agentf_free(Channel *chan)
+{
+ assert(chan->vt == &agentf_channelvt);
+ agentf *af = FROMFIELD(chan, agentf, chan);
+
+ if (af->pending)
+ agent_cancel_query(af->pending);
+ bufchain_clear(&af->inbuffer);
+ sfree(af);
+}
+
+static int agentf_send(Channel *chan, int is_stderr,
+ const void *data, int length)
+{
+ assert(chan->vt == &agentf_channelvt);
+ agentf *af = FROMFIELD(chan, agentf, chan);
+ bufchain_add(&af->inbuffer, data, length);
+ agentf_try_forward(af);
+
+ /*
+ * We exert back-pressure on an agent forwarding client if and
+ * only if we're waiting for the response to an asynchronous agent
+ * request. This prevents the client running out of window while
+ * receiving the _first_ message, but means that if any message
+ * takes time to process, the client will be discouraged from
+ * sending an endless stream of further ones after it.
+ */
+ return (af->pending ? bufchain_size(&af->inbuffer) : 0);
+}
+
+static void agentf_send_eof(Channel *chan)
+{
+ assert(chan->vt == &agentf_channelvt);
+ agentf *af = FROMFIELD(chan, agentf, chan);
+
+ af->rcvd_eof = TRUE;
+
+ /* Call try_forward, which will respond to the EOF now if
+ * appropriate, or wait until the queue of outstanding requests is
+ * dealt with if not. */
+ agentf_try_forward(af);
+}
+
+static char *agentf_log_close_msg(Channel *chan)
+{
+ return dupstr("Agent-forwarding connection closed");
+}
+
+static void agentf_set_input_wanted(Channel *chan, int wanted)
+{
+ assert(chan->vt == &agentf_channelvt);
+ agentf *af = FROMFIELD(chan, agentf, chan);
+
+ af->input_wanted = wanted;
+
+ /* Agent forwarding channels are buffer-managed by not asking the
+ * agent questions if the SSH channel isn't accepting input. So if
+ * it's started again, we should ask a question if we have one
+ * pending.. */
+ if (wanted)
+ agentf_try_forward(af);
+}
diff --git a/defs.h b/defs.h
index 53ded962..2c597953 100644
--- a/defs.h
+++ b/defs.h
@@ -53,6 +53,8 @@ typedef struct Frontend Frontend;
typedef struct ssh_tag *Ssh;
+typedef struct Channel Channel;
+
/* Note indirection: for historical reasons (it used to be closer to
* the OS socket type), the type that most code uses for a socket is
* 'Socket', not 'Socket *'. So an implementation of Socket or Plug
diff --git a/portfwd.c b/portfwd.c
index e6b06e14..d7ad74d2 100644
--- a/portfwd.c
+++ b/portfwd.c
@@ -8,6 +8,7 @@
#include "putty.h"
#include "ssh.h"
+#include "sshchan.h"
/*
* Enumeration of values that live in the 'socks_state' field of
@@ -21,12 +22,12 @@ typedef enum {
SOCKS_5_CONNECT /* expect a SOCKS 5 connection message */
} SocksState;
-struct PortForwarding {
+typedef struct PortForwarding {
struct ssh_channel *c; /* channel structure held by ssh.c */
Ssh ssh; /* instance of SSH backend itself */
/* Note that ssh need not be filled in if c is non-NULL */
Socket s;
- int throttled, throttle_override;
+ int input_wanted;
int ready;
SocksState socks_state;
/*
@@ -44,7 +45,8 @@ struct PortForwarding {
size_t socksbuf_consumed;
const Plug_vtable *plugvt;
-};
+ Channel chan;
+} PortForwarding;
struct PortListener {
Ssh ssh; /* instance of SSH backend itself */
@@ -105,6 +107,8 @@ static void pfl_log(Plug plug, int type, SockAddr addr, int port,
/* we have to dump these since we have no interface to logging.c */
}
+static void pfd_close(struct PortForwarding *pf);
+
static void pfd_closing(Plug plug, const char *error_msg, int error_code,
int calling_back)
{
@@ -142,10 +146,12 @@ static void pfl_closing(Plug plug, const char *error_msg, int error_code,
pfl_terminate(pl);
}
-static void wrap_send_port_open(void *channel, const char *hostname, int port,
- Socket s)
+static struct ssh_channel *wrap_send_port_open(
+ Ssh ssh, const char *hostname, int port, Socket s, Channel *chan)
{
char *peerinfo, *description;
+ struct ssh_channel *toret;
+
peerinfo = sk_peer_info(s);
if (peerinfo) {
description = dupprintf("forwarding from %s", peerinfo);
@@ -153,8 +159,11 @@ static void wrap_send_port_open(void *channel, const char *hostname, int port,
} else {
description = dupstr("forwarding");
}
- ssh_send_port_open(channel, hostname, port, description);
+
+ toret = ssh_send_port_open(ssh, hostname, port, description, chan);
+
sfree(description);
+ return toret;
}
static char *ipv4_to_string(unsigned ipv4)
@@ -396,21 +405,11 @@ static void pfd_receive(Plug plug, int urgent, char *data, int len)
*/
sk_set_frozen(pf->s, 1);
- pf->c = new_sock_channel(pf->ssh, pf);
- if (pf->c == NULL) {
- pfd_close(pf);
- return;
- } else {
- /* asks to forward to the specified host/port for this */
- wrap_send_port_open(pf->c, pf->hostname, pf->port, pf->s);
- }
- }
- if (pf->ready) {
- if (sshfwd_write(pf->c, data, len) > 0) {
- pf->throttled = 1;
- sk_set_frozen(pf->s, 1);
- }
+ pf->c = wrap_send_port_open(pf->ssh, pf->hostname, pf->port, pf->s,
+ &pf->chan);
}
+ if (pf->ready)
+ sshfwd_write(pf->c, data, len);
}
static void pfd_sent(Plug plug, int bufsize)
@@ -429,6 +428,25 @@ static const Plug_vtable PortForwarding_plugvt = {
NULL
};
+static void pfd_chan_free(Channel *chan);
+static void pfd_open_confirmation(Channel *chan);
+static void pfd_open_failure(Channel *chan, const char *errtext);
+static int pfd_send(Channel *chan, int is_stderr, const void *data, int len);
+static void pfd_send_eof(Channel *chan);
+static void pfd_set_input_wanted(Channel *chan, int wanted);
+static char *pfd_log_close_msg(Channel *chan);
+
+static const struct ChannelVtable PortForwarding_channelvt = {
+ pfd_chan_free,
+ pfd_open_confirmation,
+ pfd_open_failure,
+ pfd_send,
+ pfd_send_eof,
+ pfd_set_input_wanted,
+ pfd_log_close_msg,
+ chan_no_eager_close,
+};
+
/*
* Called when receiving a PORT OPEN from the server to make a
* connection to a destination host.
@@ -436,8 +454,8 @@ static const Plug_vtable PortForwarding_plugvt = {
* On success, returns NULL and fills in *pf_ret. On error, returns a
* dynamically allocated error message string.
*/
-char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
- void *c, Conf *conf, int addressfamily)
+char *pfd_connect(Channel **chan_ret, char *hostname,int port,
+ struct ssh_channel *c, Conf *conf, int addressfamily)
{
SockAddr addr;
const char *err;
@@ -459,9 +477,12 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
/*
* Open socket.
*/
- pf = *pf_ret = new_portfwd_state();
+ pf = new_portfwd_state();
+ *chan_ret = &pf->chan;
pf->plugvt = &PortForwarding_plugvt;
- pf->throttled = pf->throttle_override = 0;
+ pf->chan.initial_fixed_window_size = 0;
+ pf->chan.vt = &PortForwarding_channelvt;
+ pf->input_wanted = TRUE;
pf->ready = 1;
pf->c = c;
pf->ssh = NULL; /* we shouldn't need this */
@@ -474,7 +495,7 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
char *err_ret = dupstr(err);
sk_close(pf->s);
free_portfwd_state(pf);
- *pf_ret = NULL;
+ *chan_ret = NULL;
return err_ret;
}
@@ -495,6 +516,9 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
pl = FROMFIELD(p, struct PortListener, plugvt);
pf = new_portfwd_state();
pf->plugvt = &PortForwarding_plugvt;
+ pf->chan.initial_fixed_window_size = 0;
+ pf->chan.vt = &PortForwarding_channelvt;
+ pf->input_wanted = TRUE;
pf->c = NULL;
pf->ssh = pl->ssh;
@@ -505,7 +529,7 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
return err != NULL;
}
- pf->throttled = pf->throttle_override = 0;
+ pf->input_wanted = TRUE;
pf->ready = 0;
if (pl->is_dynamic) {
@@ -518,15 +542,8 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
pf->socks_state = SOCKS_NONE;
pf->hostname = dupstr(pl->hostname);
pf->port = pl->port;
- pf->c = new_sock_channel(pl->ssh, pf);
-
- if (pf->c == NULL) {
- free_portfwd_state(pf);
- return 1;
- } else {
- /* asks to forward to the specified host/port for this */
- wrap_send_port_open(pf->c, pf->hostname, pf->port, s);
- }
+ pf->c = wrap_send_port_open(pl->ssh, pf->hostname, pf->port,
+ s, &pf->chan);
}
return 0;
@@ -580,7 +597,12 @@ char *pfl_listen(char *desthost, int destport, char *srcaddr,
return NULL;
}
-void pfd_close(struct PortForwarding *pf)
+static char *pfd_log_close_msg(Channel *chan)
+{
+ return dupstr("Forwarded port closed");
+}
+
+static void pfd_close(struct PortForwarding *pf)
{
if (!pf)
return;
@@ -601,43 +623,42 @@ void pfl_terminate(struct PortListener *pl)
free_portlistener_state(pl);
}
-void pfd_unthrottle(struct PortForwarding *pf)
+static void pfd_set_input_wanted(Channel *chan, int wanted)
{
- if (!pf)
- return;
-
- pf->throttled = 0;
- sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);
+ assert(chan->vt == &PortForwarding_channelvt);
+ PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
+ pf->input_wanted = wanted;
+ sk_set_frozen(pf->s, !pf->input_wanted);
}
-void pfd_override_throttle(struct PortForwarding *pf, int enable)
+static void pfd_chan_free(Channel *chan)
{
- if (!pf)
- return;
-
- pf->throttle_override = enable;
- sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);
+ assert(chan->vt == &PortForwarding_channelvt);
+ PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
+ pfd_close(pf);
}
/*
* Called to send data down the raw connection.
*/
-int pfd_send(struct PortForwarding *pf, const void *data, int len)
+static int pfd_send(Channel *chan, int is_stderr, const void *data, int len)
{
- if (pf == NULL)
- return 0;
+ assert(chan->vt == &PortForwarding_channelvt);
+ PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
return sk_write(pf->s, data, len);
}
-void pfd_send_eof(struct PortForwarding *pf)
+static void pfd_send_eof(Channel *chan)
{
+ assert(chan->vt == &PortForwarding_channelvt);
+ PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
sk_write_eof(pf->s);
}
-void pfd_confirm(struct PortForwarding *pf)
+static void pfd_open_confirmation(Channel *chan)
{
- if (pf == NULL)
- return;
+ assert(chan->vt == &PortForwarding_channelvt);
+ PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
pf->ready = 1;
sk_set_frozen(pf->s, 0);
@@ -649,3 +670,15 @@ void pfd_confirm(struct PortForwarding *pf)
pf->socksbuf = NULL;
}
}
+
+static void pfd_open_failure(Channel *chan, const char *errtext)
+{
+ assert(chan->vt == &PortForwarding_channelvt);
+ PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
+
+ char *msg = dupprintf(
+ "Forwarded connection refused by server%s%s",
+ errtext ? ": " : "", errtext ? errtext : "");
+ logevent(ssh_get_frontend(pf->ssh), msg);
+ sfree(msg);
+}
diff --git a/ssh.c b/ssh.c
index 1009e038..bc2f7bf8 100644
--- a/ssh.c
+++ b/ssh.c
@@ -17,6 +17,7 @@
#include "ssh.h"
#include "sshcr.h"
#include "sshbpp.h"
+#include "sshchan.h"
#ifndef NO_GSSAPI
#include "sshgssc.h"
#include "sshgss.h"
@@ -362,29 +363,6 @@ const static struct ssh_compress *const compressions[] = {
&ssh_zlib, &ssh_comp_none
};
-enum { /* channel types */
- CHAN_MAINSESSION,
- CHAN_X11,
- CHAN_AGENT,
- CHAN_SOCKDATA,
- /*
- * CHAN_SHARING indicates a channel which is tracked here on
- * behalf of a connection-sharing downstream. We do almost nothing
- * with these channels ourselves: all messages relating to them
- * get thrown straight to sshshare.c and passed on almost
- * unmodified to downstream.
- */
- CHAN_SHARING,
- /*
- * CHAN_ZOMBIE is used to indicate a channel for which we've
- * already destroyed the local data source: for instance, if a
- * forwarded port experiences a socket error on the local side, we
- * immediately destroy its local socket and turn the SSH channel
- * into CHAN_ZOMBIE.
- */
- CHAN_ZOMBIE
-};
-
typedef void (*handler_fn_t)(Ssh ssh, PktIn *pktin);
typedef void (*chandler_fn_t)(Ssh ssh, PktIn *pktin, void *ctx);
typedef void (*cchandler_fn_t)(struct ssh_channel *, PktIn *, void *);
@@ -452,6 +430,15 @@ struct ssh_channel {
* throttled.
*/
int throttling_conn;
+
+ /*
+ * True if we currently have backed-up data on the direction of
+ * this channel pointing out of the SSH connection, and therefore
+ * would prefer the 'Channel' implementation not to read further
+ * local input if possible.
+ */
+ int throttled_by_backlog;
+
union {
struct ssh2_data_channel {
bufchain outbuffer;
@@ -472,22 +459,9 @@ struct ssh_channel {
enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state;
} v2;
} v;
- union {
- struct ssh_agent_channel {
- bufchain inbuffer;
- agent_pending_query *pending;
- } a;
- struct ssh_x11_channel {
- struct X11Connection *xconn;
- int initial;
- } x11;
- struct ssh_pfd_channel {
- struct PortForwarding *pf;
- } pfd;
- struct ssh_sharing_channel {
- void *ctx;
- } sharing;
- } u;
+
+ void *sharectx; /* sharing context, if this is a downstream channel */
+ Channel *chan; /* handle the client side of this channel, if not */
};
/*
@@ -945,6 +919,11 @@ static const char *ssh_pkt_type(Ssh ssh, int type)
return ssh2_pkt_type(ssh->pls.kctx, ssh->pls.actx, type);
}
+Frontend *ssh_get_frontend(Ssh ssh)
+{
+ return ssh->frontend;
+}
+
#define logevent(s) logevent(ssh->frontend, s)
/* logevent, only printf-formatted. */
@@ -2107,6 +2086,88 @@ static void ssh_process_user_input(void *ctx)
ssh->current_user_input_fn(ssh);
}
+void chan_remotely_opened_confirmation(Channel *chan)
+{
+ assert(0 && "this channel type should never receive OPEN_CONFIRMATION");
+}
+
+void chan_remotely_opened_failure(Channel *chan, const char *errtext)
+{
+ assert(0 && "this channel type should never receive OPEN_FAILURE");
+}
+
+int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof)
+{
+ return FALSE; /* default: never proactively ask for a close */
+}
+
+/*
+ * Trivial channel vtable for handling 'zombie channels' - those whose
+ * local source of data has already been shut down or otherwise
+ * stopped existing - so that we don't have to give them a null
+ * 'Channel *' and special-case that all over the place.
+ */
+
+static void zombiechan_free(Channel *chan);
+static int zombiechan_send(Channel *chan, int is_stderr, const void *, int);
+static void zombiechan_set_input_wanted(Channel *chan, int wanted);
+static void zombiechan_do_nothing(Channel *chan);
+static void zombiechan_open_failure(Channel *chan, const char *);
+static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof);
+static char *zombiechan_log_close_msg(Channel *chan) { return NULL; }
+
+static const struct ChannelVtable zombiechan_channelvt = {
+ zombiechan_free,
+ zombiechan_do_nothing, /* open_confirmation */
+ zombiechan_open_failure,
+ zombiechan_send,
+ zombiechan_do_nothing, /* send_eof */
+ zombiechan_set_input_wanted,
+ zombiechan_log_close_msg,
+ zombiechan_want_close,
+};
+
+Channel *zombiechan_new(void)
+{
+ Channel *chan = snew(Channel);
+ chan->vt = &zombiechan_channelvt;
+ chan->initial_fixed_window_size = 0;
+ return chan;
+}
+
+static void zombiechan_free(Channel *chan)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+ sfree(chan);
+}
+
+static void zombiechan_do_nothing(Channel *chan)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+}
+
+static void zombiechan_open_failure(Channel *chan, const char *errtext)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+}
+
+static int zombiechan_send(Channel *chan, int is_stderr,
+ const void *data, int length)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+ return 0;
+}
+
+static void zombiechan_set_input_wanted(Channel *chan, int enable)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+}
+
+static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof)
+{
+ return TRUE;
+}
+
static int ssh_do_close(Ssh ssh, int notify_exit)
{
int ret = 0;
@@ -2428,7 +2489,21 @@ static void ssh_throttle_conn(Ssh ssh, int adjust)
}
}
-static void ssh_agentf_try_forward(struct ssh_channel *c);
+static void ssh_channel_check_throttle(struct ssh_channel *c)
+{
+ /*
+ * We don't want this channel to read further input if this
+ * particular channel has a backed-up SSH window, or if the
+ * outgoing side of the whole SSH connection is currently
+ * throttled, or if this channel already has an outgoing EOF
+ * either sent or pending.
+ */
+ chan_set_input_wanted(c->chan,
+ !c->throttled_by_backlog &&
+ !c->ssh->throttled_all &&
+ !c->pending_eof &&
+ !(c->closes & CLOSES_SENT_EOF));
+}
/*
* Throttle or unthrottle _all_ local data streams (for when sends
@@ -2445,29 +2520,8 @@ static void ssh_throttle_all(Ssh ssh, int enable, int bufsize)
ssh->overall_bufsize = bufsize;
if (!ssh->channels)
return;
- for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) {
- switch (c->type) {
- case CHAN_MAINSESSION:
- /*
- * This is treated separately, outside the switch.
- */
- break;
- case CHAN_X11:
- x11_override_throttle(c->u.x11.xconn, enable);
- break;
- case CHAN_AGENT:
- /* Agent forwarding channels are buffer-managed by
- * checking ssh->throttled_all in ssh_agentf_try_forward.
- * So at the moment we _un_throttle again, we must make an
- * attempt to do something. */
- if (!enable)
- ssh_agentf_try_forward(c);
- break;
- case CHAN_SOCKDATA:
- pfd_override_throttle(c->u.pfd.pf, enable);
- break;
- }
- }
+ for (i = 0; NULL != (c = index234(ssh->channels, i)); i++)
+ ssh_channel_check_throttle(c);
}
static void ssh_agent_callback(void *sshv, void *reply, int replylen)
@@ -2503,140 +2557,6 @@ static void ssh_dialog_callback(void *sshv, int ret)
queue_idempotent_callback(&ssh->incoming_data_consumer);
}
-static void ssh_agentf_got_response(struct ssh_channel *c,
- void *reply, int replylen)
-{
- c->u.a.pending = NULL;
-
- assert(!(c->closes & CLOSES_SENT_EOF));
-
- if (!reply) {
- /* The real agent didn't send any kind of reply at all for
- * some reason, so fake an SSH_AGENT_FAILURE. */
- reply = "\0\0\0\1\5";
- replylen = 5;
- }
-
- ssh_send_channel_data(c, reply, replylen);
-}
-
-static void ssh_agentf_callback(void *cv, void *reply, int replylen);
-
-static void ssh_agentf_try_forward(struct ssh_channel *c)
-{
- unsigned datalen, length;
- strbuf *message;
- unsigned char msglen[4];
- void *reply;
- int replylen;
-
- /*
- * Don't try to parallelise agent requests. Wait for each one to
- * return before attempting the next.
- */
- if (c->u.a.pending)
- return;
-
- /*
- * If the outgoing side of the channel connection is currently
- * throttled (for any reason, either that channel's window size or
- * the entire SSH connection being throttled), don't submit any
- * new forwarded requests to the real agent. This causes the input
- * side of the agent forwarding not to be emptied, exerting the
- * required back-pressure on the remote client, and encouraging it
- * to read our responses before sending too many more requests.
- */
- if (c->ssh->throttled_all ||
- (c->ssh->version == 2 && c->v.v2.remwindow == 0))
- return;
-
- if (c->closes & CLOSES_SENT_EOF) {
- /*
- * If we've already sent outgoing EOF, there's nothing we can
- * do with incoming data except consume it and throw it away.
- */
- bufchain_clear(&c->u.a.inbuffer);
- return;
- }
-
- while (1) {
- /*
- * Try to extract a complete message from the input buffer.
- */
- datalen = bufchain_size(&c->u.a.inbuffer);
- if (datalen < 4)
- break; /* not even a length field available yet */
-
- bufchain_fetch(&c->u.a.inbuffer, msglen, 4);
- length = GET_32BIT(msglen);
-
- if (length > AGENT_MAX_MSGLEN-4) {
- /*
- * If the remote has sent a message that's just _too_
- * long, we should reject it in advance of seeing the rest
- * of the incoming message, and also close the connection
- * for good measure (which avoids us having to faff about
- * with carefully ignoring just the right number of bytes
- * from the overlong message).
- */
- ssh_agentf_got_response(c, NULL, 0);
- sshfwd_write_eof(c);
- return;
- }
-
- if (length > datalen - 4)
- break; /* a whole message is not yet available */
-
- bufchain_consume(&c->u.a.inbuffer, 4);
-
- message = strbuf_new_for_agent_query();
- bufchain_fetch_consume(
- &c->u.a.inbuffer, strbuf_append(message, length), length);
- c->u.a.pending = agent_query(
- message, &reply, &replylen, ssh_agentf_callback, c);
- strbuf_free(message);
-
- if (c->u.a.pending)
- return; /* agent_query promised to reply in due course */
-
- /*
- * If the agent gave us an answer immediately, pass it
- * straight on and go round this loop again.
- */
- ssh_agentf_got_response(c, reply, replylen);
- sfree(reply);
- }
-
- /*
- * If we get here (i.e. we left the above while loop via 'break'
- * rather than 'return'), that means we've determined that the
- * input buffer for the agent forwarding connection doesn't
- * contain a complete request.
- *
- * So if there's potentially more data to come, we can return now,
- * and wait for the remote client to send it. But if the remote
- * has sent EOF, it would be a mistake to do that, because we'd be
- * waiting a long time. So this is the moment to check for EOF,
- * and respond appropriately.
- */
- if (c->closes & CLOSES_RCVD_EOF)
- sshfwd_write_eof(c);
-}
-
-static void ssh_agentf_callback(void *cv, void *reply, int replylen)
-{
- struct ssh_channel *c = (struct ssh_channel *)cv;
-
- ssh_agentf_got_response(c, reply, replylen);
- sfree(reply);
-
- /*
- * Now try to extract and send further messages from the channel's
- * input-side buffer.
- */
- ssh_agentf_try_forward(c);
-}
-
/*
* Client-initiated disconnection. Send a DISCONNECT if `wire_reason'
* non-NULL, otherwise just close the connection. `client_reason' == NULL
@@ -4297,10 +4217,9 @@ static void ssh1_smsg_x11_open(Ssh ssh, PktIn *pktin)
c->ssh = ssh;
ssh_channel_init(c);
- c->u.x11.xconn = x11_init(ssh->x11authtree, c, NULL, -1);
+ c->chan = x11_new_channel(ssh->x11authtree, c, NULL, -1, FALSE);
c->remoteid = remoteid;
c->halfopen = FALSE;
- c->type = CHAN_X11; /* identify channel type */
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
put_uint32(pkt, c->remoteid);
put_uint32(pkt, c->localid);
@@ -4328,9 +4247,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, PktIn *pktin)
ssh_channel_init(c);
c->remoteid = remoteid;
c->halfopen = FALSE;
- c->type = CHAN_AGENT; /* identify channel type */
- c->u.a.pending = NULL;
- bufchain_init(&c->u.a.inbuffer);
+ c->chan = agentf_new(c);
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
put_uint32(pkt, c->remoteid);
put_uint32(pkt, c->localid);
@@ -4369,7 +4286,7 @@ static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
logeventf(ssh, "Received remote port open request for %s:%d",
pf.dhost, port);
- err = pfd_connect(&c->u.pfd.pf, pf.dhost, port,
+ err = pfd_connect(&c->chan, pf.dhost, port,
c, ssh->conf, pfp->pfrec->addressfamily);
if (err != NULL) {
logeventf(ssh, "Port open failed: %s", err);
@@ -4382,7 +4299,6 @@ static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
ssh_channel_init(c);
c->remoteid = remoteid;
c->halfopen = FALSE;
- c->type = CHAN_SOCKDATA; /* identify channel type */
pkt = ssh_bpp_new_pktout(
ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
put_uint32(pkt, c->remoteid);
@@ -4400,12 +4316,10 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, PktIn *pktin)
struct ssh_channel *c;
c = ssh_channel_msg(ssh, pktin);
- if (c && c->type == CHAN_SOCKDATA) {
- c->remoteid = get_uint32(pktin);
- c->halfopen = FALSE;
- c->throttling_conn = 0;
- pfd_confirm(c->u.pfd.pf);
- }
+ chan_open_confirmation(c->chan);
+ c->remoteid = get_uint32(pktin);
+ c->halfopen = FALSE;
+ c->throttling_conn = 0;
if (c && c->pending_eof) {
/*
@@ -4423,12 +4337,11 @@ static void ssh1_msg_channel_open_failure(Ssh ssh, PktIn *pktin)
struct ssh_channel *c;
c = ssh_channel_msg(ssh, pktin);
- if (c && c->type == CHAN_SOCKDATA) {
- logevent("Forwarded connection refused by server");
- pfd_close(c->u.pfd.pf);
- del234(ssh->channels, c);
- sfree(c);
- }
+ chan_open_failed(c->chan, NULL);
+ chan_free(c->chan);
+
+ del234(ssh->channels, c);
+ sfree(c);
}
static void ssh1_msg_channel_close(Ssh ssh, PktIn *pktin)
@@ -4473,39 +4386,11 @@ static void ssh1_msg_channel_close(Ssh ssh, PktIn *pktin)
}
}
-/*
- * Handle incoming data on an SSH-1 or SSH-2 agent-forwarding channel.
- */
-static int ssh_agent_channel_data(struct ssh_channel *c, const void *data,
- int length)
-{
- bufchain_add(&c->u.a.inbuffer, data, length);
- ssh_agentf_try_forward(c);
-
- /*
- * We exert back-pressure on an agent forwarding client if and
- * only if we're waiting for the response to an asynchronous agent
- * request. This prevents the client running out of window while
- * receiving the _first_ message, but means that if any message
- * takes time to process, the client will be discouraged from
- * sending an endless stream of further ones after it.
- */
- return (c->u.a.pending ? bufchain_size(&c->u.a.inbuffer) : 0);
-}
-
static int ssh_channel_data(struct ssh_channel *c, int is_stderr,
const void *data, int length)
{
- switch (c->type) {
- case CHAN_MAINSESSION:
- return from_backend(c->ssh->frontend, is_stderr, data, length);
- case CHAN_X11:
- return x11_send(c->u.x11.xconn, data, length);
- case CHAN_SOCKDATA:
- return pfd_send(c->u.pfd.pf, data, length);
- case CHAN_AGENT:
- return ssh_agent_channel_data(c, data, length);
- }
+ if (c->chan)
+ chan_send(c->chan, is_stderr, data, length);
return 0;
}
@@ -6933,6 +6818,8 @@ static void do_ssh2_transport(void *vctx)
static int ssh_send_channel_data(struct ssh_channel *c, const char *buf,
int len)
{
+ assert(!(c->closes & CLOSES_SENT_EOF));
+
if (c->ssh->version == 2) {
bufchain_add(&c->v.v2.outbuffer, buf, len);
return ssh2_try_send(c);
@@ -7002,23 +6889,8 @@ static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
return; /* don't send on channels we've EOFed */
bufsize = ssh2_try_send(c);
if (bufsize == 0) {
- switch (c->type) {
- case CHAN_MAINSESSION:
- /* stdin need not receive an unthrottle
- * notification since it will be polled */
- break;
- case CHAN_X11:
- x11_unthrottle(c->u.x11.xconn);
- break;
- case CHAN_AGENT:
- /* Now that we've successfully sent all the outgoing
- * replies we had, try to process more incoming data. */
- ssh_agentf_try_forward(c);
- break;
- case CHAN_SOCKDATA:
- pfd_unthrottle(c->u.pfd.pf);
- break;
- }
+ c->throttled_by_backlog = FALSE;
+ ssh_channel_check_throttle(c);
}
}
@@ -7036,7 +6908,9 @@ static int ssh_is_simple(Ssh ssh)
}
/*
- * Set up most of a new ssh_channel.
+ * Set up most of a new ssh_channel. Nulls out sharectx, but leaves
+ * chan untouched (since it will sometimes have been filled in before
+ * calling this).
*/
static void ssh_channel_init(struct ssh_channel *c)
{
@@ -7045,6 +6919,7 @@ static void ssh_channel_init(struct ssh_channel *c)
c->closes = 0;
c->pending_eof = FALSE;
c->throttling_conn = FALSE;
+ c->sharectx = NULL;
if (ssh->version == 2) {
c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
ssh_is_simple(ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
@@ -7162,11 +7037,12 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin)
return;
/*
- * Also, never widen the window for an X11 channel when we're
- * still waiting to see its initial auth and may yet hand it off
- * to a downstream.
+ * If the client-side Channel is in an initial setup phase with a
+ * fixed window size, e.g. for an X11 channel when we're still
+ * waiting to see its initial auth and may yet hand it off to a
+ * downstream, don't send any WINDOW_ADJUST either.
*/
- if (c->type == CHAN_X11 && c->u.x11.initial)
+ if (c->chan->initial_fixed_window_size)
return;
/*
@@ -7241,7 +7117,13 @@ static struct ssh_channel *ssh_channel_msg(Ssh ssh, PktIn *pktin)
halfopen_ok = (pktin->type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION ||
pktin->type == SSH2_MSG_CHANNEL_OPEN_FAILURE);
c = find234(ssh->channels, &localid, ssh_channelfind);
- if (!c || (c->type != CHAN_SHARING && (c->halfopen != halfopen_ok))) {
+ if (c && c->sharectx) {
+ share_got_pkt_from_server(c->sharectx, pktin->type,
+ BinarySource_UPCAST(pktin)->data,
+ BinarySource_UPCAST(pktin)->len);
+ return NULL;
+ }
+ if (!c || c->halfopen != halfopen_ok) {
char *buf = dupprintf("Received %s for %s channel %u",
ssh_pkt_type(ssh, pktin->type),
!c ? "nonexistent" :
@@ -7251,12 +7133,6 @@ static struct ssh_channel *ssh_channel_msg(Ssh ssh, PktIn *pktin)
sfree(buf);
return NULL;
}
- if (c->type == CHAN_SHARING) {
- share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
- BinarySource_UPCAST(pktin)->data,
- BinarySource_UPCAST(pktin)->len);
- return NULL;
- }
return c;
}
@@ -7421,36 +7297,20 @@ void ssh_sharing_logf(Ssh ssh, unsigned id, const char *logfmt, ...)
/*
* Close any local socket and free any local resources associated with
- * a channel. This converts the channel into a CHAN_ZOMBIE.
+ * a channel. This converts the channel into a zombie.
*/
static void ssh_channel_close_local(struct ssh_channel *c, char const *reason)
{
Ssh ssh = c->ssh;
- char const *msg = NULL;
-
- switch (c->type) {
- case CHAN_MAINSESSION:
- ssh->mainchan = NULL;
- update_specials_menu(ssh->frontend);
- break;
- case CHAN_X11:
- assert(c->u.x11.xconn != NULL);
- x11_close(c->u.x11.xconn);
- msg = "Forwarded X11 connection terminated";
- break;
- case CHAN_AGENT:
- if (c->u.a.pending)
- agent_cancel_query(c->u.a.pending);
- bufchain_clear(&c->u.a.inbuffer);
- msg = "Agent-forwarding connection closed";
- break;
- case CHAN_SOCKDATA:
- assert(c->u.pfd.pf != NULL);
- pfd_close(c->u.pfd.pf);
- msg = "Forwarded port closed";
- break;
- }
- c->type = CHAN_ZOMBIE;
+ const char *msg = NULL;
+
+ if (c->sharectx)
+ return;
+
+ msg = chan_log_close_msg(c->chan);
+ chan_free(c->chan);
+ c->chan = zombiechan_new();
+
if (msg != NULL) {
if (reason != NULL)
logeventf(ssh, "%s %s", msg, reason);
@@ -7495,7 +7355,8 @@ static void ssh2_channel_check_close(struct ssh_channel *c)
}
if ((!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) ||
- c->type == CHAN_ZOMBIE) &&
+ chan_want_close(c->chan, (c->closes & CLOSES_SENT_EOF),
+ (c->closes & CLOSES_RCVD_EOF))) &&
!c->v.v2.chanreq_head &&
!(c->closes & CLOSES_SENT_CLOSE)) {
/*
@@ -7526,34 +7387,7 @@ static void ssh_channel_got_eof(struct ssh_channel *c)
return; /* already seen EOF */
c->closes |= CLOSES_RCVD_EOF;
- if (c->type == CHAN_X11) {
- assert(c->u.x11.xconn != NULL);
- x11_send_eof(c->u.x11.xconn);
- } else if (c->type == CHAN_AGENT) {
- /* Just call try_forward, which will respond to the EOF now if
- * appropriate, or wait until the queue of outstanding
- * requests is dealt with if not */
- ssh_agentf_try_forward(c);
- } else if (c->type == CHAN_SOCKDATA) {
- assert(c->u.pfd.pf != NULL);
- pfd_send_eof(c->u.pfd.pf);
- } else if (c->type == CHAN_MAINSESSION) {
- Ssh ssh = c->ssh;
-
- if (!ssh->sent_console_eof &&
- (from_backend_eof(ssh->frontend) || ssh->got_pty)) {
- /*
- * Either from_backend_eof told us that the front end
- * wants us to close the outgoing side of the connection
- * as soon as we see EOF from the far end, or else we've
- * unilaterally decided to do that because we've allocated
- * a remote pty and hence EOF isn't a particularly
- * meaningful concept.
- */
- sshfwd_write_eof(c);
- }
- ssh->sent_console_eof = TRUE;
- }
+ chan_send_eof(c->chan);
}
static void ssh2_msg_channel_eof(Ssh ssh, PktIn *pktin)
@@ -7604,22 +7438,6 @@ static void ssh2_msg_channel_close(Ssh ssh, PktIn *pktin)
* it would have just sent CHANNEL_EOF.)
*/
if (!(c->closes & CLOSES_SENT_EOF)) {
- /*
- * Make sure we don't read any more from whatever our local
- * data source is for this channel.
- */
- switch (c->type) {
- case CHAN_MAINSESSION:
- ssh->send_ok = 0; /* stop trying to read from stdin */
- break;
- case CHAN_X11:
- x11_override_throttle(c->u.x11.xconn, 1);
- break;
- case CHAN_SOCKDATA:
- pfd_override_throttle(c->u.pfd.pf, 1);
- break;
- }
-
/*
* Abandon any buffered data we still wanted to send to this
* channel. Receiving a CHANNEL_CLOSE is an indication that
@@ -7633,6 +7451,13 @@ static void ssh2_msg_channel_close(Ssh ssh, PktIn *pktin)
* Send outgoing EOF.
*/
sshfwd_write_eof(c);
+
+ /*
+ * Make sure we don't read any more from whatever our local
+ * data source is for this channel. (This will pick up on the
+ * changes made by sshfwd_write_eof.)
+ */
+ ssh_channel_check_throttle(c);
}
/*
@@ -7657,32 +7482,22 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, PktIn *pktin)
c->v.v2.remwindow = get_uint32(pktin);
c->v.v2.remmaxpkt = get_uint32(pktin);
- if (c->type == CHAN_SOCKDATA) {
- assert(c->u.pfd.pf != NULL);
- pfd_confirm(c->u.pfd.pf);
- } else if (c->type == CHAN_ZOMBIE) {
- /*
- * This case can occur if a local socket error occurred
- * between us sending out CHANNEL_OPEN and receiving
- * OPEN_CONFIRMATION. In this case, all we can do is
- * immediately initiate close proceedings now that we know the
- * server's id to put in the close message.
- */
- ssh2_channel_check_close(c);
- } else {
- /*
- * We never expect to receive OPEN_CONFIRMATION for any
- * *other* channel type (since only local-to-remote port
- * forwardings cause us to send CHANNEL_OPEN after the main
- * channel is live - all other auxiliary channel types are
- * initiated from the server end). It's safe to enforce this
- * by assertion rather than by ssh_disconnect, because the
- * real point is that we never constructed a half-open channel
- * structure in the first place with any type other than the
- * above.
- */
- assert(!"Funny channel type in ssh2_msg_channel_open_confirmation");
- }
+ chan_open_confirmation(c->chan);
+
+ /*
+ * Now that the channel is fully open, it's possible in principle
+ * to immediately close it. Check whether it wants us to!
+ *
+ * This can occur if a local socket error occurred between us
+ * sending out CHANNEL_OPEN and receiving OPEN_CONFIRMATION. If
+ * that happens, all we can do is immediately initiate close
+ * proceedings now that we know the server's id to put in the
+ * close message. We'll have handled that in this code by having
+ * already turned c->chan into a zombie, so its want_close method
+ * (which ssh2_channel_check_close will consult) will already be
+ * returning TRUE.
+ */
+ ssh2_channel_check_close(c);
if (c->pending_eof)
ssh_channel_try_eof(c); /* in case we had a pending EOF */
@@ -7724,31 +7539,12 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, PktIn *pktin)
return;
assert(c->halfopen); /* ssh_channel_msg will have enforced this */
- if (c->type == CHAN_SOCKDATA) {
+ {
char *errtext = ssh2_channel_open_failure_error_text(pktin);
- logeventf(ssh, "Forwarded connection refused by server: %s", errtext);
+ chan_open_failed(c->chan, errtext);
sfree(errtext);
- pfd_close(c->u.pfd.pf);
- } else if (c->type == CHAN_ZOMBIE) {
- /*
- * This case can occur if a local socket error occurred
- * between us sending out CHANNEL_OPEN and receiving
- * OPEN_FAILURE. In this case, we need do nothing except allow
- * the code below to throw the half-open channel away.
- */
- } else {
- /*
- * We never expect to receive OPEN_FAILURE for any *other*
- * channel type (since only local-to-remote port forwardings
- * cause us to send CHANNEL_OPEN after the main channel is
- * live - all other auxiliary channel types are initiated from
- * the server end). It's safe to enforce this by assertion
- * rather than by ssh_disconnect, because the real point is
- * that we never constructed a half-open channel structure in
- * the first place with any type other than the above.
- */
- assert(!"Funny channel type in ssh2_msg_channel_open_failure");
}
+ chan_free(c->chan);
del234(ssh->channels, c);
sfree(c);
@@ -7970,7 +7766,6 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
const char *error = NULL;
struct ssh_channel *c;
unsigned remid, winsize, pktsize;
- unsigned our_winsize_override = 0;
PktOut *pktout;
type = get_string(pktin);
@@ -7991,22 +7786,8 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
if (!ssh->X11_fwd_enabled && !ssh->connshare)
error = "X11 forwarding is not enabled";
else {
- c->u.x11.xconn = x11_init(ssh->x11authtree, c,
- addrstr, peerport);
- c->type = CHAN_X11;
- c->u.x11.initial = TRUE;
-
- /*
- * If we are a connection-sharing upstream, then we should
- * initially present a very small window, adequate to take
- * the X11 initial authorisation packet but not much more.
- * Downstream will then present us a larger window (by
- * fiat of the connection-sharing protocol) and we can
- * guarantee to send a positive-valued WINDOW_ADJUST.
- */
- if (ssh->connshare)
- our_winsize_override = 128;
-
+ c->chan = x11_new_channel(ssh->x11authtree, c, addrstr, peerport,
+ ssh->connshare != NULL);
logevent("Opened X11 forward channel");
}
@@ -8044,7 +7825,7 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
return;
}
- err = pfd_connect(&c->u.pfd.pf, realpf->dhost, realpf->dport,
+ err = pfd_connect(&c->chan, realpf->dhost, realpf->dport,
c, ssh->conf, realpf->pfrec->addressfamily);
logeventf(ssh, "Attempting to forward remote port to "
"%s:%d", realpf->dhost, realpf->dport);
@@ -8054,17 +7835,13 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
error = "Port open failed";
} else {
logevent("Forwarded port opened successfully");
- c->type = CHAN_SOCKDATA;
}
}
} else if (ptrlen_eq_string(type, "auth-agent@openssh.com")) {
if (!ssh->agentfwd_enabled)
error = "Agent forwarding is not enabled";
- else {
- c->type = CHAN_AGENT; /* identify channel type */
- bufchain_init(&c->u.a.inbuffer);
- c->u.a.pending = NULL;
- }
+ else
+ c->chan = agentf_new(c);
} else {
error = "Unsupported channel type requested";
}
@@ -8084,9 +7861,9 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
ssh_channel_init(c);
c->v.v2.remwindow = winsize;
c->v.v2.remmaxpkt = pktsize;
- if (our_winsize_override) {
+ if (c->chan->initial_fixed_window_size) {
c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
- our_winsize_override;
+ c->chan->initial_fixed_window_size;
}
pktout = ssh_bpp_new_pktout(
ssh->bpp, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
@@ -8108,30 +7885,28 @@ void sshfwd_x11_sharing_handover(struct ssh_channel *c,
* This function is called when we've just discovered that an X
* forwarding channel on which we'd been handling the initial auth
* ourselves turns out to be destined for a connection-sharing
- * downstream. So we turn the channel into a CHAN_SHARING, meaning
+ * downstream. So we turn the channel into a sharing one, meaning
* that we completely stop tracking windows and buffering data and
* just pass more or less unmodified SSH messages back and forth.
*/
- c->type = CHAN_SHARING;
- c->u.sharing.ctx = share_cs;
+ c->sharectx = share_cs;
share_setup_x11_channel(share_cs, share_chan,
c->localid, c->remoteid, c->v.v2.remwindow,
c->v.v2.remmaxpkt, c->v.v2.locwindow,
peer_addr, peer_port, endian,
protomajor, protominor,
initial_data, initial_len);
+ chan_free(c->chan);
+ c->chan = NULL;
}
-void sshfwd_x11_is_local(struct ssh_channel *c)
+void sshfwd_window_override_removed(struct ssh_channel *c)
{
/*
- * This function is called when we've just discovered that an X
- * forwarding channel is _not_ destined for a connection-sharing
- * downstream but we're going to handle it ourselves. We stop
- * presenting a cautiously small window and go into ordinary data
- * exchange mode.
+ * This function is called when a client-side Channel has just
+ * stopped requiring an initial fixed-size window.
*/
- c->u.x11.initial = FALSE;
+ assert(!c->chan->initial_fixed_window_size);
if (c->ssh->version == 2)
ssh2_set_window(
c, ssh_is_simple(c->ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE);
@@ -9879,6 +9654,108 @@ static void ssh2_connection_setup(Ssh ssh)
ssh->channels = newtree234(ssh_channelcmp);
}
+typedef struct mainchan {
+ Ssh ssh;
+ struct ssh_channel *c;
+
+ Channel chan;
+} mainchan;
+
+static void mainchan_free(Channel *chan);
+static void mainchan_open_confirmation(Channel *chan);
+static void mainchan_open_failure(Channel *chan, const char *errtext);
+static int mainchan_send(Channel *chan, int is_stderr, const void *, int);
+static void mainchan_send_eof(Channel *chan);
+static void mainchan_set_input_wanted(Channel *chan, int wanted);
+static char *mainchan_log_close_msg(Channel *chan);
+
+static const struct ChannelVtable mainchan_channelvt = {
+ mainchan_free,
+ mainchan_open_confirmation,
+ mainchan_open_failure,
+ mainchan_send,
+ mainchan_send_eof,
+ mainchan_set_input_wanted,
+ mainchan_log_close_msg,
+ chan_no_eager_close,
+};
+
+static mainchan *mainchan_new(Ssh ssh)
+{
+ mainchan *mc = snew(mainchan);
+ mc->ssh = ssh;
+ mc->c = NULL;
+ mc->chan.vt = &mainchan_channelvt;
+ mc->chan.initial_fixed_window_size = 0;
+ return mc;
+}
+
+static void mainchan_free(Channel *chan)
+{
+ assert(chan->vt == &mainchan_channelvt);
+ mainchan *mc = FROMFIELD(chan, mainchan, chan);
+ mc->ssh->mainchan = NULL;
+ sfree(mc);
+}
+
+static void mainchan_open_confirmation(Channel *chan)
+{
+ assert(FALSE && "OPEN_CONFIRMATION for main channel should be "
+ "handled by connection layer setup");
+}
+
+static void mainchan_open_failure(Channel *chan, const char *errtext)
+{
+ assert(FALSE && "OPEN_FAILURE for main channel should be "
+ "handled by connection layer setup");
+}
+
+static int mainchan_send(Channel *chan, int is_stderr,
+ const void *data, int length)
+{
+ assert(chan->vt == &mainchan_channelvt);
+ mainchan *mc = FROMFIELD(chan, mainchan, chan);
+ return from_backend(mc->ssh->frontend, is_stderr, data, length);
+}
+
+static void mainchan_send_eof(Channel *chan)
+{
+ assert(chan->vt == &mainchan_channelvt);
+ mainchan *mc = FROMFIELD(chan, mainchan, chan);
+
+ if (!mc->ssh->sent_console_eof &&
+ (from_backend_eof(mc->ssh->frontend) || mc->ssh->got_pty)) {
+ /*
+ * Either from_backend_eof told us that the front end wants us
+ * to close the outgoing side of the connection as soon as we
+ * see EOF from the far end, or else we've unilaterally
+ * decided to do that because we've allocated a remote pty and
+ * hence EOF isn't a particularly meaningful concept.
+ */
+ sshfwd_write_eof(mc->c);
+ }
+ mc->ssh->sent_console_eof = TRUE;
+}
+
+static void mainchan_set_input_wanted(Channel *chan, int wanted)
+{
+ assert(chan->vt == &mainchan_channelvt);
+ mainchan *mc = FROMFIELD(chan, mainchan, chan);
+
+ /*
+ * This is the main channel of the SSH session, i.e. the one tied
+ * to the standard input (or GUI) of the primary SSH client user
+ * interface. So ssh->send_ok is how we control whether we're
+ * reading from that input.
+ */
+ mc->ssh->send_ok = wanted;
+}
+
+static char *mainchan_log_close_msg(Channel *chan)
+{
+ return dupstr("Main session channel closed");
+}
+
static void do_ssh2_connection(void *vctx)
{
Ssh ssh = (Ssh)vctx;
@@ -9907,22 +9784,23 @@ static void do_ssh2_connection(void *vctx)
if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) {
ssh->mainchan = NULL;
} else {
- ssh->mainchan = snew(struct ssh_channel);
- ssh->mainchan->ssh = ssh;
- ssh->mainchan->type = CHAN_MAINSESSION;
- ssh_channel_init(ssh->mainchan);
+ mainchan *mc = mainchan_new(ssh);
if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
/*
* Just start a direct-tcpip channel and use it as the main
* channel.
*/
- ssh_send_port_open(ssh->mainchan,
- conf_get_str(ssh->conf, CONF_ssh_nc_host),
- conf_get_int(ssh->conf, CONF_ssh_nc_port),
- "main channel");
+ ssh->mainchan = mc->c = ssh_send_port_open
+ (ssh, conf_get_str(ssh->conf, CONF_ssh_nc_host),
+ conf_get_int(ssh->conf, CONF_ssh_nc_port),
+ "main channel", &mc->chan);
ssh->ncmode = TRUE;
} else {
+ ssh->mainchan = mc->c = snew(struct ssh_channel);
+ ssh->mainchan->ssh = ssh;
+ ssh_channel_init(ssh->mainchan);
+ ssh->mainchan->chan = &mc->chan;
s->pktout = ssh2_chanopen_init(ssh->mainchan, "session");
logevent("Opening session as main channel");
ssh2_pkt_send(ssh, s->pktout);
@@ -11295,19 +11173,6 @@ static void ssh_special(Backend *be, Telnet_Special code)
}
}
-void *new_sock_channel(Ssh ssh, struct PortForwarding *pf)
-{
- struct ssh_channel *c;
- c = snew(struct ssh_channel);
-
- c->ssh = ssh;
- ssh_channel_init(c);
- c->halfopen = TRUE;
- c->type = CHAN_SOCKDATA;/* identify channel type */
- c->u.pfd.pf = pf;
- return c;
-}
-
unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx)
{
struct ssh_channel *c;
@@ -11315,8 +11180,8 @@ unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx)
c->ssh = ssh;
ssh_channel_init(c);
- c->type = CHAN_SHARING;
- c->u.sharing.ctx = sharing_ctx;
+ c->chan = NULL;
+ c->sharectx = sharing_ctx;
return c->localid;
}
@@ -11367,13 +11232,17 @@ static void ssh_unthrottle(Backend *be, int bufsize)
queue_idempotent_callback(&ssh->incoming_data_consumer);
}
-void ssh_send_port_open(void *channel, const char *hostname, int port,
- const char *org)
+struct ssh_channel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
+ const char *org, Channel *chan)
{
- struct ssh_channel *c = (struct ssh_channel *)channel;
- Ssh ssh = c->ssh;
+ struct ssh_channel *c = snew(struct ssh_channel);
PktOut *pktout;
+ c->ssh = ssh;
+ ssh_channel_init(c);
+ c->halfopen = TRUE;
+ c->chan = chan;
+
logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org);
if (ssh->version == 1) {
@@ -11405,6 +11274,8 @@ void ssh_send_port_open(void *channel, const char *hostname, int port,
put_uint32(pktout, 0);
ssh2_pkt_send(ssh, pktout);
}
+
+ return c;
}
static int ssh_connected(Backend *be)
diff --git a/ssh.h b/ssh.h
index 3fe8266b..82810d78 100644
--- a/ssh.h
+++ b/ssh.h
@@ -14,12 +14,12 @@ extern void sshfwd_write_eof(struct ssh_channel *c);
extern void sshfwd_unclean_close(struct ssh_channel *c, const char *err);
extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize);
Conf *sshfwd_get_conf(struct ssh_channel *c);
+void sshfwd_window_override_removed(struct ssh_channel *c);
void sshfwd_x11_sharing_handover(struct ssh_channel *c,
void *share_cs, void *share_chan,
const char *peer_addr, int peer_port,
int endian, int protomajor, int protominor,
const void *initial_data, int initial_len);
-void sshfwd_x11_is_local(struct ssh_channel *c);
/*
* Buffer management constants. There are several of these for
@@ -185,6 +185,8 @@ void share_setup_x11_channel(void *csv, void *chanv,
int protomajor, int protominor,
const void *initial_data, int initial_len);
+Frontend *ssh_get_frontend(Ssh ssh);
+
/*
* Useful thing.
*/
@@ -665,19 +667,12 @@ void logevent(Frontend *, const char *);
struct PortForwarding;
/* Allocate and register a new channel for port forwarding */
-void *new_sock_channel(Ssh ssh, struct PortForwarding *pf);
-void ssh_send_port_open(void *channel, const char *hostname, int port,
- const char *org);
+struct ssh_channel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
+ const char *org, Channel *chan);
/* Exports from portfwd.c */
-extern char *pfd_connect(struct PortForwarding **pf, char *hostname, int port,
- void *c, Conf *conf, int addressfamily);
-extern void pfd_close(struct PortForwarding *);
-extern int pfd_send(struct PortForwarding *, const void *data, int len);
-extern void pfd_send_eof(struct PortForwarding *);
-extern void pfd_confirm(struct PortForwarding *);
-extern void pfd_unthrottle(struct PortForwarding *);
-extern void pfd_override_throttle(struct PortForwarding *, int enable);
+extern char *pfd_connect(Channel **chan_ret, char *hostname, int port,
+ struct ssh_channel *c, Conf *conf, int addressfamily);
struct PortListener;
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
extern char *pfl_listen(char *desthost, int destport, char *srcaddr,
@@ -750,13 +745,9 @@ extern struct X11Display *x11_setup_display(const char *display, Conf *);
void x11_free_display(struct X11Display *disp);
struct X11FakeAuth *x11_invent_fake_auth(tree234 *t, int authtype);
void x11_free_fake_auth(struct X11FakeAuth *auth);
-struct X11Connection; /* opaque outside x11fwd.c */
-struct X11Connection *x11_init(tree234 *authtree, void *, const char *, int);
-extern void x11_close(struct X11Connection *);
-extern int x11_send(struct X11Connection *, const void *, int);
-extern void x11_send_eof(struct X11Connection *s);
-extern void x11_unthrottle(struct X11Connection *s);
-extern void x11_override_throttle(struct X11Connection *s, int enable);
+Channel *x11_new_channel(tree234 *authtree, struct ssh_channel *c,
+ const char *peeraddr, int peerport,
+ int connection_sharing_possible);
char *x11_display(const char *display);
/* Platform-dependent X11 functions */
extern void platform_get_x11_auth(struct X11Display *display, Conf *);
@@ -786,6 +777,8 @@ void x11_get_auth_from_authfile(struct X11Display *display,
int x11_identify_auth_proto(ptrlen protoname);
void *x11_dehexify(ptrlen hex, int *outlen);
+Channel *agentf_new(struct ssh_channel *c);
+
Bignum copybn(Bignum b);
Bignum bn_power_2(int n);
void bn_restore_invariant(Bignum b);
diff --git a/sshchan.h b/sshchan.h
new file mode 100644
index 00000000..26462bc6
--- /dev/null
+++ b/sshchan.h
@@ -0,0 +1,59 @@
+/*
+ * Abstraction of the various ways to handle the local end of an SSH
+ * connection-layer channel.
+ */
+
+#ifndef PUTTY_SSHCHAN_H
+#define PUTTY_SSHCHAN_H
+
+struct ChannelVtable {
+ void (*free)(Channel *);
+
+ /* Called for channel types that were created at the same time as
+ * we sent an outgoing CHANNEL_OPEN, when the confirmation comes
+ * back from the server indicating that the channel has been
+ * opened, or the failure message indicating that it hasn't,
+ * respectively. In the latter case, this must _not_ free the
+ * Channel structure - the client will call the free method
+ * separately. But it might do logging or other local cleanup. */
+ void (*open_confirmation)(Channel *);
+ void (*open_failed)(Channel *, const char *error_text);
+
+ int (*send)(Channel *, int is_stderr, const void *buf, int len);
+ void (*send_eof)(Channel *);
+ void (*set_input_wanted)(Channel *, int wanted);
+
+ char *(*log_close_msg)(Channel *);
+
+ int (*want_close)(Channel *, int sent_local_eof, int rcvd_remote_eof);
+};
+
+struct Channel {
+ const struct ChannelVtable *vt;
+ unsigned initial_fixed_window_size;
+};
+
+#define chan_free(ch) ((ch)->vt->free(ch))
+#define chan_open_confirmation(ch) ((ch)->vt->open_confirmation(ch))
+#define chan_open_failed(ch, err) ((ch)->vt->open_failed(ch, err))
+#define chan_send(ch, err, buf, len) ((ch)->vt->send(ch, err, buf, len))
+#define chan_send_eof(ch) ((ch)->vt->send_eof(ch))
+#define chan_set_input_wanted(ch, wanted) \
+ ((ch)->vt->set_input_wanted(ch, wanted))
+#define chan_log_close_msg(ch) ((ch)->vt->send_eof(ch))
+#define chan_want_close(ch, leof, reof) ((ch)->vt->want_close(ch, leof, reof))
+
+/*
+ * Reusable methods you can put in vtables to give default handling of
+ * some of those functions.
+ */
+
+/* open_confirmation / open_failed for any channel it doesn't apply to */
+void chan_remotely_opened_confirmation(Channel *chan);
+void chan_remotely_opened_failure(Channel *chan, const char *errtext);
+
+/* want_close for any channel that wants the default behaviour of not
+ * closing until both directions have had an EOF */
+int chan_no_eager_close(Channel *, int, int);
+
+#endif /* PUTTY_SSHCHAN_H */
diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
index 6445fd57..a694bc44 100644
--- a/unix/uxpgnt.c
+++ b/unix/uxpgnt.c
@@ -161,13 +161,17 @@ int sshfwd_write(struct ssh_channel *c, const void *data, int len)
void sshfwd_write_eof(struct ssh_channel *c) { }
void sshfwd_unclean_close(struct ssh_channel *c, const char *err) { }
void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) {}
+void sshfwd_window_override_removed(struct ssh_channel *c) { }
+void chan_remotely_opened_confirmation(Channel *chan) { }
+void chan_remotely_opened_failure(Channel *chan, const char *err) { }
+int chan_no_eager_close(Channel *chan, int s, int r) { return FALSE; }
+
Conf *sshfwd_get_conf(struct ssh_channel *c) { return NULL; }
void sshfwd_x11_sharing_handover(struct ssh_channel *c,
void *share_cs, void *share_chan,
const char *peer_addr, int peer_port,
int endian, int protomajor, int protominor,
const void *initial_data, int initial_len) {}
-void sshfwd_x11_is_local(struct ssh_channel *c) {}
/*
* These functions are part of the plug for our connection to the X
diff --git a/x11fwd.c b/x11fwd.c
index 190b6f68..419d4968 100644
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -9,6 +9,7 @@
#include "putty.h"
#include "ssh.h"
+#include "sshchan.h"
#include "tree234.h"
#define GET_16BIT(endian, cp) \
@@ -26,7 +27,7 @@ struct XDMSeen {
unsigned char clientid[6];
};
-struct X11Connection {
+typedef struct X11Connection {
unsigned char firstpkt[12]; /* first X data packet */
tree234 *authtree;
struct X11Display *disp;
@@ -34,7 +35,7 @@ struct X11Connection {
unsigned char *auth_data;
int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize;
int verified;
- int throttled, throttle_override;
+ int input_wanted;
int no_data_sent_to_x_client;
char *peer_addr;
int peer_port;
@@ -42,7 +43,8 @@ struct X11Connection {
Socket s;
const Plug_vtable *plugvt;
-};
+ Channel chan;
+} X11Connection;
static int xdmseen_cmp(void *a, void *b)
{
@@ -666,11 +668,8 @@ static void x11_receive(Plug plug, int urgent, char *data, int len)
struct X11Connection *xconn = FROMFIELD(
plug, struct X11Connection, plugvt);
- if (sshfwd_write(xconn->c, data, len) > 0) {
- xconn->throttled = 1;
- xconn->no_data_sent_to_x_client = FALSE;
- sk_set_frozen(xconn->s, 1);
- }
+ xconn->no_data_sent_to_x_client = FALSE;
+ sshfwd_write(xconn->c, data, len);
}
static void x11_sent(Plug plug, int bufsize)
@@ -707,12 +706,30 @@ static const Plug_vtable X11Connection_plugvt = {
NULL
};
+static void x11_chan_free(Channel *chan);
+static int x11_send(Channel *chan, int is_stderr, const void *vdata, int len);
+static void x11_send_eof(Channel *chan);
+static void x11_set_input_wanted(Channel *chan, int wanted);
+static char *x11_log_close_msg(Channel *chan);
+
+static const struct ChannelVtable X11Connection_channelvt = {
+ x11_chan_free,
+ chan_remotely_opened_confirmation,
+ chan_remotely_opened_failure,
+ x11_send,
+ x11_send_eof,
+ x11_set_input_wanted,
+ x11_log_close_msg,
+ chan_no_eager_close,
+};
+
/*
* Called to set up the X11Connection structure, though this does not
* yet connect to an actual server.
*/
-struct X11Connection *x11_init(tree234 *authtree, void *c,
- const char *peeraddr, int peerport)
+Channel *x11_new_channel(tree234 *authtree, struct ssh_channel *c,
+ const char *peeraddr, int peerport,
+ int connection_sharing_possible)
{
struct X11Connection *xconn;
@@ -721,11 +738,14 @@ struct X11Connection *x11_init(tree234 *authtree, void *c,
*/
xconn = snew(struct X11Connection);
xconn->plugvt = &X11Connection_plugvt;
+ xconn->chan.vt = &X11Connection_channelvt;
+ xconn->chan.initial_fixed_window_size =
+ (connection_sharing_possible ? 128 : 0);
xconn->auth_protocol = NULL;
xconn->authtree = authtree;
xconn->verified = 0;
xconn->data_read = 0;
- xconn->throttled = xconn->throttle_override = 0;
+ xconn->input_wanted = TRUE;
xconn->no_data_sent_to_x_client = TRUE;
xconn->c = c;
@@ -746,13 +766,13 @@ struct X11Connection *x11_init(tree234 *authtree, void *c,
xconn->peer_addr = peeraddr ? dupstr(peeraddr) : NULL;
xconn->peer_port = peerport;
- return xconn;
+ return &xconn->chan;
}
-void x11_close(struct X11Connection *xconn)
+static void x11_chan_free(Channel *chan)
{
- if (!xconn)
- return;
+ assert(chan->vt == &X11Connection_channelvt);
+ X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
if (xconn->auth_protocol) {
sfree(xconn->auth_protocol);
@@ -766,24 +786,14 @@ void x11_close(struct X11Connection *xconn)
sfree(xconn);
}
-void x11_unthrottle(struct X11Connection *xconn)
+static void x11_set_input_wanted(Channel *chan, int wanted)
{
- if (!xconn)
- return;
+ assert(chan->vt == &X11Connection_channelvt);
+ X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
- xconn->throttled = 0;
+ xconn->input_wanted = wanted;
if (xconn->s)
- sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override);
-}
-
-void x11_override_throttle(struct X11Connection *xconn, int enable)
-{
- if (!xconn)
- return;
-
- xconn->throttle_override = enable;
- if (xconn->s)
- sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override);
+ sk_set_frozen(xconn->s, !xconn->input_wanted);
}
static void x11_send_init_error(struct X11Connection *xconn,
@@ -831,13 +841,12 @@ static int x11_parse_ip(const char *addr_string, unsigned long *ip)
/*
* Called to send data down the raw connection.
*/
-int x11_send(struct X11Connection *xconn, const void *vdata, int len)
+static int x11_send(Channel *chan, int is_stderr, const void *vdata, int len)
{
+ assert(chan->vt == &X11Connection_channelvt);
+ X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
const char *data = (const char *)vdata;
- if (!xconn)
- return 0;
-
/*
* Read the first packet.
*/
@@ -914,7 +923,8 @@ int x11_send(struct X11Connection *xconn, const void *vdata, int len)
/*
* If this auth points to a connection-sharing downstream
* rather than an X display we know how to connect to
- * directly, pass it off to the sharing module now.
+ * directly, pass it off to the sharing module now. (This will
+ * have the side effect of freeing xconn.)
*/
if (auth_matched->share_cs) {
sshfwd_x11_sharing_handover(xconn->c, auth_matched->share_cs,
@@ -929,7 +939,8 @@ int x11_send(struct X11Connection *xconn, const void *vdata, int len)
* Now we know we're going to accept the connection, and what
* X display to connect to. Actually connect to it.
*/
- sshfwd_x11_is_local(xconn->c);
+ xconn->chan.initial_fixed_window_size = 0;
+ sshfwd_window_override_removed(xconn->c);
xconn->disp = auth_matched->disp;
xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),
xconn->disp->realhost, xconn->disp->port,
@@ -984,8 +995,11 @@ int x11_send(struct X11Connection *xconn, const void *vdata, int len)
return sk_write(xconn->s, data, len);
}
-void x11_send_eof(struct X11Connection *xconn)
+static void x11_send_eof(Channel *chan)
{
+ assert(chan->vt == &X11Connection_channelvt);
+ X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
+
if (xconn->s) {
sk_write_eof(xconn->s);
} else {
@@ -1000,6 +1014,11 @@ void x11_send_eof(struct X11Connection *xconn)
}
}
+static char *x11_log_close_msg(Channel *chan)
+{
+ return dupstr("Forwarded X11 connection terminated");
+}
+
/*
* Utility functions used by connection sharing to convert textual
* representations of an X11 auth protocol name + hex cookie into our
From 08b43c0ccaa7a80515a0829a09a247aa5b3c0634 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 13 Sep 2018 09:09:10 +0100
Subject: [PATCH 395/607] Expose structure tags for the connection-sharing data
types.
This was a particularly confusing piece of type-danger, because three
different types were passed outside sshshare.c as 'void *' and only
human vigilance prevented one coming back as the wrong one. Now they
all keep their opaque structure tags when they move through other
parts of the code.
---
defs.h | 4 ++++
ssh.c | 27 +++++++++++++++------------
ssh.h | 34 +++++++++++++++++++---------------
sshshare.c | 32 +++++++++++++-------------------
unix/uxpgnt.c | 3 ++-
x11fwd.c | 3 ++-
6 files changed, 55 insertions(+), 48 deletions(-)
diff --git a/defs.h b/defs.h
index 2c597953..329883d9 100644
--- a/defs.h
+++ b/defs.h
@@ -55,6 +55,10 @@ typedef struct ssh_tag *Ssh;
typedef struct Channel Channel;
+typedef struct ssh_sharing_state ssh_sharing_state;
+typedef struct ssh_sharing_connstate ssh_sharing_connstate;
+typedef struct share_channel share_channel;
+
/* Note indirection: for historical reasons (it used to be closer to
* the OS socket type), the type that most code uses for a socket is
* 'Socket', not 'Socket *'. So an implementation of Socket or Plug
diff --git a/ssh.c b/ssh.c
index bc2f7bf8..dd1d1c1f 100644
--- a/ssh.c
+++ b/ssh.c
@@ -460,7 +460,8 @@ struct ssh_channel {
} v2;
} v;
- void *sharectx; /* sharing context, if this is a downstream channel */
+ ssh_sharing_connstate *sharectx; /* sharing context, if this is a
+ * downstream channel */
Channel *chan; /* handle the client side of this channel, if not */
};
@@ -497,7 +498,7 @@ struct ssh_rportfwd {
unsigned sport, dport;
char *shost, *dhost;
char *sportdesc;
- void *share_ctx;
+ ssh_sharing_connstate *share_ctx;
struct ssh_portfwd *pfrec;
};
@@ -695,7 +696,7 @@ struct ssh_tag {
int bare_connection;
int attempting_connshare;
- void *connshare;
+ ssh_sharing_state *connshare;
char *savedhost;
int savedport;
@@ -3800,7 +3801,7 @@ static void ssh_rportfwd_succfail(Ssh ssh, PktIn *pktin, void *ctx)
}
int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
- void *share_ctx)
+ ssh_sharing_connstate *share_ctx)
{
struct ssh_rportfwd *pf = snew(struct ssh_rportfwd);
pf->dhost = NULL;
@@ -3822,7 +3823,7 @@ int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
}
void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
- void *share_ctx)
+ ssh_sharing_connstate *share_ctx)
{
struct ssh_rportfwd pf, *realpf;
@@ -3844,7 +3845,8 @@ static void ssh_sharing_global_request_response(Ssh ssh, PktIn *pktin,
BinarySource_UPCAST(pktin)->len);
}
-void ssh_sharing_queue_global_request(Ssh ssh, void *share_ctx)
+void ssh_sharing_queue_global_request(Ssh ssh,
+ ssh_sharing_connstate *share_ctx)
{
ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS, SSH2_MSG_REQUEST_FAILURE,
ssh_sharing_global_request_response, share_ctx);
@@ -7735,9 +7737,9 @@ static void ssh2_msg_global_request(Ssh ssh, PktIn *pktin)
}
}
-struct X11FakeAuth *ssh_sharing_add_x11_display(Ssh ssh, int authtype,
- void *share_cs,
- void *share_chan)
+struct X11FakeAuth *ssh_sharing_add_x11_display(
+ Ssh ssh, int authtype, ssh_sharing_connstate *share_cs,
+ share_channel *share_chan)
{
struct X11FakeAuth *auth;
@@ -7876,7 +7878,8 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
}
void sshfwd_x11_sharing_handover(struct ssh_channel *c,
- void *share_cs, void *share_chan,
+ ssh_sharing_connstate *share_cs,
+ share_channel *share_chan,
const char *peer_addr, int peer_port,
int endian, int protomajor, int protominor,
const void *initial_data, int initial_len)
@@ -11173,7 +11176,7 @@ static void ssh_special(Backend *be, Telnet_Special code)
}
}
-unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx)
+unsigned ssh_alloc_sharing_channel(Ssh ssh, ssh_sharing_connstate *connstate)
{
struct ssh_channel *c;
c = snew(struct ssh_channel);
@@ -11181,7 +11184,7 @@ unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx)
c->ssh = ssh;
ssh_channel_init(c);
c->chan = NULL;
- c->sharectx = sharing_ctx;
+ c->sharectx = connstate;
return c->localid;
}
diff --git a/ssh.h b/ssh.h
index 82810d78..4fee36bf 100644
--- a/ssh.h
+++ b/ssh.h
@@ -16,7 +16,8 @@ extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize);
Conf *sshfwd_get_conf(struct ssh_channel *c);
void sshfwd_window_override_removed(struct ssh_channel *c);
void sshfwd_x11_sharing_handover(struct ssh_channel *c,
- void *share_cs, void *share_chan,
+ ssh_sharing_connstate *share_cs,
+ share_channel *share_chan,
const char *peer_addr, int peer_port,
int endian, int protomajor, int protominor,
const void *initial_data, int initial_len);
@@ -148,26 +149,28 @@ void ssh_free_pktout(PktOut *pkt);
extern Socket ssh_connection_sharing_init(
const char *host, int port, Conf *conf, Ssh ssh, Plug sshplug,
- void **state);
+ ssh_sharing_state **state);
int ssh_share_test_for_upstream(const char *host, int port, Conf *conf);
-void share_got_pkt_from_server(void *ctx, int type,
+void share_got_pkt_from_server(ssh_sharing_connstate *ctx, int type,
const void *pkt, int pktlen);
-void share_activate(void *state, const char *server_verstring);
-void sharestate_free(void *state);
-int share_ndownstreams(void *state);
+void share_activate(ssh_sharing_state *sharestate,
+ const char *server_verstring);
+void sharestate_free(ssh_sharing_state *state);
+int share_ndownstreams(ssh_sharing_state *state);
void ssh_connshare_log(Ssh ssh, int event, const char *logtext,
const char *ds_err, const char *us_err);
-unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx);
+unsigned ssh_alloc_sharing_channel(Ssh ssh, ssh_sharing_connstate *connstate);
void ssh_delete_sharing_channel(Ssh ssh, unsigned localid);
int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
- void *share_ctx);
+ ssh_sharing_connstate *connstate);
void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
- void *share_ctx);
-void ssh_sharing_queue_global_request(Ssh ssh, void *share_ctx);
-struct X11FakeAuth *ssh_sharing_add_x11_display(Ssh ssh, int authtype,
- void *share_cs,
- void *share_chan);
+ ssh_sharing_connstate *connstate);
+void ssh_sharing_queue_global_request(
+ Ssh ssh, ssh_sharing_connstate *connstate);
+struct X11FakeAuth *ssh_sharing_add_x11_display(
+ Ssh ssh, int authtype, ssh_sharing_connstate *share_cs,
+ share_channel *share_chan);
void ssh_sharing_remove_x11_display(Ssh ssh, struct X11FakeAuth *auth);
void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type,
const void *pkt, int pktlen,
@@ -177,7 +180,7 @@ void ssh_sharing_downstream_connected(Ssh ssh, unsigned id,
void ssh_sharing_downstream_disconnected(Ssh ssh, unsigned id);
void ssh_sharing_logf(Ssh ssh, unsigned id, const char *logfmt, ...);
int ssh_agent_forwarding_permitted(Ssh ssh);
-void share_setup_x11_channel(void *csv, void *chanv,
+void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
unsigned upstream_id, unsigned server_id,
unsigned server_currwin, unsigned server_maxpkt,
unsigned client_adjusted_window,
@@ -727,7 +730,8 @@ struct X11FakeAuth {
* What to do with an X connection matching this auth data.
*/
struct X11Display *disp;
- void *share_cs, *share_chan;
+ ssh_sharing_connstate *share_cs;
+ share_channel *share_chan;
};
void *x11_make_greeting(int endian, int protomajor, int protominor,
int auth_proto, const void *auth_data, int auth_len,
diff --git a/sshshare.c b/sshshare.c
index 30fe7360..22611e7d 100644
--- a/sshshare.c
+++ b/sshshare.c
@@ -507,9 +507,8 @@ static void share_connstate_free(struct ssh_sharing_connstate *cs)
sfree(cs);
}
-void sharestate_free(void *v)
+void sharestate_free(ssh_sharing_state *sharestate)
{
- struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)v;
struct ssh_sharing_connstate *cs;
platform_ssh_share_cleanup(sharestate->sockname);
@@ -1061,7 +1060,7 @@ void share_xchannel_failure(struct ssh_sharing_connstate *cs,
share_dead_xchannel_respond(cs, xc);
}
-void share_setup_x11_channel(void *csv, void *chanv,
+void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
unsigned upstream_id, unsigned server_id,
unsigned server_currwin, unsigned server_maxpkt,
unsigned client_adjusted_window,
@@ -1069,8 +1068,6 @@ void share_setup_x11_channel(void *csv, void *chanv,
int protomajor, int protominor,
const void *initial_data, int initial_len)
{
- struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)csv;
- struct share_channel *chan = (struct share_channel *)chanv;
struct share_xchannel *xc;
void *greeting;
int greeting_len;
@@ -1128,11 +1125,10 @@ void share_setup_x11_channel(void *csv, void *chanv,
}
}
-void share_got_pkt_from_server(void *csv, int type,
+void share_got_pkt_from_server(ssh_sharing_connstate *cs, int type,
const void *vpkt, int pktlen)
{
const unsigned char *pkt = (const unsigned char *)vpkt;
- struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)csv;
struct share_globreq *globreq;
size_t id_pos;
unsigned upstream_id, server_id;
@@ -1718,8 +1714,8 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
static void share_receive(Plug plug, int urgent, char *data, int len)
{
- struct ssh_sharing_connstate *cs = FROMFIELD(
- plug, struct ssh_sharing_connstate, plugvt);
+ ssh_sharing_connstate *cs = FROMFIELD(
+ plug, ssh_sharing_connstate, plugvt);
static const char expected_verstring_prefix[] =
"SSHCONNECTION@putty.projects.tartarus.org-2.0-";
unsigned char c;
@@ -1795,8 +1791,8 @@ static void share_receive(Plug plug, int urgent, char *data, int len)
static void share_sent(Plug plug, int bufsize)
{
- /* struct ssh_sharing_connstate *cs = FROMFIELD(
- plug, struct ssh_sharing_connstate, plugvt); */
+ /* ssh_sharing_connstate *cs = FROMFIELD(
+ plug, ssh_sharing_connstate, plugvt); */
/*
* We do nothing here, because we expect that there won't be a
@@ -1811,8 +1807,7 @@ static void share_sent(Plug plug, int bufsize)
static void share_listen_closing(Plug plug, const char *error_msg,
int error_code, int calling_back)
{
- struct ssh_sharing_state *sharestate = FROMFIELD(
- plug, struct ssh_sharing_state, plugvt);
+ ssh_sharing_state *sharestate = FROMFIELD(plug, ssh_sharing_state, plugvt);
if (error_msg)
ssh_sharing_logf(sharestate->ssh, 0,
"listening socket: %s", error_msg);
@@ -1820,7 +1815,7 @@ static void share_listen_closing(Plug plug, const char *error_msg,
sharestate->listensock = NULL;
}
-static void share_send_verstring(struct ssh_sharing_connstate *cs)
+static void share_send_verstring(ssh_sharing_connstate *cs)
{
char *fullstring = dupcat("SSHCONNECTION@putty.projects.tartarus.org-2.0-",
cs->parent->server_verstring, "\015\012", NULL);
@@ -1830,19 +1825,18 @@ static void share_send_verstring(struct ssh_sharing_connstate *cs)
cs->sent_verstring = TRUE;
}
-int share_ndownstreams(void *state)
+int share_ndownstreams(ssh_sharing_state *sharestate)
{
- struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)state;
return count234(sharestate->connections);
}
-void share_activate(void *state, const char *server_verstring)
+void share_activate(ssh_sharing_state *sharestate,
+ const char *server_verstring)
{
/*
* Indication from ssh.c that we are now ready to begin serving
* any downstreams that have already connected to us.
*/
- struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)state;
struct ssh_sharing_connstate *cs;
int i;
@@ -2028,7 +2022,7 @@ static const Plug_vtable ssh_sharing_listen_plugvt = {
*/
Socket ssh_connection_sharing_init(const char *host, int port,
Conf *conf, Ssh ssh, Plug sshplug,
- void **state)
+ ssh_sharing_state **state)
{
int result, can_upstream, can_downstream;
char *logtext, *ds_err, *us_err;
diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
index a694bc44..efd93726 100644
--- a/unix/uxpgnt.c
+++ b/unix/uxpgnt.c
@@ -168,7 +168,8 @@ int chan_no_eager_close(Channel *chan, int s, int r) { return FALSE; }
Conf *sshfwd_get_conf(struct ssh_channel *c) { return NULL; }
void sshfwd_x11_sharing_handover(struct ssh_channel *c,
- void *share_cs, void *share_chan,
+ ssh_sharing_connstate *share_cs,
+ share_channel *share_chan,
const char *peer_addr, int peer_port,
int endian, int protomajor, int protominor,
const void *initial_data, int initial_len) {}
diff --git a/x11fwd.c b/x11fwd.c
index 419d4968..f3ea5397 100644
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -128,7 +128,8 @@ struct X11FakeAuth *x11_invent_fake_auth(tree234 *authtree, int authtype)
auth->data[i]);
auth->disp = NULL;
- auth->share_cs = auth->share_chan = NULL;
+ auth->share_cs = NULL;
+ auth->share_chan = NULL;
return auth;
}
From fc375c0b6ad8a7a088e5f3144fe32fbf74232ec3 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 13 Sep 2018 12:33:26 +0100
Subject: [PATCH 396/607] Remove some redundant utility macros.
ATOFFSET in dialog.h became obsolete when the old 'struct Config' gave
way to the new Conf, because its only use was to identify fields in
struct Config for the generic control handlers to update.
And lenof in ssh.h is redundant because there's also a copy in misc.h.
(Which is already included _everywhere_ that lenof is used - I didn't
even need to add any instances of #include "misc.h" after removing the
copy in ssh.h.)
---
dialog.h | 6 ------
ssh.h | 7 -------
2 files changed, 13 deletions(-)
diff --git a/dialog.h b/dialog.h
index 9ac355c6..68f7dff1 100644
--- a/dialog.h
+++ b/dialog.h
@@ -2,12 +2,6 @@
* Exports and types from dialog.c.
*/
-/*
- * This will come in handy for generic control handlers. Anyone
- * knows how to make this more portable, let me know :-)
- */
-#define ATOFFSET(data, offset) ( (void *) ( (char *)(data) + (offset) ) )
-
/*
* This is the big union which defines a single control, of any
* type.
diff --git a/ssh.h b/ssh.h
index 4fee36bf..2d285d23 100644
--- a/ssh.h
+++ b/ssh.h
@@ -190,13 +190,6 @@ void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
Frontend *ssh_get_frontend(Ssh ssh);
-/*
- * Useful thing.
- */
-#ifndef lenof
-#define lenof(x) ( (sizeof((x))) / (sizeof(*(x))))
-#endif
-
#define SSH_CIPHER_IDEA 1
#define SSH_CIPHER_DES 2
#define SSH_CIPHER_3DES 3
From 3aae1f9d762497cd182f6c7e45981560e0694f9f Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 13 Sep 2018 12:58:44 +0100
Subject: [PATCH 397/607] Expose the structure tag 'dlgparam'.
This continues my ongoing crusade against dangerous 'void *'
parameters.
---
config.c | 52 ++++++++++-----------
defs.h | 2 +
dialog.h | 62 ++++++++++++-------------
putty.h | 10 ++--
sercfg.c | 6 +--
unix/gtkcfg.c | 2 +-
unix/gtkdlg.c | 112 +++++++++++++++++----------------------------
windows/wincfg.c | 6 +--
windows/winctrls.c | 99 +++++++++++++--------------------------
windows/winstuff.h | 6 +--
10 files changed, 150 insertions(+), 207 deletions(-)
diff --git a/config.c b/config.c
index fac7dc3d..bdffd6bb 100644
--- a/config.c
+++ b/config.c
@@ -15,7 +15,7 @@
#define HOST_BOX_TITLE "Host Name (or IP address)"
#define PORT_BOX_TITLE "Port"
-void conf_radiobutton_handler(union control *ctrl, void *dlg,
+void conf_radiobutton_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
int button;
@@ -44,7 +44,7 @@ void conf_radiobutton_handler(union control *ctrl, void *dlg,
}
#define CHECKBOX_INVERT (1<<30)
-void conf_checkbox_handler(union control *ctrl, void *dlg,
+void conf_checkbox_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
int key, invert;
@@ -75,7 +75,7 @@ void conf_checkbox_handler(union control *ctrl, void *dlg,
}
}
-void conf_editbox_handler(union control *ctrl, void *dlg,
+void conf_editbox_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
/*
@@ -124,7 +124,7 @@ void conf_editbox_handler(union control *ctrl, void *dlg,
}
}
-void conf_filesel_handler(union control *ctrl, void *dlg,
+void conf_filesel_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
int key = ctrl->fileselect.context.i;
@@ -139,7 +139,7 @@ void conf_filesel_handler(union control *ctrl, void *dlg,
}
}
-void conf_fontsel_handler(union control *ctrl, void *dlg,
+void conf_fontsel_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
int key = ctrl->fontselect.context.i;
@@ -154,7 +154,7 @@ void conf_fontsel_handler(union control *ctrl, void *dlg,
}
}
-static void config_host_handler(union control *ctrl, void *dlg,
+static void config_host_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -186,7 +186,7 @@ static void config_host_handler(union control *ctrl, void *dlg,
}
}
-static void config_port_handler(union control *ctrl, void *dlg,
+static void config_port_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -235,7 +235,7 @@ struct hostport {
* routines can use it to conveniently identify the protocol radio
* buttons in order to add to them.
*/
-void config_protocolbuttons_handler(union control *ctrl, void *dlg,
+void config_protocolbuttons_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
int button;
@@ -289,7 +289,7 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg,
}
}
-static void loggingbuttons_handler(union control *ctrl, void *dlg,
+static void loggingbuttons_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
int button;
@@ -318,7 +318,7 @@ static void loggingbuttons_handler(union control *ctrl, void *dlg,
}
}
-static void numeric_keypad_handler(union control *ctrl, void *dlg,
+static void numeric_keypad_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
int button;
@@ -349,7 +349,7 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg,
}
}
-static void cipherlist_handler(union control *ctrl, void *dlg,
+static void cipherlist_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -395,7 +395,7 @@ static void cipherlist_handler(union control *ctrl, void *dlg,
}
#ifndef NO_GSSAPI
-static void gsslist_handler(union control *ctrl, void *dlg,
+static void gsslist_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -422,7 +422,7 @@ static void gsslist_handler(union control *ctrl, void *dlg,
}
#endif
-static void kexlist_handler(union control *ctrl, void *dlg,
+static void kexlist_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -466,7 +466,7 @@ static void kexlist_handler(union control *ctrl, void *dlg,
}
}
-static void hklist_handler(union control *ctrl, void *dlg,
+static void hklist_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -509,7 +509,7 @@ static void hklist_handler(union control *ctrl, void *dlg,
}
}
-static void printerbox_handler(union control *ctrl, void *dlg,
+static void printerbox_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -545,7 +545,7 @@ static void printerbox_handler(union control *ctrl, void *dlg,
}
}
-static void codepage_handler(union control *ctrl, void *dlg,
+static void codepage_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -569,7 +569,7 @@ static void codepage_handler(union control *ctrl, void *dlg,
}
}
-static void sshbug_handler(union control *ctrl, void *dlg,
+static void sshbug_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -624,7 +624,7 @@ static void sessionsaver_data_free(void *ssdv)
* failure.
*/
static int load_selected_session(struct sessionsaver_data *ssd,
- void *dlg, Conf *conf, int *maybe_launch)
+ dlgparam *dlg, Conf *conf, int *maybe_launch)
{
int i = dlg_listbox_index(ssd->listbox, dlg);
int isdef;
@@ -645,7 +645,7 @@ static int load_selected_session(struct sessionsaver_data *ssd,
return 1;
}
-static void sessionsaver_handler(union control *ctrl, void *dlg,
+static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -786,7 +786,7 @@ struct charclass_data {
union control *listbox, *editbox, *button;
};
-static void charclass_handler(union control *ctrl, void *dlg,
+static void charclass_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -841,7 +841,7 @@ static const char *const colours[] = {
"ANSI White", "ANSI White Bold"
};
-static void colour_handler(union control *ctrl, void *dlg,
+static void colour_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -949,7 +949,7 @@ struct ttymodes_data {
union control *valradio, *valbox, *setbutton, *listbox;
};
-static void ttymodes_handler(union control *ctrl, void *dlg,
+static void ttymodes_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -1034,7 +1034,7 @@ struct environ_data {
union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
};
-static void environ_handler(union control *ctrl, void *dlg,
+static void environ_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -1110,7 +1110,7 @@ struct portfwd_data {
#endif
};
-static void portfwd_handler(union control *ctrl, void *dlg,
+static void portfwd_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -1274,7 +1274,7 @@ struct manual_hostkey_data {
union control *addbutton, *rembutton, *listbox, *keybox;
};
-static void manual_hostkey_handler(union control *ctrl, void *dlg,
+static void manual_hostkey_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
@@ -1337,7 +1337,7 @@ static void manual_hostkey_handler(union control *ctrl, void *dlg,
}
}
-static void clipboard_selector_handler(union control *ctrl, void *dlg,
+static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
diff --git a/defs.h b/defs.h
index 329883d9..1bc8f9b8 100644
--- a/defs.h
+++ b/defs.h
@@ -59,6 +59,8 @@ typedef struct ssh_sharing_state ssh_sharing_state;
typedef struct ssh_sharing_connstate ssh_sharing_connstate;
typedef struct share_channel share_channel;
+typedef struct dlgparam dlgparam;
+
/* Note indirection: for historical reasons (it used to be closer to
* the OS socket type), the type that most code uses for a socket is
* 'Socket', not 'Socket *'. So an implementation of Socket or Plug
diff --git a/dialog.h b/dialog.h
index 68f7dff1..4b708489 100644
--- a/dialog.h
+++ b/dialog.h
@@ -103,7 +103,7 @@ enum {
EVENT_SELCHANGE,
EVENT_CALLBACK
};
-typedef void (*handler_fn)(union control *ctrl, void *dlg,
+typedef void (*handler_fn)(union control *ctrl, dlgparam *dp,
void *data, int event);
#define STANDARD_PREFIX \
@@ -534,16 +534,16 @@ union control *ctrl_tabdelay(struct controlset *, union control *);
* Routines the platform-independent dialog code can call to read
* and write the values of controls.
*/
-void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton);
-int dlg_radiobutton_get(union control *ctrl, void *dlg);
-void dlg_checkbox_set(union control *ctrl, void *dlg, int checked);
-int dlg_checkbox_get(union control *ctrl, void *dlg);
-void dlg_editbox_set(union control *ctrl, void *dlg, char const *text);
-char *dlg_editbox_get(union control *ctrl, void *dlg); /* result must be freed by caller */
+void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int whichbutton);
+int dlg_radiobutton_get(union control *ctrl, dlgparam *dp);
+void dlg_checkbox_set(union control *ctrl, dlgparam *dp, int checked);
+int dlg_checkbox_get(union control *ctrl, dlgparam *dp);
+void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text);
+char *dlg_editbox_get(union control *ctrl, dlgparam *dp); /* result must be freed by caller */
/* The `listbox' functions can also apply to combo boxes. */
-void dlg_listbox_clear(union control *ctrl, void *dlg);
-void dlg_listbox_del(union control *ctrl, void *dlg, int index);
-void dlg_listbox_add(union control *ctrl, void *dlg, char const *text);
+void dlg_listbox_clear(union control *ctrl, dlgparam *dp);
+void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index);
+void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text);
/*
* Each listbox entry may have a numeric id associated with it.
* Note that some front ends only permit a string to be stored at
@@ -551,53 +551,53 @@ void dlg_listbox_add(union control *ctrl, void *dlg, char const *text);
* strings in any listbox then you MUST not assign them different
* IDs and expect to get meaningful results back.
*/
-void dlg_listbox_addwithid(union control *ctrl, void *dlg,
+void dlg_listbox_addwithid(union control *ctrl, dlgparam *dp,
char const *text, int id);
-int dlg_listbox_getid(union control *ctrl, void *dlg, int index);
+int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index);
/* dlg_listbox_index returns <0 if no single element is selected. */
-int dlg_listbox_index(union control *ctrl, void *dlg);
-int dlg_listbox_issel(union control *ctrl, void *dlg, int index);
-void dlg_listbox_select(union control *ctrl, void *dlg, int index);
-void dlg_text_set(union control *ctrl, void *dlg, char const *text);
-void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn);
-Filename *dlg_filesel_get(union control *ctrl, void *dlg);
-void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fn);
-FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg);
+int dlg_listbox_index(union control *ctrl, dlgparam *dp);
+int dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index);
+void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index);
+void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text);
+void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn);
+Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp);
+void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fn);
+FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp);
/*
* Bracketing a large set of updates in these two functions will
* cause the front end (if possible) to delay updating the screen
* until it's all complete, thus avoiding flicker.
*/
-void dlg_update_start(union control *ctrl, void *dlg);
-void dlg_update_done(union control *ctrl, void *dlg);
+void dlg_update_start(union control *ctrl, dlgparam *dp);
+void dlg_update_done(union control *ctrl, dlgparam *dp);
/*
* Set input focus into a particular control.
*/
-void dlg_set_focus(union control *ctrl, void *dlg);
+void dlg_set_focus(union control *ctrl, dlgparam *dp);
/*
* Change the label text on a control.
*/
-void dlg_label_change(union control *ctrl, void *dlg, char const *text);
+void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text);
/*
* Return the `ctrl' structure for the most recent control that had
* the input focus apart from the one mentioned. This is NOT
* GUARANTEED to work on all platforms, so don't base any critical
* functionality on it!
*/
-union control *dlg_last_focused(union control *ctrl, void *dlg);
+union control *dlg_last_focused(union control *ctrl, dlgparam *dp);
/*
* During event processing, you might well want to give an error
* indication to the user. dlg_beep() is a quick and easy generic
* error; dlg_error() puts up a message-box or equivalent.
*/
-void dlg_beep(void *dlg);
-void dlg_error_msg(void *dlg, const char *msg);
+void dlg_beep(dlgparam *dp);
+void dlg_error_msg(dlgparam *dp, const char *msg);
/*
* This function signals to the front end that the dialog's
* processing is completed, and passes an integer value (typically
* a success status).
*/
-void dlg_end(void *dlg, int value);
+void dlg_end(dlgparam *dp, int value);
/*
* Routines to manage a (per-platform) colour selector.
@@ -612,9 +612,9 @@ void dlg_end(void *dlg, int value);
* dlg_coloursel_start() accepts an RGB triple which is used to
* initialise the colour selector to its starting value.
*/
-void dlg_coloursel_start(union control *ctrl, void *dlg,
+void dlg_coloursel_start(union control *ctrl, dlgparam *dp,
int r, int g, int b);
-int dlg_coloursel_results(union control *ctrl, void *dlg,
+int dlg_coloursel_results(union control *ctrl, dlgparam *dp,
int *r, int *g, int *b);
/*
@@ -626,7 +626,7 @@ int dlg_coloursel_results(union control *ctrl, void *dlg,
* If `ctrl' is NULL, _all_ controls in the dialog get refreshed
* (for loading or saving entire sets of settings).
*/
-void dlg_refresh(union control *ctrl, void *dlg);
+void dlg_refresh(union control *ctrl, dlgparam *dp);
/*
* Standard helper functions for reading a controlbox structure.
diff --git a/putty.h b/putty.h
index 1a8c30ce..7b1f90e4 100644
--- a/putty.h
+++ b/putty.h
@@ -1421,16 +1421,16 @@ void cmdline_error(const char *, ...);
*/
struct controlbox;
union control;
-void conf_radiobutton_handler(union control *ctrl, void *dlg,
+void conf_radiobutton_handler(union control *ctrl, dlgparam *dlg,
void *data, int event);
#define CHECKBOX_INVERT (1<<30)
-void conf_checkbox_handler(union control *ctrl, void *dlg,
+void conf_checkbox_handler(union control *ctrl, dlgparam *dlg,
void *data, int event);
-void conf_editbox_handler(union control *ctrl, void *dlg,
+void conf_editbox_handler(union control *ctrl, dlgparam *dlg,
void *data, int event);
-void conf_filesel_handler(union control *ctrl, void *dlg,
+void conf_filesel_handler(union control *ctrl, dlgparam *dlg,
void *data, int event);
-void conf_fontsel_handler(union control *ctrl, void *dlg,
+void conf_fontsel_handler(union control *ctrl, dlgparam *dlg,
void *data, int event);
void setup_config_box(struct controlbox *b, int midsession,
int protocol, int protcfginfo);
diff --git a/sercfg.c b/sercfg.c
index fef910f3..71462100 100644
--- a/sercfg.c
+++ b/sercfg.c
@@ -16,7 +16,7 @@
#include "dialog.h"
#include "storage.h"
-static void serial_parity_handler(union control *ctrl, void *dlg,
+static void serial_parity_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
static const struct {
@@ -71,7 +71,7 @@ static void serial_parity_handler(union control *ctrl, void *dlg,
}
}
-static void serial_flow_handler(union control *ctrl, void *dlg,
+static void serial_flow_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
static const struct {
@@ -132,7 +132,7 @@ void ser_setup_config_box(struct controlbox *b, int midsession,
if (!midsession) {
int i;
- extern void config_protocolbuttons_handler(union control *, void *,
+ extern void config_protocolbuttons_handler(union control *, dlgparam *,
void *, int);
/*
diff --git a/unix/gtkcfg.c b/unix/gtkcfg.c
index 4307176f..bbde8d2a 100644
--- a/unix/gtkcfg.c
+++ b/unix/gtkcfg.c
@@ -10,7 +10,7 @@
#include "dialog.h"
#include "storage.h"
-static void about_handler(union control *ctrl, void *dlg,
+static void about_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
if (event == EVENT_ACTION) {
diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c
index f1425daf..6ffb5b5c 100644
--- a/unix/gtkdlg.c
+++ b/unix/gtkdlg.c
@@ -252,27 +252,24 @@ static struct uctrl *dlg_find_bywidget(struct dlgparam *dp, GtkWidget *w)
return ret;
}
-union control *dlg_last_focused(union control *ctrl, void *dlg)
+union control *dlg_last_focused(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
if (dp->currfocus != ctrl)
return dp->currfocus;
else
return dp->lastfocus;
}
-void dlg_radiobutton_set(union control *ctrl, void *dlg, int which)
+void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int which)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_RADIO);
assert(uc->buttons != NULL);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->buttons[which]), TRUE);
}
-int dlg_radiobutton_get(union control *ctrl, void *dlg)
+int dlg_radiobutton_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
int i;
@@ -284,25 +281,22 @@ int dlg_radiobutton_get(union control *ctrl, void *dlg)
return 0; /* got to return something */
}
-void dlg_checkbox_set(union control *ctrl, void *dlg, int checked)
+void dlg_checkbox_set(union control *ctrl, dlgparam *dp, int checked)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->toplevel), checked);
}
-int dlg_checkbox_get(union control *ctrl, void *dlg)
+int dlg_checkbox_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->toplevel));
}
-void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
+void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
GtkWidget *entry;
char *tmpstring;
@@ -339,9 +333,8 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
sfree(tmpstring);
}
-char *dlg_editbox_get(union control *ctrl, void *dlg)
+char *dlg_editbox_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX);
@@ -369,9 +362,8 @@ static void container_remove_and_destroy(GtkWidget *w, gpointer data)
#endif
/* The `listbox' functions can also apply to combo boxes. */
-void dlg_listbox_clear(union control *ctrl, void *dlg)
+void dlg_listbox_clear(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
@@ -398,9 +390,8 @@ void dlg_listbox_clear(union control *ctrl, void *dlg)
assert(!"We shouldn't get here");
}
-void dlg_listbox_del(union control *ctrl, void *dlg, int index)
+void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
@@ -433,9 +424,9 @@ void dlg_listbox_del(union control *ctrl, void *dlg, int index)
assert(!"We shouldn't get here");
}
-void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
+void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text)
{
- dlg_listbox_addwithid(ctrl, dlg, text, 0);
+ dlg_listbox_addwithid(ctrl, dp, text, 0);
}
/*
@@ -445,10 +436,9 @@ void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
* strings in any listbox then you MUST not assign them different
* IDs and expect to get meaningful results back.
*/
-void dlg_listbox_addwithid(union control *ctrl, void *dlg,
+void dlg_listbox_addwithid(union control *ctrl, dlgparam *dp,
char const *text, int id)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
@@ -594,9 +584,8 @@ void dlg_listbox_addwithid(union control *ctrl, void *dlg,
dp->flags &= ~FLAG_UPDATING_COMBO_LIST;
}
-int dlg_listbox_getid(union control *ctrl, void *dlg, int index)
+int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
@@ -634,9 +623,8 @@ int dlg_listbox_getid(union control *ctrl, void *dlg, int index)
}
/* dlg_listbox_index returns <0 if no single element is selected. */
-int dlg_listbox_index(union control *ctrl, void *dlg)
+int dlg_listbox_index(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
@@ -719,9 +707,8 @@ int dlg_listbox_index(union control *ctrl, void *dlg)
return -1; /* placate dataflow analysis */
}
-int dlg_listbox_issel(union control *ctrl, void *dlg, int index)
+int dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
@@ -777,9 +764,8 @@ int dlg_listbox_issel(union control *ctrl, void *dlg, int index)
return -1; /* placate dataflow analysis */
}
-void dlg_listbox_select(union control *ctrl, void *dlg, int index)
+void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
@@ -846,9 +832,8 @@ void dlg_listbox_select(union control *ctrl, void *dlg, int index)
assert(!"We shouldn't get here");
}
-void dlg_text_set(union control *ctrl, void *dlg, char const *text)
+void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_TEXT);
@@ -857,9 +842,8 @@ void dlg_text_set(union control *ctrl, void *dlg, char const *text)
gtk_label_set_text(GTK_LABEL(uc->text), text);
}
-void dlg_label_change(union control *ctrl, void *dlg, char const *text)
+void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
switch (uc->ctrl->generic.type) {
@@ -897,9 +881,8 @@ void dlg_label_change(union control *ctrl, void *dlg, char const *text)
}
}
-void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn)
+void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
/* We must copy fn->path before passing it to gtk_entry_set_text.
* See comment in dlg_editbox_set() for the reasons. */
@@ -910,18 +893,16 @@ void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn)
sfree(duppath);
}
-Filename *dlg_filesel_get(union control *ctrl, void *dlg)
+Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_FILESELECT);
assert(uc->entry != NULL);
return filename_from_str(gtk_entry_get_text(GTK_ENTRY(uc->entry)));
}
-void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs)
+void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fs)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
/* We must copy fs->name before passing it to gtk_entry_set_text.
* See comment in dlg_editbox_set() for the reasons. */
@@ -932,9 +913,8 @@ void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs)
sfree(dupname);
}
-FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg)
+FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_FONTSELECT);
assert(uc->entry != NULL);
@@ -946,7 +926,7 @@ FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg)
* cause the front end (if possible) to delay updating the screen
* until it's all complete, thus avoiding flicker.
*/
-void dlg_update_start(union control *ctrl, void *dlg)
+void dlg_update_start(union control *ctrl, dlgparam *dp)
{
/*
* Apparently we can't do this at all in GTK. GtkCList supports
@@ -954,7 +934,7 @@ void dlg_update_start(union control *ctrl, void *dlg)
*/
}
-void dlg_update_done(union control *ctrl, void *dlg)
+void dlg_update_done(union control *ctrl, dlgparam *dp)
{
/*
* Apparently we can't do this at all in GTK. GtkCList supports
@@ -962,9 +942,8 @@ void dlg_update_done(union control *ctrl, void *dlg)
*/
}
-void dlg_set_focus(union control *ctrl, void *dlg)
+void dlg_set_focus(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
switch (ctrl->generic.type) {
@@ -1039,7 +1018,7 @@ void dlg_set_focus(union control *ctrl, void *dlg)
* indication to the user. dlg_beep() is a quick and easy generic
* error; dlg_error() puts up a message-box or equivalent.
*/
-void dlg_beep(void *dlg)
+void dlg_beep(dlgparam *dp)
{
gdk_display_beep(gdk_display_get_default());
}
@@ -1079,9 +1058,8 @@ void trivial_post_dialog_fn(void *vctx, int result)
{
}
-void dlg_error_msg(void *dlg, const char *msg)
+void dlg_error_msg(dlgparam *dp, const char *msg)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
create_message_box(
dp->window, "Error", msg,
string_width("Some sort of text about a config-box error message"),
@@ -1093,16 +1071,14 @@ void dlg_error_msg(void *dlg, const char *msg)
* processing is completed, and passes an integer value (typically
* a success status).
*/
-void dlg_end(void *dlg, int value)
+void dlg_end(dlgparam *dp, int value)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
dp->retval = value;
gtk_widget_destroy(dp->window);
}
-void dlg_refresh(union control *ctrl, void *dlg)
+void dlg_refresh(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc;
if (ctrl) {
@@ -1120,9 +1096,8 @@ void dlg_refresh(union control *ctrl, void *dlg)
}
}
-void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
+void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
#if GTK_CHECK_VERSION(3,0,0)
@@ -1206,10 +1181,9 @@ void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
gtk_widget_show(coloursel);
}
-int dlg_coloursel_results(union control *ctrl, void *dlg,
+int dlg_coloursel_results(union control *ctrl, dlgparam *dp,
int *r, int *g, int *b)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
if (dp->coloursel_result.ok) {
*r = dp->coloursel_result.r;
*g = dp->coloursel_result.g;
@@ -3283,11 +3257,11 @@ static void dlgparam_destroy(GtkWidget *widget, gpointer data)
sfree(dp);
}
-static void messagebox_handler(union control *ctrl, void *dlg,
+static void messagebox_handler(union control *ctrl, dlgparam *dp,
void *data, int event)
{
if (event == EVENT_ACTION)
- dlg_end(dlg, ctrl->generic.context.i);
+ dlg_end(dp, ctrl->generic.context.i);
}
const struct message_box_button button_array_yn[] = {
@@ -3806,13 +3780,13 @@ static void eventlog_destroy(GtkWidget *widget, gpointer data)
dlg_cleanup(&es->dp);
ctrl_free_box(es->eventbox);
}
-static void eventlog_ok_handler(union control *ctrl, void *dlg,
+static void eventlog_ok_handler(union control *ctrl, dlgparam *dp,
void *data, int event)
{
if (event == EVENT_ACTION)
- dlg_end(dlg, 0);
+ dlg_end(dp, 0);
}
-static void eventlog_list_handler(union control *ctrl, void *dlg,
+static void eventlog_list_handler(union control *ctrl, dlgparam *dp,
void *data, int event)
{
struct eventlog_stuff *es = (struct eventlog_stuff *)data;
@@ -3820,15 +3794,15 @@ static void eventlog_list_handler(union control *ctrl, void *dlg,
if (event == EVENT_REFRESH) {
int i;
- dlg_update_start(ctrl, dlg);
- dlg_listbox_clear(ctrl, dlg);
+ dlg_update_start(ctrl, dp);
+ dlg_listbox_clear(ctrl, dp);
for (i = 0; i < es->ninitial; i++) {
- dlg_listbox_add(ctrl, dlg, es->events_initial[i]);
+ dlg_listbox_add(ctrl, dp, es->events_initial[i]);
}
for (i = 0; i < es->ncircular; i++) {
- dlg_listbox_add(ctrl, dlg, es->events_circular[(es->circular_first + i) % LOGEVENT_CIRCULAR_MAX]);
+ dlg_listbox_add(ctrl, dp, es->events_circular[(es->circular_first + i) % LOGEVENT_CIRCULAR_MAX]);
}
- dlg_update_done(ctrl, dlg);
+ dlg_update_done(ctrl, dp);
} else if (event == EVENT_SELCHANGE) {
int i;
int selsize = 0;
@@ -3849,7 +3823,7 @@ static void eventlog_list_handler(union control *ctrl, void *dlg,
es->seldata = NULL;
es->sellen = 0;
for (i = 0; i < es->ninitial; i++) {
- if (dlg_listbox_issel(ctrl, dlg, i)) {
+ if (dlg_listbox_issel(ctrl, dp, i)) {
int extralen = strlen(es->events_initial[i]);
if (es->sellen + extralen + 2 > selsize) {
@@ -3863,7 +3837,7 @@ static void eventlog_list_handler(union control *ctrl, void *dlg,
}
}
for (i = 0; i < es->ncircular; i++) {
- if (dlg_listbox_issel(ctrl, dlg, es->ninitial + i)) {
+ if (dlg_listbox_issel(ctrl, dp, es->ninitial + i)) {
int j = (es->circular_first + i) % LOGEVENT_CIRCULAR_MAX;
int extralen = strlen(es->events_circular[j]);
diff --git a/windows/wincfg.c b/windows/wincfg.c
index 9e242875..8131883c 100644
--- a/windows/wincfg.c
+++ b/windows/wincfg.c
@@ -10,7 +10,7 @@
#include "dialog.h"
#include "storage.h"
-static void about_handler(union control *ctrl, void *dlg,
+static void about_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
HWND *hwndp = (HWND *)ctrl->generic.context.p;
@@ -20,7 +20,7 @@ static void about_handler(union control *ctrl, void *dlg,
}
}
-static void help_handler(union control *ctrl, void *dlg,
+static void help_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
HWND *hwndp = (HWND *)ctrl->generic.context.p;
@@ -30,7 +30,7 @@ static void help_handler(union control *ctrl, void *dlg,
}
}
-static void variable_pitch_handler(union control *ctrl, void *dlg,
+static void variable_pitch_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
if (event == EVENT_REFRESH) {
diff --git a/windows/winctrls.c b/windows/winctrls.c
index da2fac62..1b51598f 100644
--- a/windows/winctrls.c
+++ b/windows/winctrls.c
@@ -1722,9 +1722,8 @@ static void winctrl_set_focus(union control *ctrl, struct dlgparam *dp,
}
}
-union control *dlg_last_focused(union control *ctrl, void *dlg)
+union control *dlg_last_focused(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
return dp->focused == ctrl ? dp->lastfocused : dp->focused;
}
@@ -2067,9 +2066,8 @@ static struct winctrl *dlg_findbyctrl(struct dlgparam *dp, union control *ctrl)
return NULL;
}
-void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton)
+void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int whichbutton)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_RADIO);
CheckRadioButton(dp->hwnd,
@@ -2078,9 +2076,8 @@ void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton)
c->base_id + 1 + whichbutton);
}
-int dlg_radiobutton_get(union control *ctrl, void *dlg)
+int dlg_radiobutton_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int i;
assert(c && c->ctrl->generic.type == CTRL_RADIO);
@@ -2091,42 +2088,37 @@ int dlg_radiobutton_get(union control *ctrl, void *dlg)
return 0;
}
-void dlg_checkbox_set(union control *ctrl, void *dlg, int checked)
+void dlg_checkbox_set(union control *ctrl, dlgparam *dp, int checked)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);
CheckDlgButton(dp->hwnd, c->base_id, (checked != 0));
}
-int dlg_checkbox_get(union control *ctrl, void *dlg)
+int dlg_checkbox_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);
return 0 != IsDlgButtonChecked(dp->hwnd, c->base_id);
}
-void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
+void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
SetDlgItemText(dp->hwnd, c->base_id+1, text);
}
-char *dlg_editbox_get(union control *ctrl, void *dlg)
+char *dlg_editbox_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
return GetDlgItemText_alloc(dp->hwnd, c->base_id+1);
}
/* The `listbox' functions can also apply to combo boxes. */
-void dlg_listbox_clear(union control *ctrl, void *dlg)
+void dlg_listbox_clear(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg;
assert(c &&
@@ -2138,9 +2130,8 @@ void dlg_listbox_clear(union control *ctrl, void *dlg)
SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0);
}
-void dlg_listbox_del(union control *ctrl, void *dlg, int index)
+void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg;
assert(c &&
@@ -2152,9 +2143,8 @@ void dlg_listbox_del(union control *ctrl, void *dlg, int index)
SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0);
}
-void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
+void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg;
assert(c &&
@@ -2173,10 +2163,9 @@ void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
* strings in any listbox then you MUST not assign them different
* IDs and expect to get meaningful results back.
*/
-void dlg_listbox_addwithid(union control *ctrl, void *dlg,
+void dlg_listbox_addwithid(union control *ctrl, dlgparam *dp,
char const *text, int id)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg, msg2, index;
assert(c &&
@@ -2191,9 +2180,8 @@ void dlg_listbox_addwithid(union control *ctrl, void *dlg,
SendDlgItemMessage(dp->hwnd, c->base_id+1, msg2, index, (LPARAM)id);
}
-int dlg_listbox_getid(union control *ctrl, void *dlg, int index)
+int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg;
assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
@@ -2203,9 +2191,8 @@ int dlg_listbox_getid(union control *ctrl, void *dlg, int index)
}
/* dlg_listbox_index returns <0 if no single element is selected. */
-int dlg_listbox_index(union control *ctrl, void *dlg)
+int dlg_listbox_index(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg, ret;
assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
@@ -2223,9 +2210,8 @@ int dlg_listbox_index(union control *ctrl, void *dlg)
return ret;
}
-int dlg_listbox_issel(union control *ctrl, void *dlg, int index)
+int dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
c->ctrl->listbox.multisel &&
@@ -2234,9 +2220,8 @@ int dlg_listbox_issel(union control *ctrl, void *dlg, int index)
SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSEL, index, 0);
}
-void dlg_listbox_select(union control *ctrl, void *dlg, int index)
+void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg;
assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
@@ -2245,17 +2230,15 @@ void dlg_listbox_select(union control *ctrl, void *dlg, int index)
SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0);
}
-void dlg_text_set(union control *ctrl, void *dlg, char const *text)
+void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_TEXT);
SetDlgItemText(dp->hwnd, c->base_id, text);
}
-void dlg_label_change(union control *ctrl, void *dlg, char const *text)
+void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
char *escaped = NULL;
int id = -1;
@@ -2300,17 +2283,15 @@ void dlg_label_change(union control *ctrl, void *dlg, char const *text)
}
}
-void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn)
+void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_FILESELECT);
SetDlgItemText(dp->hwnd, c->base_id+1, fn->path);
}
-Filename *dlg_filesel_get(union control *ctrl, void *dlg)
+Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
char *tmp;
Filename *ret;
@@ -2321,10 +2302,9 @@ Filename *dlg_filesel_get(union control *ctrl, void *dlg)
return ret;
}
-void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs)
+void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fs)
{
char *buf, *boldstr;
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_FONTSELECT);
@@ -2344,9 +2324,8 @@ void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fs)
dlg_auto_set_fixed_pitch_flag(dp);
}
-FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg)
+FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
assert(c && c->ctrl->generic.type == CTRL_FONTSELECT);
return fontspec_copy((FontSpec *)c->data);
@@ -2357,18 +2336,16 @@ FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg)
* cause the front end (if possible) to delay updating the screen
* until it's all complete, thus avoiding flicker.
*/
-void dlg_update_start(union control *ctrl, void *dlg)
+void dlg_update_start(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
if (c && c->ctrl->generic.type == CTRL_LISTBOX) {
SendDlgItemMessage(dp->hwnd, c->base_id+1, WM_SETREDRAW, FALSE, 0);
}
}
-void dlg_update_done(union control *ctrl, void *dlg)
+void dlg_update_done(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
if (c && c->ctrl->generic.type == CTRL_LISTBOX) {
HWND hw = GetDlgItem(dp->hwnd, c->base_id+1);
@@ -2377,9 +2354,8 @@ void dlg_update_done(union control *ctrl, void *dlg)
}
}
-void dlg_set_focus(union control *ctrl, void *dlg)
+void dlg_set_focus(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int id;
HWND ctl;
@@ -2413,15 +2389,13 @@ void dlg_set_focus(union control *ctrl, void *dlg)
* indication to the user. dlg_beep() is a quick and easy generic
* error; dlg_error() puts up a message-box or equivalent.
*/
-void dlg_beep(void *dlg)
+void dlg_beep(dlgparam *dp)
{
- /* struct dlgparam *dp = (struct dlgparam *)dlg; */
MessageBeep(0);
}
-void dlg_error_msg(void *dlg, const char *msg)
+void dlg_error_msg(dlgparam *dp, const char *msg)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
MessageBox(dp->hwnd, msg,
dp->errtitle ? dp->errtitle : NULL,
MB_OK | MB_ICONERROR);
@@ -2432,16 +2406,14 @@ void dlg_error_msg(void *dlg, const char *msg)
* processing is completed, and passes an integer value (typically
* a success status).
*/
-void dlg_end(void *dlg, int value)
+void dlg_end(dlgparam *dp, int value)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
dp->ended = TRUE;
dp->endresult = value;
}
-void dlg_refresh(union control *ctrl, void *dlg)
+void dlg_refresh(union control *ctrl, dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
int i, j;
struct winctrl *c;
@@ -2467,19 +2439,17 @@ void dlg_refresh(union control *ctrl, void *dlg)
}
}
-void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
+void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
dp->coloursel_wanted = TRUE;
dp->coloursel_result.r = r;
dp->coloursel_result.g = g;
dp->coloursel_result.b = b;
}
-int dlg_coloursel_results(union control *ctrl, void *dlg,
+int dlg_coloursel_results(union control *ctrl, dlgparam *dp,
int *r, int *g, int *b)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
if (dp->coloursel_result.ok) {
*r = dp->coloursel_result.r;
*g = dp->coloursel_result.g;
@@ -2489,9 +2459,8 @@ int dlg_coloursel_results(union control *ctrl, void *dlg,
return 0;
}
-void dlg_auto_set_fixed_pitch_flag(void *dlg)
+void dlg_auto_set_fixed_pitch_flag(dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
Conf *conf = (Conf *)dp->data;
FontSpec *fs;
int quality;
@@ -2533,15 +2502,13 @@ void dlg_auto_set_fixed_pitch_flag(void *dlg)
dp->fixed_pitch_fonts = FALSE;
}
-int dlg_get_fixed_pitch_flag(void *dlg)
+int dlg_get_fixed_pitch_flag(dlgparam *dp)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
return dp->fixed_pitch_fonts;
}
-void dlg_set_fixed_pitch_flag(void *dlg, int flag)
+void dlg_set_fixed_pitch_flag(dlgparam *dp, int flag)
{
- struct dlgparam *dp = (struct dlgparam *)dlg;
dp->fixed_pitch_fonts = flag;
}
diff --git a/windows/winstuff.h b/windows/winstuff.h
index 1ecec333..e57bf0da 100644
--- a/windows/winstuff.h
+++ b/windows/winstuff.h
@@ -437,9 +437,9 @@ void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
char *btext, int bid,
char *r1text, int r1id, char *r2text, int r2id);
-void dlg_auto_set_fixed_pitch_flag(void *dlg);
-int dlg_get_fixed_pitch_flag(void *dlg);
-void dlg_set_fixed_pitch_flag(void *dlg, int flag);
+void dlg_auto_set_fixed_pitch_flag(dlgparam *dlg);
+int dlg_get_fixed_pitch_flag(dlgparam *dlg);
+void dlg_set_fixed_pitch_flag(dlgparam *dlg, int flag);
#define MAX_SHORTCUTS_PER_CTRL 16
From 65b65bb8ef8e35cc700d0883d344a361f04d3927 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 13 Sep 2018 13:00:56 +0100
Subject: [PATCH 398/607] Expose the structure tag 'crcda_ctx'.
This one isn't used in many places, but it's another 'void *' pointer
that ought to come with an identifying structure tag.
---
ssh.h | 7 ++++---
ssh1bpp.c | 2 +-
sshcrcda.c | 8 +++-----
3 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/ssh.h b/ssh.h
index 2d285d23..1026a90b 100644
--- a/ssh.h
+++ b/ssh.h
@@ -322,9 +322,10 @@ unsigned long crc32_compute(const void *s, size_t len);
unsigned long crc32_update(unsigned long crc_input, const void *s, size_t len);
/* SSH CRC compensation attack detector */
-void *crcda_make_context(void);
-void crcda_free_context(void *handle);
-int detect_attack(void *handle, unsigned char *buf, uint32 len,
+struct crcda_ctx;
+struct crcda_ctx *crcda_make_context(void);
+void crcda_free_context(struct crcda_ctx *ctx);
+int detect_attack(struct crcda_ctx *ctx, unsigned char *buf, uint32 len,
unsigned char *IV);
/*
diff --git a/ssh1bpp.c b/ssh1bpp.c
index 7f5b72f6..5ace35fd 100644
--- a/ssh1bpp.c
+++ b/ssh1bpp.c
@@ -20,7 +20,7 @@ struct ssh1_bpp_state {
const struct ssh_cipher *cipher;
void *cipher_ctx;
- void *crcda_ctx;
+ struct crcda_ctx *crcda_ctx;
void *compctx, *decompctx;
diff --git a/sshcrcda.c b/sshcrcda.c
index 8d77cbb6..fe6598d3 100644
--- a/sshcrcda.c
+++ b/sshcrcda.c
@@ -55,7 +55,7 @@ struct crcda_ctx {
uint32 n;
};
-void *crcda_make_context(void)
+struct crcda_ctx *crcda_make_context(void)
{
struct crcda_ctx *ret = snew(struct crcda_ctx);
ret->h = NULL;
@@ -63,9 +63,8 @@ void *crcda_make_context(void)
return ret;
}
-void crcda_free_context(void *handle)
+void crcda_free_context(struct crcda_ctx *ctx)
{
- struct crcda_ctx *ctx = (struct crcda_ctx *)handle;
if (ctx) {
sfree(ctx->h);
ctx->h = NULL;
@@ -102,9 +101,8 @@ static int check_crc(uchar *S, uchar *buf, uint32 len, uchar *IV)
}
/* Detect a crc32 compensation attack on a packet */
-int detect_attack(void *handle, uchar *buf, uint32 len, uchar *IV)
+int detect_attack(struct crcda_ctx *ctx, uchar *buf, uint32 len, uchar *IV)
{
- struct crcda_ctx *ctx = (struct crcda_ctx *)handle;
register uint32 i, j;
uint32 l;
register uchar *c;
From 6c5cc49e276a7d0991da5a5a21540f38a8d0b0d6 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 13 Sep 2018 13:29:32 +0100
Subject: [PATCH 399/607] Turn SSH-1 ciphers into a classoid.
The interchangeable system of SSH-1 ciphers previously followed the
same pattern as the backends and the public-key algorithms, in that
all the clients would maintain two separate pointers, one to the
vtable and the other to the individual instance / context. Now I've
merged them, just as I did with those other two, so that you only cart
around a single pointer, which has a vtable pointer inside it and a
type distinguishing it from an instance of any of the other
interchangeable sets of algorithms.
---
ssh.c | 7 ++--
ssh.h | 30 ++++++++++-----
ssh1bpp.c | 18 ++++-----
sshblowf.c | 54 +++++++++++++++++---------
sshbpp.h | 2 +-
sshdes.c | 110 +++++++++++++++++++++++++++++++++--------------------
6 files changed, 137 insertions(+), 84 deletions(-)
diff --git a/ssh.c b/ssh.c
index dd1d1c1f..3d4baf47 100644
--- a/ssh.c
+++ b/ssh.c
@@ -2914,10 +2914,9 @@ static void do_ssh1_login(void *vctx)
sfree(s->rsabuf);
{
- const struct ssh_cipher *cipher =
- (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
- s->cipher_type == SSH_CIPHER_DES ? &ssh_des :
- &ssh_3des);
+ const struct ssh1_cipheralg *cipher =
+ (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh1_blowfish :
+ s->cipher_type == SSH_CIPHER_DES ? &ssh1_des : &ssh1_3des);
ssh1_bpp_new_cipher(ssh->bpp, cipher, ssh->session_key);
logeventf(ssh, "Initialised %s encryption", cipher->text_name);
}
diff --git a/ssh.h b/ssh.h
index 1026a90b..c10d6fb1 100644
--- a/ssh.h
+++ b/ssh.h
@@ -420,17 +420,27 @@ void SHA384_Init(SHA384_State * s);
void SHA384_Final(SHA384_State * s, unsigned char *output);
void SHA384_Simple(const void *p, int len, unsigned char *output);
-struct ssh_mac;
-struct ssh_cipher {
- void *(*make_context)(void);
- void (*free_context)(void *);
- void (*sesskey) (void *, const void *key); /* for SSH-1 */
- void (*encrypt) (void *, void *blk, int len);
- void (*decrypt) (void *, void *blk, int len);
+struct ssh2_macalg;
+
+struct ssh1_cipheralg;
+typedef const struct ssh1_cipheralg *ssh1_cipher;
+
+struct ssh1_cipheralg {
+ ssh1_cipher *(*new)(void);
+ void (*free)(ssh1_cipher *);
+ void (*sesskey)(ssh1_cipher *, const void *key);
+ void (*encrypt)(ssh1_cipher *, void *blk, int len);
+ void (*decrypt)(ssh1_cipher *, void *blk, int len);
int blksize;
const char *text_name;
};
+#define ssh1_cipher_new(alg) ((alg)->new())
+#define ssh1_cipher_free(ctx) ((*(ctx))->free(ctx))
+#define ssh1_cipher_sesskey(ctx, key) ((*(ctx))->sesskey(ctx, key))
+#define ssh1_cipher_encrypt(ctx, blk, len) ((*(ctx))->encrypt(ctx, blk, len))
+#define ssh1_cipher_decrypt(ctx, blk, len) ((*(ctx))->decrypt(ctx, blk, len))
+
struct ssh2_cipher {
void *(*make_context)(void);
void (*free_context)(void *);
@@ -576,9 +586,9 @@ struct ssh2_userkey {
/* The maximum length of any hash algorithm used in kex. (bytes) */
#define SSH2_KEX_MAX_HASH_LEN (64) /* SHA-512 */
-extern const struct ssh_cipher ssh_3des;
-extern const struct ssh_cipher ssh_des;
-extern const struct ssh_cipher ssh_blowfish_ssh1;
+extern const struct ssh1_cipheralg ssh1_3des;
+extern const struct ssh1_cipheralg ssh1_des;
+extern const struct ssh1_cipheralg ssh1_blowfish;
extern const struct ssh2_ciphers ssh2_3des;
extern const struct ssh2_ciphers ssh2_des;
extern const struct ssh2_ciphers ssh2_aes;
diff --git a/ssh1bpp.c b/ssh1bpp.c
index 5ace35fd..627d55a5 100644
--- a/ssh1bpp.c
+++ b/ssh1bpp.c
@@ -17,8 +17,7 @@ struct ssh1_bpp_state {
int chunk;
PktIn *pktin;
- const struct ssh_cipher *cipher;
- void *cipher_ctx;
+ ssh1_cipher *cipher;
struct crcda_ctx *crcda_ctx;
@@ -51,7 +50,7 @@ static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
{
struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
if (s->cipher)
- s->cipher->free_context(s->cipher_ctx);
+ ssh1_cipher_free(s->cipher);
if (s->compctx)
zlib_compress_cleanup(s->compctx);
if (s->decompctx)
@@ -64,7 +63,7 @@ static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
}
void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
- const struct ssh_cipher *cipher,
+ const struct ssh1_cipheralg *cipher,
const void *session_key)
{
struct ssh1_bpp_state *s;
@@ -73,10 +72,9 @@ void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
assert(!s->cipher);
- s->cipher = cipher;
- if (s->cipher) {
- s->cipher_ctx = cipher->make_context();
- cipher->sesskey(s->cipher_ctx, session_key);
+ if (cipher) {
+ s->cipher = ssh1_cipher_new(cipher);
+ ssh1_cipher_sesskey(s->cipher, session_key);
assert(!s->crcda_ctx);
s->crcda_ctx = crcda_make_context();
@@ -146,7 +144,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
}
if (s->cipher)
- s->cipher->decrypt(s->cipher_ctx, s->data, s->biglen);
+ ssh1_cipher_decrypt(s->cipher, s->data, s->biglen);
s->realcrc = crc32_compute(s->data, s->biglen - 4);
s->gotcrc = GET_32BIT(s->data + s->biglen - 4);
@@ -273,7 +271,7 @@ static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
PUT_32BIT(pkt->data + pktoffs, len);
if (s->cipher)
- s->cipher->encrypt(s->cipher_ctx, pkt->data + pktoffs + 4, biglen);
+ ssh1_cipher_encrypt(s->cipher, pkt->data + pktoffs + 4, biglen);
bufchain_add(s->bpp.out_raw, pkt->data + pktoffs,
biglen + 4); /* len(length+padding+type+data+CRC) */
diff --git a/sshblowf.c b/sshblowf.c
index 24421059..764f3f65 100644
--- a/sshblowf.c
+++ b/sshblowf.c
@@ -561,15 +561,30 @@ void *blowfish_make_context(void)
return snew(BlowfishContext);
}
-static void *blowfish_ssh1_make_context(void)
+void blowfish_free_context(void *handle)
{
+ sfree(handle);
+}
+
+struct blowfish_ssh1_ctx {
/* In SSH-1, need one key for each direction */
- return snewn(2, BlowfishContext);
+ BlowfishContext contexts[2];
+ ssh1_cipher vt;
+};
+
+static ssh1_cipher *blowfish_ssh1_new(void)
+{
+ struct blowfish_ssh1_ctx *ctx = snew(struct blowfish_ssh1_ctx);
+ ctx->vt = &ssh1_blowfish;
+ return &ctx->vt;
}
-void blowfish_free_context(void *handle)
+static void blowfish_ssh1_free(ssh1_cipher *cipher)
{
- sfree(handle);
+ struct blowfish_ssh1_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
}
static void blowfish_key(void *handle, const void *key)
@@ -592,25 +607,27 @@ static void blowfish_iv(void *handle, const void *viv)
ctx->iv1 = GET_32BIT_MSB_FIRST(iv + 4);
}
-static void blowfish_sesskey(void *handle, const void *key)
+static void blowfish_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
{
- BlowfishContext *ctx = (BlowfishContext *)handle;
- blowfish_setkey(ctx, key, SSH_SESSION_KEY_LENGTH);
- ctx->iv0 = 0;
- ctx->iv1 = 0;
- ctx[1] = ctx[0]; /* structure copy */
+ struct blowfish_ssh1_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt);
+ blowfish_setkey(&ctx->contexts[0], key, SSH_SESSION_KEY_LENGTH);
+ ctx->contexts[0].iv0 = ctx->contexts[0].iv1 = 0;
+ ctx->contexts[1] = ctx->contexts[0]; /* structure copy */
}
-static void blowfish_ssh1_encrypt_blk(void *handle, void *blk, int len)
+static void blowfish_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
{
- BlowfishContext *ctx = (BlowfishContext *)handle;
- blowfish_lsb_encrypt_cbc(blk, len, ctx);
+ struct blowfish_ssh1_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt);
+ blowfish_lsb_encrypt_cbc(blk, len, ctx->contexts);
}
-static void blowfish_ssh1_decrypt_blk(void *handle, void *blk, int len)
+static void blowfish_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
{
- BlowfishContext *ctx = (BlowfishContext *)handle;
- blowfish_lsb_decrypt_cbc(blk, len, ctx+1);
+ struct blowfish_ssh1_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt);
+ blowfish_lsb_decrypt_cbc(blk, len, ctx->contexts+1);
}
static void blowfish_ssh2_encrypt_blk(void *handle, void *blk, int len)
@@ -631,8 +648,9 @@ static void blowfish_ssh2_sdctr(void *handle, void *blk, int len)
blowfish_msb_sdctr(blk, len, ctx);
}
-const struct ssh_cipher ssh_blowfish_ssh1 = {
- blowfish_ssh1_make_context, blowfish_free_context, blowfish_sesskey,
+const struct ssh1_cipheralg ssh1_blowfish = {
+ blowfish_ssh1_new, blowfish_ssh1_free,
+ blowfish_ssh1_sesskey,
blowfish_ssh1_encrypt_blk, blowfish_ssh1_decrypt_blk,
8, "Blowfish-128 CBC"
};
diff --git a/sshbpp.h b/sshbpp.h
index c1ac7a13..de11e69d 100644
--- a/sshbpp.h
+++ b/sshbpp.h
@@ -32,7 +32,7 @@ struct BinaryPacketProtocol {
BinaryPacketProtocol *ssh1_bpp_new(void);
void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
- const struct ssh_cipher *cipher,
+ const struct ssh1_cipheralg *cipher,
const void *session_key);
void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
diff --git a/sshdes.c b/sshdes.c
index 585deb7f..6cc1ce67 100644
--- a/sshdes.c
+++ b/sshdes.c
@@ -752,10 +752,23 @@ static void *des3_make_context(void)
return snewn(3, DESContext);
}
-static void *des3_ssh1_make_context(void)
+struct des3_ssh1_ctx {
+ /* 3 cipher context for each direction */
+ DESContext contexts[6];
+ ssh1_cipher vt;
+};
+
+struct des_ssh1_ctx {
+ /* 1 cipher context for each direction */
+ DESContext contexts[2];
+ ssh1_cipher vt;
+};
+
+static ssh1_cipher *des3_ssh1_new(void)
{
- /* Need 3 keys for each direction, in SSH-1 */
- return snewn(6, DESContext);
+ struct des3_ssh1_ctx *ctx = snew(struct des3_ssh1_ctx);
+ ctx->vt = &ssh1_3des;
+ return &ctx->vt;
}
static void *des_make_context(void)
@@ -763,10 +776,25 @@ static void *des_make_context(void)
return snew(DESContext);
}
-static void *des_ssh1_make_context(void)
+static ssh1_cipher *des_ssh1_new(void)
+{
+ struct des_ssh1_ctx *ctx = snew(struct des_ssh1_ctx);
+ ctx->vt = &ssh1_des;
+ return &ctx->vt;
+}
+
+static void des3_ssh1_free(ssh1_cipher *cipher)
+{
+ struct des3_ssh1_ctx *ctx = FROMFIELD(cipher, struct des3_ssh1_ctx, vt);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
+}
+
+static void des_ssh1_free(ssh1_cipher *cipher)
{
- /* Need one key for each direction, in SSH-1 */
- return snewn(2, DESContext);
+ struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
}
static void des3_free_context(void *handle) /* used for both 3DES and DES */
@@ -802,23 +830,42 @@ static void des_key(void *handle, const void *vkey)
GET_32BIT_MSB_FIRST(key + 4), &keys[0]);
}
-static void des3_sesskey(void *handle, const void *key)
+static void des3_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
{
- DESContext *keys = (DESContext *) handle;
- des3_key(keys, key);
- des3_key(keys+3, key);
+ struct des3_ssh1_ctx *ctx = FROMFIELD(cipher, struct des3_ssh1_ctx, vt);
+ des3_key(ctx->contexts, key);
+ des3_key(ctx->contexts+3, key);
}
-static void des3_encrypt_blk(void *handle, void *blk, int len)
+static void des3_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
{
- DESContext *keys = (DESContext *) handle;
- des_3cbc_encrypt(blk, len, keys);
+ struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+ des_3cbc_encrypt(blk, len, ctx->contexts);
}
-static void des3_decrypt_blk(void *handle, void *blk, int len)
+static void des3_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
{
- DESContext *keys = (DESContext *) handle;
- des_3cbc_decrypt(blk, len, keys+3);
+ struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+ des_3cbc_decrypt(blk, len, ctx->contexts+3);
+}
+
+static void des_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
+{
+ struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+ des_key(ctx->contexts, key);
+ des_key(ctx->contexts+1, key);
+}
+
+static void des_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
+{
+ struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+ des_cbc_encrypt(blk, len, ctx->contexts);
+}
+
+static void des_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
+{
+ struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+ des_cbc_decrypt(blk, len, ctx->contexts+1);
}
static void des3_ssh2_encrypt_blk(void *handle, void *blk, int len)
@@ -1017,34 +1064,15 @@ const struct ssh2_ciphers ssh2_des = {
des_list
};
-const struct ssh_cipher ssh_3des = {
- des3_ssh1_make_context, des3_free_context, des3_sesskey,
- des3_encrypt_blk, des3_decrypt_blk,
+const struct ssh1_cipheralg ssh1_3des = {
+ des3_ssh1_new, des3_ssh1_free, des3_ssh1_sesskey,
+ des3_ssh1_encrypt_blk, des3_ssh1_decrypt_blk,
8, "triple-DES inner-CBC"
};
-static void des_sesskey(void *handle, const void *key)
-{
- DESContext *keys = (DESContext *) handle;
- des_key(keys, key);
- des_key(keys+1, key);
-}
-
-static void des_encrypt_blk(void *handle, void *blk, int len)
-{
- DESContext *keys = (DESContext *) handle;
- des_cbc_encrypt(blk, len, keys);
-}
-
-static void des_decrypt_blk(void *handle, void *blk, int len)
-{
- DESContext *keys = (DESContext *) handle;
- des_cbc_decrypt(blk, len, keys+1);
-}
-
-const struct ssh_cipher ssh_des = {
- des_ssh1_make_context, des3_free_context, des_sesskey,
- des_encrypt_blk, des_decrypt_blk,
+const struct ssh1_cipheralg ssh1_des = {
+ des_ssh1_new, des_ssh1_free, des_ssh1_sesskey,
+ des_ssh1_encrypt_blk, des_ssh1_decrypt_blk,
8, "single-DES CBC"
};
From 229af2b5bf6945395aa5719db609a4224bd75719 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 13 Sep 2018 14:43:04 +0100
Subject: [PATCH 400/607] Turn SSH-2 ciphers into a classoid.
This is more or less the same job as the SSH-1 case, only more
extensive, because we have a wider range of ciphers.
I'm a bit disappointed about the AES case, in particular, because I
feel as if it ought to have been possible to arrange to combine this
layer of vtable dispatch with the subsidiary one that selects between
hardware and software implementations of the underlying cipher. I may
come back later and have another try at that, in fact.
---
import.c | 2 +-
ssh.c | 4 +-
ssh.h | 60 +++++++++++-------
ssh2bpp.c | 92 ++++++++++++++--------------
sshaes.c | 121 ++++++++++++++++++++++++------------
ssharcf.c | 44 ++++++++------
sshblowf.c | 104 +++++++++++++++++++------------
sshblowf.h | 4 +-
sshbpp.h | 4 +-
sshccp.c | 48 ++++++++-------
sshdes.c | 176 +++++++++++++++++++++++++++++++++--------------------
sshmd5.c | 2 +-
sshsh256.c | 2 +-
sshsha.c | 2 +-
14 files changed, 402 insertions(+), 263 deletions(-)
diff --git a/import.c b/import.c
index e95883a0..98cf8010 100644
--- a/import.c
+++ b/import.c
@@ -549,7 +549,7 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
des3_decrypt_pubkey_ossh(keybuf, key->iv,
key->keyblob->u, key->keyblob->len);
else {
- void *ctx;
+ AESContext *ctx;
assert(key->encryption == OP_E_AES);
ctx = aes_make_context();
aes128_key(ctx, keybuf);
diff --git a/ssh.c b/ssh.c
index 3d4baf47..1ece7488 100644
--- a/ssh.c
+++ b/ssh.c
@@ -4850,7 +4850,7 @@ struct kexinit_algorithm {
int warn;
} hk;
struct {
- const struct ssh2_cipher *cipher;
+ const struct ssh2_cipheralg *cipher;
int warn;
} cipher;
struct {
@@ -5026,7 +5026,7 @@ static void do_ssh2_transport(void *vctx)
const struct ssh_mac *const *maclist;
int nmacs;
struct {
- const struct ssh2_cipher *cipher;
+ const struct ssh2_cipheralg *cipher;
const struct ssh_mac *mac;
int etm_mode;
const struct ssh_compress *comp;
diff --git a/ssh.h b/ssh.h
index c10d6fb1..a856aa9e 100644
--- a/ssh.h
+++ b/ssh.h
@@ -355,6 +355,9 @@ Bignum ssh_ecdhkex_getkey(struct ec_key *key,
Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key,
unsigned char *digest, int digest_len);
+struct ssh2_cipheralg;
+typedef const struct ssh2_cipheralg *ssh2_cipher;
+
typedef struct {
uint32 h[4];
} MD5_Core_State;
@@ -371,7 +374,7 @@ void MD5Init(struct MD5Context *context);
void MD5Final(unsigned char digest[16], struct MD5Context *context);
void MD5Simple(void const *p, unsigned len, unsigned char output[16]);
-void *hmacmd5_make_context(void *);
+void *hmacmd5_make_context(ssh2_cipher *);
void hmacmd5_free_context(void *handle);
void hmacmd5_key(void *handle, void const *key, int len);
void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
@@ -441,16 +444,18 @@ struct ssh1_cipheralg {
#define ssh1_cipher_encrypt(ctx, blk, len) ((*(ctx))->encrypt(ctx, blk, len))
#define ssh1_cipher_decrypt(ctx, blk, len) ((*(ctx))->decrypt(ctx, blk, len))
-struct ssh2_cipher {
- void *(*make_context)(void);
- void (*free_context)(void *);
- void (*setiv) (void *, const void *iv); /* for SSH-2 */
- void (*setkey) (void *, const void *key);/* for SSH-2 */
- void (*encrypt) (void *, void *blk, int len);
- void (*decrypt) (void *, void *blk, int len);
+struct ssh2_cipheralg {
+ ssh2_cipher *(*new)(const struct ssh2_cipheralg *alg);
+ void (*free)(ssh2_cipher *);
+ void (*setiv)(ssh2_cipher *, const void *iv);
+ void (*setkey)(ssh2_cipher *, const void *key);
+ void (*encrypt)(ssh2_cipher *, void *blk, int len);
+ void (*decrypt)(ssh2_cipher *, void *blk, int len);
/* Ignored unless SSH_CIPHER_SEPARATE_LENGTH flag set */
- void (*encrypt_length) (void *, void *blk, int len, unsigned long seq);
- void (*decrypt_length) (void *, void *blk, int len, unsigned long seq);
+ void (*encrypt_length)(ssh2_cipher *, void *blk, int len,
+ unsigned long seq);
+ void (*decrypt_length)(ssh2_cipher *, void *blk, int len,
+ unsigned long seq);
const char *name;
int blksize;
/* real_keybits is the number of bits of entropy genuinely used by
@@ -474,14 +479,26 @@ struct ssh2_cipher {
const struct ssh_mac *required_mac;
};
+#define ssh2_cipher_new(alg) ((alg)->new(alg))
+#define ssh2_cipher_free(ctx) ((*(ctx))->free(ctx))
+#define ssh2_cipher_setiv(ctx, iv) ((*(ctx))->setiv(ctx, iv))
+#define ssh2_cipher_setkey(ctx, key) ((*(ctx))->setkey(ctx, key))
+#define ssh2_cipher_encrypt(ctx, blk, len) ((*(ctx))->encrypt(ctx, blk, len))
+#define ssh2_cipher_decrypt(ctx, blk, len) ((*(ctx))->decrypt(ctx, blk, len))
+#define ssh2_cipher_encrypt_length(ctx, blk, len, seq) \
+ ((*(ctx))->encrypt_length(ctx, blk, len, seq))
+#define ssh2_cipher_decrypt_length(ctx, blk, len, seq) \
+ ((*(ctx))->decrypt_length(ctx, blk, len, seq))
+#define ssh2_cipher_alg(ctx) (*(ctx))
+
struct ssh2_ciphers {
int nciphers;
- const struct ssh2_cipher *const *list;
+ const struct ssh2_cipheralg *const *list;
};
struct ssh_mac {
/* Passes in the cipher context */
- void *(*make_context)(void *);
+ void *(*make_context)(ssh2_cipher *);
void (*free_context)(void *);
void (*setkey) (void *, const void *key);
/* whole-packet operations */
@@ -618,15 +635,16 @@ extern const struct ssh_mac ssh_hmac_sha1_96;
extern const struct ssh_mac ssh_hmac_sha1_96_buggy;
extern const struct ssh_mac ssh_hmac_sha256;
-void *aes_make_context(void);
-void aes_free_context(void *handle);
-void aes128_key(void *handle, const void *key);
-void aes192_key(void *handle, const void *key);
-void aes256_key(void *handle, const void *key);
-void aes_iv(void *handle, const void *iv);
-void aes_ssh2_encrypt_blk(void *handle, void *blk, int len);
-void aes_ssh2_decrypt_blk(void *handle, void *blk, int len);
-void aes_ssh2_sdctr(void *handle, void *blk, int len);
+typedef struct AESContext AESContext;
+AESContext *aes_make_context(void);
+void aes_free_context(AESContext *ctx);
+void aes128_key(AESContext *ctx, const void *key);
+void aes192_key(AESContext *ctx, const void *key);
+void aes256_key(AESContext *ctx, const void *key);
+void aes_iv(AESContext *ctx, const void *iv);
+void aes_ssh2_encrypt_blk(AESContext *ctx, void *blk, int len);
+void aes_ssh2_decrypt_blk(AESContext *ctx, void *blk, int len);
+void aes_ssh2_sdctr(AESContext *ctx, void *blk, int len);
/*
* PuTTY version number formatted as an SSH version string.
diff --git a/ssh2bpp.c b/ssh2bpp.c
index 007ab735..510e21a5 100644
--- a/ssh2bpp.c
+++ b/ssh2bpp.c
@@ -11,8 +11,7 @@
struct ssh2_bpp_direction {
unsigned long sequence;
- const struct ssh2_cipher *cipher;
- void *cipher_ctx;
+ ssh2_cipher *cipher;
const struct ssh_mac *mac;
int etm_mode;
void *mac_ctx;
@@ -60,14 +59,14 @@ static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
{
struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
sfree(s->buf);
- if (s->out.cipher_ctx)
- s->out.cipher->free_context(s->out.cipher_ctx);
+ if (s->out.cipher)
+ ssh2_cipher_free(s->out.cipher);
if (s->out.mac_ctx)
s->out.mac->free_context(s->out.mac_ctx);
if (s->out.comp_ctx)
s->out.comp->compress_cleanup(s->out.comp_ctx);
- if (s->in.cipher_ctx)
- s->in.cipher->free_context(s->in.cipher_ctx);
+ if (s->in.cipher)
+ ssh2_cipher_free(s->in.cipher);
if (s->in.mac_ctx)
s->in.mac->free_context(s->in.mac_ctx);
if (s->in.comp_ctx)
@@ -79,7 +78,7 @@ static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
void ssh2_bpp_new_outgoing_crypto(
BinaryPacketProtocol *bpp,
- const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+ const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
const struct ssh_mac *mac, int etm_mode, const void *mac_key,
const struct ssh_compress *compression)
{
@@ -87,23 +86,24 @@ void ssh2_bpp_new_outgoing_crypto(
assert(bpp->vt == &ssh2_bpp_vtable);
s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
- if (s->out.cipher_ctx)
- s->out.cipher->free_context(s->out.cipher_ctx);
+ if (s->out.cipher)
+ ssh2_cipher_free(s->out.cipher);
if (s->out.mac_ctx)
s->out.mac->free_context(s->out.mac_ctx);
if (s->out.comp_ctx)
s->out.comp->compress_cleanup(s->out.comp_ctx);
- s->out.cipher = cipher;
if (cipher) {
- s->out.cipher_ctx = cipher->make_context();
- cipher->setkey(s->out.cipher_ctx, ckey);
- cipher->setiv(s->out.cipher_ctx, iv);
+ s->out.cipher = ssh2_cipher_new(cipher);
+ ssh2_cipher_setkey(s->out.cipher, ckey);
+ ssh2_cipher_setiv(s->out.cipher, iv);
+ } else {
+ s->out.cipher = NULL;
}
s->out.mac = mac;
s->out.etm_mode = etm_mode;
if (mac) {
- s->out.mac_ctx = mac->make_context(s->out.cipher_ctx);
+ s->out.mac_ctx = mac->make_context(s->out.cipher);
mac->setkey(s->out.mac_ctx, mac_key);
}
@@ -116,7 +116,7 @@ void ssh2_bpp_new_outgoing_crypto(
void ssh2_bpp_new_incoming_crypto(
BinaryPacketProtocol *bpp,
- const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+ const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
const struct ssh_mac *mac, int etm_mode, const void *mac_key,
const struct ssh_compress *compression)
{
@@ -124,23 +124,24 @@ void ssh2_bpp_new_incoming_crypto(
assert(bpp->vt == &ssh2_bpp_vtable);
s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
- if (s->in.cipher_ctx)
- s->in.cipher->free_context(s->in.cipher_ctx);
+ if (s->in.cipher)
+ ssh2_cipher_free(s->in.cipher);
if (s->in.mac_ctx)
s->in.mac->free_context(s->in.mac_ctx);
if (s->in.comp_ctx)
s->in.comp->decompress_cleanup(s->in.comp_ctx);
- s->in.cipher = cipher;
if (cipher) {
- s->in.cipher_ctx = cipher->make_context();
- cipher->setkey(s->in.cipher_ctx, ckey);
- cipher->setiv(s->in.cipher_ctx, iv);
+ s->in.cipher = ssh2_cipher_new(cipher);
+ ssh2_cipher_setkey(s->in.cipher, ckey);
+ ssh2_cipher_setiv(s->in.cipher, iv);
+ } else {
+ s->in.cipher = NULL;
}
s->in.mac = mac;
s->in.etm_mode = etm_mode;
if (mac) {
- s->in.mac_ctx = mac->make_context(s->in.cipher_ctx);
+ s->in.mac_ctx = mac->make_context(s->in.cipher);
mac->setkey(s->in.mac_ctx, mac_key);
}
@@ -165,14 +166,15 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
s->maxlen = 0;
s->length = 0;
if (s->in.cipher)
- s->cipherblk = s->in.cipher->blksize;
+ s->cipherblk = ssh2_cipher_alg(s->in.cipher)->blksize;
else
s->cipherblk = 8;
if (s->cipherblk < 8)
s->cipherblk = 8;
s->maclen = s->in.mac ? s->in.mac->len : 0;
- if (s->in.cipher && (s->in.cipher->flags & SSH_CIPHER_IS_CBC) &&
+ if (s->in.cipher &&
+ (ssh2_cipher_alg(s->in.cipher)->flags & SSH_CIPHER_IS_CBC) &&
s->in.mac && !s->in.etm_mode) {
/*
* When dealing with a CBC-mode cipher, we want to avoid the
@@ -219,9 +221,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
s->cipherblk));
/* Decrypt one more block (a little further back in
* the stream). */
- s->in.cipher->decrypt(
- s->in.cipher_ctx,
- s->buf + s->packetlen, s->cipherblk);
+ ssh2_cipher_decrypt(s->in.cipher,
+ s->buf + s->packetlen, s->cipherblk);
/* Feed that block to the MAC. */
put_data(s->sc_mac_bs,
@@ -265,13 +266,13 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
s->bpp.in_raw, s->buf, 4));
/* Cipher supports length decryption, so do it */
- if (s->in.cipher &&
- (s->in.cipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+ if (s->in.cipher && (ssh2_cipher_alg(s->in.cipher)->flags &
+ SSH_CIPHER_SEPARATE_LENGTH)) {
/* Keep the packet the same though, so the MAC passes */
unsigned char len[4];
memcpy(len, s->buf, 4);
- s->in.cipher->decrypt_length(
- s->in.cipher_ctx, len, 4, s->in.sequence);
+ ssh2_cipher_decrypt_length(
+ s->in.cipher, len, 4, s->in.sequence);
s->len = toint(GET_32BIT(len));
} else {
s->len = toint(GET_32BIT(s->buf));
@@ -321,8 +322,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
/* Decrypt everything between the length field and the MAC. */
if (s->in.cipher)
- s->in.cipher->decrypt(
- s->in.cipher_ctx, s->data + 4, s->packetlen - 4);
+ ssh2_cipher_decrypt(
+ s->in.cipher, s->data + 4, s->packetlen - 4);
} else {
if (s->bufsize < s->cipherblk) {
s->bufsize = s->cipherblk;
@@ -337,8 +338,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
s->bpp.in_raw, s->buf, s->cipherblk));
if (s->in.cipher)
- s->in.cipher->decrypt(
- s->in.cipher_ctx, s->buf, s->cipherblk);
+ ssh2_cipher_decrypt(
+ s->in.cipher, s->buf, s->cipherblk);
/*
* Now get the length figure.
@@ -381,8 +382,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
/* Decrypt everything _except_ the MAC. */
if (s->in.cipher)
- s->in.cipher->decrypt(
- s->in.cipher_ctx,
+ ssh2_cipher_decrypt(
+ s->in.cipher,
s->data + s->cipherblk, s->packetlen - s->cipherblk);
/*
@@ -524,7 +525,7 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
pkt->downstream_id, pkt->additional_log_text);
}
- cipherblk = s->out.cipher ? s->out.cipher->blksize : 8;
+ cipherblk = s->out.cipher ? ssh2_cipher_alg(s->out.cipher)->blksize : 8;
cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
if (s->out.comp && s->out.comp_ctx) {
@@ -573,9 +574,9 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
/* Encrypt length if the scheme requires it */
if (s->out.cipher &&
- (s->out.cipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
- s->out.cipher->encrypt_length(s->out.cipher_ctx, pkt->data, 4,
- s->out.sequence);
+ (ssh2_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+ ssh2_cipher_encrypt_length(s->out.cipher, pkt->data, 4,
+ s->out.sequence);
}
put_padding(pkt, maclen, 0);
@@ -585,8 +586,8 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
* OpenSSH-defined encrypt-then-MAC protocol.
*/
if (s->out.cipher)
- s->out.cipher->encrypt(s->out.cipher_ctx,
- pkt->data + 4, origlen + padding - 4);
+ ssh2_cipher_encrypt(s->out.cipher,
+ pkt->data + 4, origlen + padding - 4);
s->out.mac->generate(s->out.mac_ctx, pkt->data, origlen + padding,
s->out.sequence);
} else {
@@ -598,8 +599,7 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
s->out.mac_ctx, pkt->data, origlen + padding,
s->out.sequence);
if (s->out.cipher)
- s->out.cipher->encrypt(s->out.cipher_ctx,
- pkt->data, origlen + padding);
+ ssh2_cipher_encrypt(s->out.cipher, pkt->data, origlen + padding);
}
s->out.sequence++; /* whether or not we MACed */
@@ -634,7 +634,7 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
int block, length;
PktOut *ignore_pkt;
- block = s->out.cipher ? s->out.cipher->blksize : 0;
+ block = s->out.cipher ? ssh2_cipher_alg(s->out.cipher)->blksize : 0;
if (block < 8)
block = 8;
length = pkt->length;
diff --git a/sshaes.c b/sshaes.c
index 0b09a6a6..5259396f 100644
--- a/sshaes.c
+++ b/sshaes.c
@@ -970,38 +970,35 @@ static void aes_decrypt_cbc_sw(unsigned char *blk, int len, AESContext * ctx)
memcpy(ctx->iv, iv, sizeof(iv));
}
-void *aes_make_context(void)
+AESContext *aes_make_context(void)
{
return snew(AESContext);
}
-void aes_free_context(void *handle)
+void aes_free_context(AESContext *ctx)
{
- sfree(handle);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
}
-void aes128_key(void *handle, const void *key)
+void aes128_key(AESContext *ctx, const void *key)
{
- AESContext *ctx = (AESContext *)handle;
aes_setup(ctx, key, 16);
}
-void aes192_key(void *handle, const void *key)
+void aes192_key(AESContext *ctx, const void *key)
{
- AESContext *ctx = (AESContext *)handle;
aes_setup(ctx, key, 24);
}
-void aes256_key(void *handle, const void *key)
+void aes256_key(AESContext *ctx, const void *key)
{
- AESContext *ctx = (AESContext *)handle;
aes_setup(ctx, key, 32);
}
-void aes_iv(void *handle, const void *viv)
+void aes_iv(AESContext *ctx, const void *viv)
{
const unsigned char *iv = (const unsigned char *)viv;
- AESContext *ctx = (AESContext *)handle;
if (ctx->isNI) {
memcpy(ctx->iv, iv, sizeof(ctx->iv));
}
@@ -1012,21 +1009,18 @@ void aes_iv(void *handle, const void *viv)
}
}
-void aes_ssh2_encrypt_blk(void *handle, void *blk, int len)
+void aes_ssh2_encrypt_blk(AESContext *ctx, void *blk, int len)
{
- AESContext *ctx = (AESContext *)handle;
aes_encrypt_cbc(blk, len, ctx);
}
-void aes_ssh2_decrypt_blk(void *handle, void *blk, int len)
+void aes_ssh2_decrypt_blk(AESContext *ctx, void *blk, int len)
{
- AESContext *ctx = (AESContext *)handle;
aes_decrypt_cbc(blk, len, ctx);
}
-void aes_ssh2_sdctr(void *handle, void *blk, int len)
+void aes_ssh2_sdctr(AESContext *ctx, void *blk, int len)
{
- AESContext *ctx = (AESContext *)handle;
aes_sdctr(blk, len, ctx);
}
@@ -1048,63 +1042,112 @@ void aes256_decrypt_pubkey(const void *key, void *blk, int len)
smemclr(&ctx, sizeof(ctx));
}
-static const struct ssh2_cipher ssh_aes128_ctr = {
- aes_make_context, aes_free_context, aes_iv, aes128_key,
- aes_ssh2_sdctr, aes_ssh2_sdctr, NULL, NULL,
+struct aes_ssh2_ctx {
+ AESContext context;
+ ssh2_cipher vt;
+};
+
+ssh2_cipher *aes_ssh2_new(const struct ssh2_cipheralg *alg)
+{
+ struct aes_ssh2_ctx *ctx = snew(struct aes_ssh2_ctx);
+ ctx->vt = alg;
+ return &ctx->vt;
+}
+
+static void aes_ssh2_free(ssh2_cipher *cipher)
+{
+ struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
+}
+
+static void aes_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
+{
+ struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+ aes_iv(&ctx->context, iv);
+}
+
+static void aes_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+{
+ struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+ aes_setup(&ctx->context, key, ctx->vt->padded_keybytes);
+}
+
+static void aes_ssh2_encrypt(ssh2_cipher *cipher, void *blk, int len)
+{
+ struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+ aes_encrypt_cbc(blk, len, &ctx->context);
+}
+
+static void aes_ssh2_decrypt(ssh2_cipher *cipher, void *blk, int len)
+{
+ struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+ aes_decrypt_cbc(blk, len, &ctx->context);
+}
+
+static void aes_ssh2_sdctr_method(ssh2_cipher *cipher, void *blk, int len)
+{
+ struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+ aes_sdctr(blk, len, &ctx->context);
+}
+
+static const struct ssh2_cipheralg ssh_aes128_ctr = {
+ aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+ aes_ssh2_sdctr_method, aes_ssh2_sdctr_method, NULL, NULL,
"aes128-ctr",
16, 128, 16, 0, "AES-128 SDCTR",
NULL
};
-static const struct ssh2_cipher ssh_aes192_ctr = {
- aes_make_context, aes_free_context, aes_iv, aes192_key,
- aes_ssh2_sdctr, aes_ssh2_sdctr, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes192_ctr = {
+ aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+ aes_ssh2_sdctr_method, aes_ssh2_sdctr_method, NULL, NULL,
"aes192-ctr",
16, 192, 24, 0, "AES-192 SDCTR",
NULL
};
-static const struct ssh2_cipher ssh_aes256_ctr = {
- aes_make_context, aes_free_context, aes_iv, aes256_key,
- aes_ssh2_sdctr, aes_ssh2_sdctr, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes256_ctr = {
+ aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+ aes_ssh2_sdctr_method, aes_ssh2_sdctr_method, NULL, NULL,
"aes256-ctr",
16, 256, 32, 0, "AES-256 SDCTR",
NULL
};
-static const struct ssh2_cipher ssh_aes128 = {
- aes_make_context, aes_free_context, aes_iv, aes128_key,
- aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes128 = {
+ aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+ aes_ssh2_encrypt, aes_ssh2_decrypt, NULL, NULL,
"aes128-cbc",
16, 128, 16, SSH_CIPHER_IS_CBC, "AES-128 CBC",
NULL
};
-static const struct ssh2_cipher ssh_aes192 = {
- aes_make_context, aes_free_context, aes_iv, aes192_key,
- aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes192 = {
+ aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+ aes_ssh2_encrypt, aes_ssh2_decrypt, NULL, NULL,
"aes192-cbc",
16, 192, 24, SSH_CIPHER_IS_CBC, "AES-192 CBC",
NULL
};
-static const struct ssh2_cipher ssh_aes256 = {
- aes_make_context, aes_free_context, aes_iv, aes256_key,
- aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes256 = {
+ aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+ aes_ssh2_encrypt, aes_ssh2_decrypt, NULL, NULL,
"aes256-cbc",
16, 256, 32, SSH_CIPHER_IS_CBC, "AES-256 CBC",
NULL
};
-static const struct ssh2_cipher ssh_rijndael_lysator = {
- aes_make_context, aes_free_context, aes_iv, aes256_key,
- aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
+static const struct ssh2_cipheralg ssh_rijndael_lysator = {
+ aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+ aes_ssh2_encrypt, aes_ssh2_decrypt, NULL, NULL,
"rijndael-cbc@lysator.liu.se",
16, 256, 32, SSH_CIPHER_IS_CBC, "AES-256 CBC",
NULL
};
-static const struct ssh2_cipher *const aes_list[] = {
+static const struct ssh2_cipheralg *const aes_list[] = {
&ssh_aes256_ctr,
&ssh_aes256,
&ssh_rijndael_lysator,
diff --git a/ssharcf.c b/ssharcf.c
index 336e4d06..3434459c 100644
--- a/ssharcf.c
+++ b/ssharcf.c
@@ -9,6 +9,7 @@
typedef struct {
unsigned char i, j, s[256];
+ ssh2_cipher vt;
} ArcfourContext;
static void arcfour_block(void *handle, void *vblk, int len)
@@ -61,14 +62,18 @@ static void arcfour_setkey(ArcfourContext *ctx, unsigned char const *key,
* to leak data about the key.
*/
-static void *arcfour_make_context(void)
+static ssh2_cipher *arcfour_new(const struct ssh2_cipheralg *alg)
{
- return snew(ArcfourContext);
+ ArcfourContext *ctx = snew(ArcfourContext);
+ ctx->vt = alg;
+ return &ctx->vt;
}
-static void arcfour_free_context(void *handle)
+static void arcfour_free(ssh2_cipher *cipher)
{
- sfree(handle);
+ ArcfourContext *ctx = FROMFIELD(cipher, ArcfourContext, vt);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
}
static void arcfour_stir(ArcfourContext *ctx)
@@ -80,42 +85,41 @@ static void arcfour_stir(ArcfourContext *ctx)
sfree(junk);
}
-static void arcfour128_key(void *handle, const void *key)
+static void arcfour_ssh2_setiv(ssh2_cipher *cipher, const void *key)
{
- ArcfourContext *ctx = (ArcfourContext *)handle;
- arcfour_setkey(ctx, key, 16);
- arcfour_stir(ctx);
+ /* As a pure stream cipher, Arcfour has no IV separate from the key */
}
-static void arcfour256_key(void *handle, const void *key)
+static void arcfour_ssh2_setkey(ssh2_cipher *cipher, const void *key)
{
- ArcfourContext *ctx = (ArcfourContext *)handle;
- arcfour_setkey(ctx, key, 32);
+ ArcfourContext *ctx = FROMFIELD(cipher, ArcfourContext, vt);
+ arcfour_setkey(ctx, key, ctx->vt->padded_keybytes);
arcfour_stir(ctx);
}
-static void arcfour_iv(void *handle, const void *iv)
+static void arcfour_ssh2_block(ssh2_cipher *cipher, void *blk, int len)
{
-
+ ArcfourContext *ctx = FROMFIELD(cipher, ArcfourContext, vt);
+ arcfour_block(ctx, blk, len);
}
-const struct ssh2_cipher ssh_arcfour128_ssh2 = {
- arcfour_make_context, arcfour_free_context, arcfour_iv, arcfour128_key,
- arcfour_block, arcfour_block, NULL, NULL,
+const struct ssh2_cipheralg ssh_arcfour128_ssh2 = {
+ arcfour_new, arcfour_free, arcfour_ssh2_setiv, arcfour_ssh2_setkey,
+ arcfour_ssh2_block, arcfour_ssh2_block, NULL, NULL,
"arcfour128",
1, 128, 16, 0, "Arcfour-128",
NULL
};
-const struct ssh2_cipher ssh_arcfour256_ssh2 = {
- arcfour_make_context, arcfour_free_context, arcfour_iv, arcfour256_key,
- arcfour_block, arcfour_block, NULL, NULL,
+const struct ssh2_cipheralg ssh_arcfour256_ssh2 = {
+ arcfour_new, arcfour_free, arcfour_ssh2_setiv, arcfour_ssh2_setkey,
+ arcfour_ssh2_block, arcfour_ssh2_block, NULL, NULL,
"arcfour256",
1, 256, 32, 0, "Arcfour-256",
NULL
};
-static const struct ssh2_cipher *const arcfour_list[] = {
+static const struct ssh2_cipheralg *const arcfour_list[] = {
&ssh_arcfour256_ssh2,
&ssh_arcfour128_ssh2,
};
diff --git a/sshblowf.c b/sshblowf.c
index 764f3f65..f5460518 100644
--- a/sshblowf.c
+++ b/sshblowf.c
@@ -554,16 +554,23 @@ static void blowfish_setkey(BlowfishContext *ctx,
/* -- Interface with PuTTY -- */
-#define SSH_SESSION_KEY_LENGTH 32
+#define SSH1_SESSION_KEY_LENGTH 32
-void *blowfish_make_context(void)
+BlowfishContext *blowfish_make_context(void)
{
return snew(BlowfishContext);
}
-void blowfish_free_context(void *handle)
+void blowfish_free_context(BlowfishContext *ctx)
{
- sfree(handle);
+ sfree(ctx);
+}
+
+static void blowfish_iv(BlowfishContext *ctx, const void *viv)
+{
+ const unsigned char *iv = (const unsigned char *)viv;
+ ctx->iv0 = GET_32BIT_MSB_FIRST(iv);
+ ctx->iv1 = GET_32BIT_MSB_FIRST(iv + 4);
}
struct blowfish_ssh1_ctx {
@@ -587,31 +594,11 @@ static void blowfish_ssh1_free(ssh1_cipher *cipher)
sfree(ctx);
}
-static void blowfish_key(void *handle, const void *key)
-{
- BlowfishContext *ctx = (BlowfishContext *)handle;
- blowfish_setkey(ctx, key, 16);
-}
-
-static void blowfish256_key(void *handle, const void *key)
-{
- BlowfishContext *ctx = (BlowfishContext *)handle;
- blowfish_setkey(ctx, key, 32);
-}
-
-static void blowfish_iv(void *handle, const void *viv)
-{
- const unsigned char *iv = (const unsigned char *)viv;
- BlowfishContext *ctx = (BlowfishContext *)handle;
- ctx->iv0 = GET_32BIT_MSB_FIRST(iv);
- ctx->iv1 = GET_32BIT_MSB_FIRST(iv + 4);
-}
-
static void blowfish_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
{
struct blowfish_ssh1_ctx *ctx =
FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt);
- blowfish_setkey(&ctx->contexts[0], key, SSH_SESSION_KEY_LENGTH);
+ blowfish_setkey(&ctx->contexts[0], key, SSH1_SESSION_KEY_LENGTH);
ctx->contexts[0].iv0 = ctx->contexts[0].iv1 = 0;
ctx->contexts[1] = ctx->contexts[0]; /* structure copy */
}
@@ -630,22 +617,59 @@ static void blowfish_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
blowfish_lsb_decrypt_cbc(blk, len, ctx->contexts+1);
}
-static void blowfish_ssh2_encrypt_blk(void *handle, void *blk, int len)
+struct blowfish_ssh2_ctx {
+ BlowfishContext context;
+ ssh2_cipher vt;
+};
+
+static ssh2_cipher *blowfish_ssh2_new(const struct ssh2_cipheralg *alg)
+{
+ struct blowfish_ssh2_ctx *ctx = snew(struct blowfish_ssh2_ctx);
+ ctx->vt = alg;
+ return &ctx->vt;
+}
+
+static void blowfish_ssh2_free(ssh2_cipher *cipher)
+{
+ struct blowfish_ssh2_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
+}
+
+static void blowfish_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
+{
+ struct blowfish_ssh2_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+ blowfish_iv(&ctx->context, iv);
+}
+
+static void blowfish_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+{
+ struct blowfish_ssh2_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+ blowfish_setkey(&ctx->context, key, ctx->vt->padded_keybytes);
+}
+
+static void blowfish_ssh2_encrypt_blk(ssh2_cipher *cipher, void *blk, int len)
{
- BlowfishContext *ctx = (BlowfishContext *)handle;
- blowfish_msb_encrypt_cbc(blk, len, ctx);
+ struct blowfish_ssh2_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+ blowfish_msb_encrypt_cbc(blk, len, &ctx->context);
}
-static void blowfish_ssh2_decrypt_blk(void *handle, void *blk, int len)
+static void blowfish_ssh2_decrypt_blk(ssh2_cipher *cipher, void *blk, int len)
{
- BlowfishContext *ctx = (BlowfishContext *)handle;
- blowfish_msb_decrypt_cbc(blk, len, ctx);
+ struct blowfish_ssh2_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+ blowfish_msb_decrypt_cbc(blk, len, &ctx->context);
}
-static void blowfish_ssh2_sdctr(void *handle, void *blk, int len)
+static void blowfish_ssh2_sdctr(ssh2_cipher *cipher, void *blk, int len)
{
- BlowfishContext *ctx = (BlowfishContext *)handle;
- blowfish_msb_sdctr(blk, len, ctx);
+ struct blowfish_ssh2_ctx *ctx =
+ FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+ blowfish_msb_sdctr(blk, len, &ctx->context);
}
const struct ssh1_cipheralg ssh1_blowfish = {
@@ -655,23 +679,25 @@ const struct ssh1_cipheralg ssh1_blowfish = {
8, "Blowfish-128 CBC"
};
-static const struct ssh2_cipher ssh_blowfish_ssh2 = {
- blowfish_make_context, blowfish_free_context, blowfish_iv, blowfish_key,
+static const struct ssh2_cipheralg ssh_blowfish_ssh2 = {
+ blowfish_ssh2_new, blowfish_ssh2_free,
+ blowfish_ssh2_setiv, blowfish_ssh2_setkey,
blowfish_ssh2_encrypt_blk, blowfish_ssh2_decrypt_blk, NULL, NULL,
"blowfish-cbc",
8, 128, 16, SSH_CIPHER_IS_CBC, "Blowfish-128 CBC",
NULL
};
-static const struct ssh2_cipher ssh_blowfish_ssh2_ctr = {
- blowfish_make_context, blowfish_free_context, blowfish_iv, blowfish256_key,
+static const struct ssh2_cipheralg ssh_blowfish_ssh2_ctr = {
+ blowfish_ssh2_new, blowfish_ssh2_free,
+ blowfish_ssh2_setiv, blowfish_ssh2_setkey,
blowfish_ssh2_sdctr, blowfish_ssh2_sdctr, NULL, NULL,
"blowfish-ctr",
8, 256, 32, 0, "Blowfish-256 SDCTR",
NULL
};
-static const struct ssh2_cipher *const blowfish_list[] = {
+static const struct ssh2_cipheralg *const blowfish_list[] = {
&ssh_blowfish_ssh2_ctr,
&ssh_blowfish_ssh2
};
diff --git a/sshblowf.h b/sshblowf.h
index e2364521..a9efe3da 100644
--- a/sshblowf.h
+++ b/sshblowf.h
@@ -5,8 +5,8 @@
typedef struct BlowfishContext BlowfishContext;
-void *blowfish_make_context(void);
-void blowfish_free_context(void *handle);
+BlowfishContext *blowfish_make_context(void);
+void blowfish_free_context(BlowfishContext *ctx);
void blowfish_initkey(BlowfishContext *ctx);
void blowfish_expandkey(BlowfishContext *ctx,
const void *key, short keybytes,
diff --git a/sshbpp.h b/sshbpp.h
index de11e69d..41683410 100644
--- a/sshbpp.h
+++ b/sshbpp.h
@@ -39,12 +39,12 @@ void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
BinaryPacketProtocol *ssh2_bpp_new(void);
void ssh2_bpp_new_outgoing_crypto(
BinaryPacketProtocol *bpp,
- const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+ const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
const struct ssh_mac *mac, int etm_mode, const void *mac_key,
const struct ssh_compress *compression);
void ssh2_bpp_new_incoming_crypto(
BinaryPacketProtocol *bpp,
- const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+ const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
const struct ssh_mac *mac, int etm_mode, const void *mac_key,
const struct ssh_compress *compression);
diff --git a/sshccp.c b/sshccp.c
index 04f1cc16..9919ced2 100644
--- a/sshccp.c
+++ b/sshccp.c
@@ -20,7 +20,7 @@
* This has an intricate link between the cipher and the MAC. The
* keying of both is done in by the cipher and setting of the IV is
* done by the MAC. One cannot operate without the other. The
- * configuration of the ssh2_cipher structure ensures that the MAC is
+ * configuration of the ssh2_cipheralg structure ensures that the MAC is
* set (and others ignored) if this cipher is chosen.
*
* This cipher also encrypts the length using a different
@@ -866,11 +866,12 @@ struct ccp_context {
struct poly1305 mac;
BinarySink_IMPLEMENTATION;
+ ssh2_cipher vt;
};
-static void *poly_make_context(void *ctx)
+static void *poly_make_context(ssh2_cipher *cipher)
{
- return ctx;
+ return FROMFIELD(cipher, struct ccp_context, vt);
}
static void poly_free_context(void *ctx)
@@ -987,48 +988,49 @@ static const struct ssh_mac ssh2_poly1305 = {
16, 0, "Poly1305"
};
-static void *ccp_make_context(void)
+static ssh2_cipher *ccp_new(const struct ssh2_cipheralg *alg)
{
struct ccp_context *ctx = snew(struct ccp_context);
BinarySink_INIT(ctx, poly_BinarySink_write);
poly1305_init(&ctx->mac);
- return ctx;
+ ctx->vt = alg;
+ return &ctx->vt;
}
-static void ccp_free_context(void *vctx)
+static void ccp_free(ssh2_cipher *cipher)
{
- struct ccp_context *ctx = (struct ccp_context *)vctx;
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
smemclr(&ctx->a_cipher, sizeof(ctx->a_cipher));
smemclr(&ctx->b_cipher, sizeof(ctx->b_cipher));
smemclr(&ctx->mac, sizeof(ctx->mac));
sfree(ctx);
}
-static void ccp_iv(void *vctx, const void *iv)
+static void ccp_iv(ssh2_cipher *cipher, const void *iv)
{
- /* struct ccp_context *ctx = (struct ccp_context *)vctx; */
+ /* struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt); */
/* IV is set based on the sequence number */
}
-static void ccp_key(void *vctx, const void *vkey)
+static void ccp_key(ssh2_cipher *cipher, const void *vkey)
{
const unsigned char *key = (const unsigned char *)vkey;
- struct ccp_context *ctx = (struct ccp_context *)vctx;
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
/* Initialise the a_cipher (for decrypting lengths) with the first 256 bits */
chacha20_key(&ctx->a_cipher, key + 32);
/* Initialise the b_cipher (for content and MAC) with the second 256 bits */
chacha20_key(&ctx->b_cipher, key);
}
-static void ccp_encrypt(void *vctx, void *blk, int len)
+static void ccp_encrypt(ssh2_cipher *cipher, void *blk, int len)
{
- struct ccp_context *ctx = (struct ccp_context *)vctx;
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
chacha20_encrypt(&ctx->b_cipher, blk, len);
}
-static void ccp_decrypt(void *vctx, void *blk, int len)
+static void ccp_decrypt(ssh2_cipher *cipher, void *blk, int len)
{
- struct ccp_context *ctx = (struct ccp_context *)vctx;
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
chacha20_decrypt(&ctx->b_cipher, blk, len);
}
@@ -1049,26 +1051,26 @@ static void ccp_length_op(struct ccp_context *ctx, void *blk, int len,
smemclr(iv, sizeof(iv));
}
-static void ccp_encrypt_length(void *vctx, void *blk, int len,
+static void ccp_encrypt_length(ssh2_cipher *cipher, void *blk, int len,
unsigned long seq)
{
- struct ccp_context *ctx = (struct ccp_context *)vctx;
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
ccp_length_op(ctx, blk, len, seq);
chacha20_encrypt(&ctx->a_cipher, blk, len);
}
-static void ccp_decrypt_length(void *vctx, void *blk, int len,
+static void ccp_decrypt_length(ssh2_cipher *cipher, void *blk, int len,
unsigned long seq)
{
- struct ccp_context *ctx = (struct ccp_context *)vctx;
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
ccp_length_op(ctx, blk, len, seq);
chacha20_decrypt(&ctx->a_cipher, blk, len);
}
-static const struct ssh2_cipher ssh2_chacha20_poly1305 = {
+static const struct ssh2_cipheralg ssh2_chacha20_poly1305 = {
- ccp_make_context,
- ccp_free_context,
+ ccp_new,
+ ccp_free,
ccp_iv,
ccp_key,
ccp_encrypt,
@@ -1082,7 +1084,7 @@ static const struct ssh2_cipher ssh2_chacha20_poly1305 = {
&ssh2_poly1305
};
-static const struct ssh2_cipher *const ccp_list[] = {
+static const struct ssh2_cipheralg *const ccp_list[] = {
&ssh2_chacha20_poly1305
};
diff --git a/sshdes.c b/sshdes.c
index 6cc1ce67..c3afe3eb 100644
--- a/sshdes.c
+++ b/sshdes.c
@@ -747,9 +747,29 @@ static void des_sdctr3(unsigned char *blk,
scheds->iv1 = iv1;
}
-static void *des3_make_context(void)
+static void des3_key(DESContext *contexts, const void *vkey)
{
- return snewn(3, DESContext);
+ const unsigned char *key = (const unsigned char *)vkey;
+ des_key_setup(GET_32BIT_MSB_FIRST(key),
+ GET_32BIT_MSB_FIRST(key + 4), &contexts[0]);
+ des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
+ GET_32BIT_MSB_FIRST(key + 12), &contexts[1]);
+ des_key_setup(GET_32BIT_MSB_FIRST(key + 16),
+ GET_32BIT_MSB_FIRST(key + 20), &contexts[2]);
+}
+
+static void des_iv(DESContext *context, const void *viv)
+{
+ const unsigned char *iv = (const unsigned char *)viv;
+ context->iv0 = GET_32BIT_MSB_FIRST(iv);
+ context->iv1 = GET_32BIT_MSB_FIRST(iv + 4);
+}
+
+static void des_key(DESContext *context, const void *vkey)
+{
+ const unsigned char *key = (const unsigned char *)vkey;
+ des_key_setup(GET_32BIT_MSB_FIRST(key),
+ GET_32BIT_MSB_FIRST(key + 4), context);
}
struct des3_ssh1_ctx {
@@ -771,11 +791,6 @@ static ssh1_cipher *des3_ssh1_new(void)
return &ctx->vt;
}
-static void *des_make_context(void)
-{
- return snew(DESContext);
-}
-
static ssh1_cipher *des_ssh1_new(void)
{
struct des_ssh1_ctx *ctx = snew(struct des_ssh1_ctx);
@@ -797,39 +812,6 @@ static void des_ssh1_free(ssh1_cipher *cipher)
sfree(ctx);
}
-static void des3_free_context(void *handle) /* used for both 3DES and DES */
-{
- sfree(handle);
-}
-
-static void des3_key(void *handle, const void *vkey)
-{
- const unsigned char *key = (const unsigned char *)vkey;
- DESContext *keys = (DESContext *) handle;
- des_key_setup(GET_32BIT_MSB_FIRST(key),
- GET_32BIT_MSB_FIRST(key + 4), &keys[0]);
- des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
- GET_32BIT_MSB_FIRST(key + 12), &keys[1]);
- des_key_setup(GET_32BIT_MSB_FIRST(key + 16),
- GET_32BIT_MSB_FIRST(key + 20), &keys[2]);
-}
-
-static void des3_iv(void *handle, const void *viv)
-{
- const unsigned char *iv = (const unsigned char *)viv;
- DESContext *keys = (DESContext *) handle;
- keys[0].iv0 = GET_32BIT_MSB_FIRST(iv);
- keys[0].iv1 = GET_32BIT_MSB_FIRST(iv + 4);
-}
-
-static void des_key(void *handle, const void *vkey)
-{
- const unsigned char *key = (const unsigned char *)vkey;
- DESContext *keys = (DESContext *) handle;
- des_key_setup(GET_32BIT_MSB_FIRST(key),
- GET_32BIT_MSB_FIRST(key + 4), &keys[0]);
-}
-
static void des3_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
{
struct des3_ssh1_ctx *ctx = FROMFIELD(cipher, struct des3_ssh1_ctx, vt);
@@ -868,34 +850,98 @@ static void des_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
des_cbc_decrypt(blk, len, ctx->contexts+1);
}
-static void des3_ssh2_encrypt_blk(void *handle, void *blk, int len)
+struct des3_ssh2_ctx {
+ DESContext contexts[3];
+ ssh2_cipher vt;
+};
+
+struct des_ssh2_ctx {
+ DESContext context;
+ ssh2_cipher vt;
+};
+
+static ssh2_cipher *des3_ssh2_new(const struct ssh2_cipheralg *alg)
+{
+ struct des3_ssh2_ctx *ctx = snew(struct des3_ssh2_ctx);
+ ctx->vt = alg;
+ return &ctx->vt;
+}
+
+static ssh2_cipher *des_ssh2_new(const struct ssh2_cipheralg *alg)
+{
+ struct des_ssh2_ctx *ctx = snew(struct des_ssh2_ctx);
+ ctx->vt = alg;
+ return &ctx->vt;
+}
+
+static void des3_ssh2_free(ssh2_cipher *cipher)
+{
+ struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
+}
+
+static void des_ssh2_free(ssh2_cipher *cipher)
+{
+ struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
+}
+
+static void des3_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
+{
+ struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+ des_iv(&ctx->contexts[0], iv);
+ /* SSH-2 treats triple-DES as a single block cipher to wrap in
+ * CBC, so there's only one IV required, not three */
+}
+
+static void des3_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+{
+ struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+ des3_key(ctx->contexts, key);
+}
+
+static void des_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
+{
+ struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+ des_iv(&ctx->context, iv);
+}
+
+static void des_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+{
+ struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+ des_key(&ctx->context, key);
+}
+
+static void des3_ssh2_encrypt_blk(ssh2_cipher *cipher, void *blk, int len)
{
- DESContext *keys = (DESContext *) handle;
- des_cbc3_encrypt(blk, len, keys);
+ struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+ des_cbc3_encrypt(blk, len, ctx->contexts);
}
-static void des3_ssh2_decrypt_blk(void *handle, void *blk, int len)
+static void des3_ssh2_decrypt_blk(ssh2_cipher *cipher, void *blk, int len)
{
- DESContext *keys = (DESContext *) handle;
- des_cbc3_decrypt(blk, len, keys);
+ struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+ des_cbc3_decrypt(blk, len, ctx->contexts);
}
-static void des3_ssh2_sdctr(void *handle, void *blk, int len)
+static void des3_ssh2_sdctr(ssh2_cipher *cipher, void *blk, int len)
{
- DESContext *keys = (DESContext *) handle;
- des_sdctr3(blk, len, keys);
+ struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+ des_sdctr3(blk, len, ctx->contexts);
}
-static void des_ssh2_encrypt_blk(void *handle, void *blk, int len)
+static void des_ssh2_encrypt_blk(ssh2_cipher *cipher, void *blk, int len)
{
- DESContext *keys = (DESContext *) handle;
- des_cbc_encrypt(blk, len, keys);
+ struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+ des_cbc_encrypt(blk, len, &ctx->context);
}
-static void des_ssh2_decrypt_blk(void *handle, void *blk, int len)
+static void des_ssh2_decrypt_blk(ssh2_cipher *cipher, void *blk, int len)
{
- DESContext *keys = (DESContext *) handle;
- des_cbc_decrypt(blk, len, keys);
+ struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+ des_cbc_decrypt(blk, len, &ctx->context);
}
void des3_decrypt_pubkey(const void *vkey, void *vblk, int len)
@@ -1004,16 +1050,16 @@ void des_decrypt_xdmauth(const void *keydata, void *blk, int len)
des_cbc_decrypt(blk, len, &dc);
}
-static const struct ssh2_cipher ssh_3des_ssh2 = {
- des3_make_context, des3_free_context, des3_iv, des3_key,
+static const struct ssh2_cipheralg ssh_3des_ssh2 = {
+ des3_ssh2_new, des3_ssh2_free, des3_ssh2_setiv, des3_ssh2_setkey,
des3_ssh2_encrypt_blk, des3_ssh2_decrypt_blk, NULL, NULL,
"3des-cbc",
8, 168, 24, SSH_CIPHER_IS_CBC, "triple-DES CBC",
NULL
};
-static const struct ssh2_cipher ssh_3des_ssh2_ctr = {
- des3_make_context, des3_free_context, des3_iv, des3_key,
+static const struct ssh2_cipheralg ssh_3des_ssh2_ctr = {
+ des3_ssh2_new, des3_ssh2_free, des3_ssh2_setiv, des3_ssh2_setkey,
des3_ssh2_sdctr, des3_ssh2_sdctr, NULL, NULL,
"3des-ctr",
8, 168, 24, 0, "triple-DES SDCTR",
@@ -1028,23 +1074,23 @@ static const struct ssh2_cipher ssh_3des_ssh2_ctr = {
* apparently aren't the only people to do so, so we sigh
* and implement it anyway.
*/
-static const struct ssh2_cipher ssh_des_ssh2 = {
- des_make_context, des3_free_context, des3_iv, des_key,
+static const struct ssh2_cipheralg ssh_des_ssh2 = {
+ des_ssh2_new, des_ssh2_free, des_ssh2_setiv, des_ssh2_setkey,
des_ssh2_encrypt_blk, des_ssh2_decrypt_blk, NULL, NULL,
"des-cbc",
8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC",
NULL
};
-static const struct ssh2_cipher ssh_des_sshcom_ssh2 = {
- des_make_context, des3_free_context, des3_iv, des_key,
+static const struct ssh2_cipheralg ssh_des_sshcom_ssh2 = {
+ des_ssh2_new, des_ssh2_free, des_ssh2_setiv, des_ssh2_setkey,
des_ssh2_encrypt_blk, des_ssh2_decrypt_blk, NULL, NULL,
"des-cbc@ssh.com",
8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC",
NULL
};
-static const struct ssh2_cipher *const des3_list[] = {
+static const struct ssh2_cipheralg *const des3_list[] = {
&ssh_3des_ssh2_ctr,
&ssh_3des_ssh2
};
@@ -1054,7 +1100,7 @@ const struct ssh2_ciphers ssh2_3des = {
des3_list
};
-static const struct ssh2_cipher *const des_list[] = {
+static const struct ssh2_cipheralg *const des_list[] = {
&ssh_des_ssh2,
&ssh_des_sshcom_ssh2
};
diff --git a/sshmd5.c b/sshmd5.c
index 05f00d0b..be3b96cf 100644
--- a/sshmd5.c
+++ b/sshmd5.c
@@ -228,7 +228,7 @@ void MD5Simple(void const *p, unsigned len, unsigned char output[16])
* useful elsewhere (SOCKS5 CHAP authentication uses HMAC-MD5).
*/
-void *hmacmd5_make_context(void *cipher_ctx)
+void *hmacmd5_make_context(ssh2_cipher *cipher)
{
return snewn(3, struct MD5Context);
}
diff --git a/sshsh256.c b/sshsh256.c
index 66752326..51788679 100644
--- a/sshsh256.c
+++ b/sshsh256.c
@@ -256,7 +256,7 @@ const struct ssh_hash ssh_sha256 = {
* HMAC wrapper on it.
*/
-static void *sha256_make_context(void *cipher_ctx)
+static void *sha256_make_context(ssh2_cipher *cipher)
{
return snewn(3, SHA256_State);
}
diff --git a/sshsha.c b/sshsha.c
index 4b9d002f..e2f2a860 100644
--- a/sshsha.c
+++ b/sshsha.c
@@ -283,7 +283,7 @@ const struct ssh_hash ssh_sha1 = {
* HMAC wrapper on it.
*/
-static void *sha1_make_context(void *cipher_ctx)
+static void *sha1_make_context(ssh2_cipher *cipher)
{
return snewn(3, SHA_State);
}
From 853bd8b28410d0469596d0b594e5199fbb3b3b8f Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 13 Sep 2018 16:15:17 +0100
Subject: [PATCH 401/607] Turn SSH-2 MACs into a classoid.
This piece of tidying-up has come out particularly well in terms of
saving tedious repetition and boilerplate. I've managed to remove
three pointless methods from every MAC implementation by means of
writing them once centrally in terms of the implementation-specific
methods; another method (hmacmd5_sink) vanished because I was able to
make the interface type 'ssh2_mac' be directly usable as a BinarySink
by way of a new delegation system; and because all the method
implementations can now find their own vtable, I was even able to
merge a lot of keying and output functions that had previously
differed only in length parameters by having them look up the lengths
in whatever vtable they were passed.
---
Recipe | 1 +
cproxy.c | 4 +-
marshal.h | 11 ++++
ssh.c | 10 ++--
ssh.h | 61 +++++++++++++--------
ssh2bpp.c | 75 ++++++++++++-------------
sshbpp.h | 4 +-
sshccp.c | 95 +++++++++-----------------------
sshmac.c | 42 ++++++++++++++
sshmd5.c | 129 +++++++++++++++++--------------------------
sshsh256.c | 99 ++++++++++++---------------------
sshsha.c | 158 ++++++++++++++++-------------------------------------
12 files changed, 293 insertions(+), 396 deletions(-)
create mode 100644 sshmac.c
diff --git a/Recipe b/Recipe
index 5af98117..b735ac47 100644
--- a/Recipe
+++ b/Recipe
@@ -255,6 +255,7 @@ SSH = ssh ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf
+ sshgssc pgssapi sshshare sshecc aqsync marshal nullplug agentf
+ + sshmac
WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc
+ winhsock errsock
UXSSH = SSH uxnoise uxagentc uxgss uxshare
diff --git a/cproxy.c b/cproxy.c
index 142c2a17..5d5439fa 100644
--- a/cproxy.c
+++ b/cproxy.c
@@ -19,10 +19,10 @@
static void hmacmd5_chap(const unsigned char *challenge, int challen,
const char *passwd, unsigned char *response)
{
- void *hmacmd5_ctx;
+ struct hmacmd5_context *hmacmd5_ctx;
int pwlen;
- hmacmd5_ctx = hmacmd5_make_context(NULL);
+ hmacmd5_ctx = hmacmd5_make_context();
pwlen = strlen(passwd);
if (pwlen>64) {
diff --git a/marshal.h b/marshal.h
index 2aa0301a..631e2465 100644
--- a/marshal.h
+++ b/marshal.h
@@ -25,6 +25,17 @@ struct BinarySink {
((obj)->binarysink_->write = (writefn), \
(obj)->binarysink_->binarysink_ = (obj)->binarysink_)
+/*
+ * To define a larger structure type as a valid BinarySink in such a
+ * way that it will delegate the write method to some other object,
+ * put 'BinarySink_DELEGATE_IMPLEMENTATION' in its declaration, and
+ * when an instance is set up, use 'BinarySink_DELEGATE_INIT' to point
+ * at the object it wants to delegate to.
+ */
+#define BinarySink_DELEGATE_IMPLEMENTATION BinarySink *binarysink_
+#define BinarySink_DELEGATE_INIT(obj, othersink) \
+ ((obj)->binarysink_ = BinarySink_UPCAST(othersink))
+
/*
* The implementing type's write function will want to downcast its
* 'BinarySink *' parameter back to the more specific type. Also,
diff --git a/ssh.c b/ssh.c
index 1ece7488..814d28b7 100644
--- a/ssh.c
+++ b/ssh.c
@@ -328,10 +328,10 @@ const static struct ssh_signkey_with_user_pref_id hostkey_algs[] = {
{ &ssh_rsa, HK_RSA },
};
-const static struct ssh_mac *const macs[] = {
+const static struct ssh2_macalg *const macs[] = {
&ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
};
-const static struct ssh_mac *const buggymacs[] = {
+const static struct ssh2_macalg *const buggymacs[] = {
&ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
};
@@ -4854,7 +4854,7 @@ struct kexinit_algorithm {
int warn;
} cipher;
struct {
- const struct ssh_mac *mac;
+ const struct ssh2_macalg *mac;
int etm;
} mac;
const struct ssh_compress *comp;
@@ -5023,11 +5023,11 @@ static void do_ssh2_transport(void *vctx)
void *our_kexinit;
int our_kexinitlen;
int kex_init_value, kex_reply_value;
- const struct ssh_mac *const *maclist;
+ const struct ssh2_macalg *const *maclist;
int nmacs;
struct {
const struct ssh2_cipheralg *cipher;
- const struct ssh_mac *mac;
+ const struct ssh2_macalg *mac;
int etm_mode;
const struct ssh_compress *comp;
} in, out;
diff --git a/ssh.h b/ssh.h
index a856aa9e..6b768a8a 100644
--- a/ssh.h
+++ b/ssh.h
@@ -374,11 +374,12 @@ void MD5Init(struct MD5Context *context);
void MD5Final(unsigned char digest[16], struct MD5Context *context);
void MD5Simple(void const *p, unsigned len, unsigned char output[16]);
-void *hmacmd5_make_context(ssh2_cipher *);
-void hmacmd5_free_context(void *handle);
-void hmacmd5_key(void *handle, void const *key, int len);
-void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
- unsigned char *hmac);
+struct hmacmd5_context;
+struct hmacmd5_context *hmacmd5_make_context(void);
+void hmacmd5_free_context(struct hmacmd5_context *ctx);
+void hmacmd5_key(struct hmacmd5_context *ctx, void const *key, int len);
+void hmacmd5_do_hmac(struct hmacmd5_context *ctx,
+ unsigned char const *blk, int len, unsigned char *hmac);
int supports_sha_ni(void);
@@ -476,7 +477,7 @@ struct ssh2_cipheralg {
#define SSH_CIPHER_SEPARATE_LENGTH 2
const char *text_name;
/* If set, this takes priority over other MAC. */
- const struct ssh_mac *required_mac;
+ const struct ssh2_macalg *required_mac;
};
#define ssh2_cipher_new(alg) ((alg)->new(alg))
@@ -496,24 +497,36 @@ struct ssh2_ciphers {
const struct ssh2_cipheralg *const *list;
};
-struct ssh_mac {
+struct ssh2_macalg;
+typedef struct ssh2_mac {
+ const struct ssh2_macalg *vt;
+ BinarySink_DELEGATE_IMPLEMENTATION;
+} ssh2_mac;
+
+struct ssh2_macalg {
/* Passes in the cipher context */
- void *(*make_context)(ssh2_cipher *);
- void (*free_context)(void *);
- void (*setkey) (void *, const void *key);
- /* whole-packet operations */
- void (*generate) (void *, void *blk, int len, unsigned long seq);
- int (*verify) (void *, const void *blk, int len, unsigned long seq);
- /* partial-packet operations */
- void (*start) (void *);
- BinarySink *(*sink) (void *);
- void (*genresult) (void *, unsigned char *);
- int (*verresult) (void *, unsigned char const *);
+ ssh2_mac *(*new)(const struct ssh2_macalg *alg, ssh2_cipher *cipher);
+ void (*free)(ssh2_mac *);
+ void (*setkey)(ssh2_mac *, const void *key);
+ void (*start)(ssh2_mac *);
+ void (*genresult)(ssh2_mac *, unsigned char *);
const char *name, *etm_name;
int len, keylen;
const char *text_name;
};
+#define ssh2_mac_new(alg, cipher) ((alg)->new(alg, cipher))
+#define ssh2_mac_free(ctx) ((ctx)->vt->free(ctx))
+#define ssh2_mac_setkey(ctx, key) ((ctx)->vt->free(ctx, key))
+#define ssh2_mac_start(ctx) ((ctx)->vt->start(ctx))
+#define ssh2_mac_genresult(ctx, out) ((ctx)->vt->genresult(ctx, out))
+#define ssh2_mac_alg(ctx) ((ctx)->vt)
+
+/* Centralised 'methods' for ssh2_mac, defined in sshmac.c */
+int ssh2_mac_verresult(ssh2_mac *, const void *);
+void ssh2_mac_generate(ssh2_mac *, void *, int, unsigned long seq);
+int ssh2_mac_verify(ssh2_mac *, const void *, int, unsigned long seq);
+
struct ssh_hash {
void *(*init)(void); /* also allocates context */
void *(*copy)(const void *);
@@ -628,12 +641,12 @@ extern const ssh_keyalg ssh_ecdsa_ed25519;
extern const ssh_keyalg ssh_ecdsa_nistp256;
extern const ssh_keyalg ssh_ecdsa_nistp384;
extern const ssh_keyalg ssh_ecdsa_nistp521;
-extern const struct ssh_mac ssh_hmac_md5;
-extern const struct ssh_mac ssh_hmac_sha1;
-extern const struct ssh_mac ssh_hmac_sha1_buggy;
-extern const struct ssh_mac ssh_hmac_sha1_96;
-extern const struct ssh_mac ssh_hmac_sha1_96_buggy;
-extern const struct ssh_mac ssh_hmac_sha256;
+extern const struct ssh2_macalg ssh_hmac_md5;
+extern const struct ssh2_macalg ssh_hmac_sha1;
+extern const struct ssh2_macalg ssh_hmac_sha1_buggy;
+extern const struct ssh2_macalg ssh_hmac_sha1_96;
+extern const struct ssh2_macalg ssh_hmac_sha1_96_buggy;
+extern const struct ssh2_macalg ssh_hmac_sha256;
typedef struct AESContext AESContext;
AESContext *aes_make_context(void);
diff --git a/ssh2bpp.c b/ssh2bpp.c
index 510e21a5..ff957b05 100644
--- a/ssh2bpp.c
+++ b/ssh2bpp.c
@@ -12,9 +12,8 @@
struct ssh2_bpp_direction {
unsigned long sequence;
ssh2_cipher *cipher;
- const struct ssh_mac *mac;
+ ssh2_mac *mac;
int etm_mode;
- void *mac_ctx;
const struct ssh_compress *comp;
void *comp_ctx;
};
@@ -27,7 +26,6 @@ struct ssh2_bpp_state {
unsigned char *data;
unsigned cipherblk;
PktIn *pktin;
- BinarySink *sc_mac_bs;
struct ssh2_bpp_direction in, out;
int pending_newkeys;
@@ -61,14 +59,14 @@ static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
sfree(s->buf);
if (s->out.cipher)
ssh2_cipher_free(s->out.cipher);
- if (s->out.mac_ctx)
- s->out.mac->free_context(s->out.mac_ctx);
+ if (s->out.mac)
+ ssh2_mac_free(s->out.mac);
if (s->out.comp_ctx)
s->out.comp->compress_cleanup(s->out.comp_ctx);
if (s->in.cipher)
ssh2_cipher_free(s->in.cipher);
- if (s->in.mac_ctx)
- s->in.mac->free_context(s->in.mac_ctx);
+ if (s->in.mac)
+ ssh2_mac_free(s->in.mac);
if (s->in.comp_ctx)
s->in.comp->decompress_cleanup(s->in.comp_ctx);
if (s->pktin)
@@ -79,7 +77,7 @@ static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
void ssh2_bpp_new_outgoing_crypto(
BinaryPacketProtocol *bpp,
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
- const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+ const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
const struct ssh_compress *compression)
{
struct ssh2_bpp_state *s;
@@ -88,8 +86,8 @@ void ssh2_bpp_new_outgoing_crypto(
if (s->out.cipher)
ssh2_cipher_free(s->out.cipher);
- if (s->out.mac_ctx)
- s->out.mac->free_context(s->out.mac_ctx);
+ if (s->out.mac)
+ ssh2_mac_free(s->out.mac);
if (s->out.comp_ctx)
s->out.comp->compress_cleanup(s->out.comp_ctx);
@@ -100,11 +98,12 @@ void ssh2_bpp_new_outgoing_crypto(
} else {
s->out.cipher = NULL;
}
- s->out.mac = mac;
s->out.etm_mode = etm_mode;
if (mac) {
- s->out.mac_ctx = mac->make_context(s->out.cipher);
- mac->setkey(s->out.mac_ctx, mac_key);
+ s->out.mac = ssh2_mac_new(mac, s->out.cipher);
+ mac->setkey(s->out.mac, mac_key);
+ } else {
+ s->out.mac = NULL;
}
s->out.comp = compression;
@@ -117,7 +116,7 @@ void ssh2_bpp_new_outgoing_crypto(
void ssh2_bpp_new_incoming_crypto(
BinaryPacketProtocol *bpp,
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
- const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+ const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
const struct ssh_compress *compression)
{
struct ssh2_bpp_state *s;
@@ -126,8 +125,8 @@ void ssh2_bpp_new_incoming_crypto(
if (s->in.cipher)
ssh2_cipher_free(s->in.cipher);
- if (s->in.mac_ctx)
- s->in.mac->free_context(s->in.mac_ctx);
+ if (s->in.mac)
+ ssh2_mac_free(s->in.mac);
if (s->in.comp_ctx)
s->in.comp->decompress_cleanup(s->in.comp_ctx);
@@ -138,11 +137,12 @@ void ssh2_bpp_new_incoming_crypto(
} else {
s->in.cipher = NULL;
}
- s->in.mac = mac;
s->in.etm_mode = etm_mode;
if (mac) {
- s->in.mac_ctx = mac->make_context(s->in.cipher);
- mac->setkey(s->in.mac_ctx, mac_key);
+ s->in.mac = ssh2_mac_new(mac, s->in.cipher);
+ mac->setkey(s->in.mac, mac_key);
+ } else {
+ s->in.mac = NULL;
}
s->in.comp = compression;
@@ -171,7 +171,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
s->cipherblk = 8;
if (s->cipherblk < 8)
s->cipherblk = 8;
- s->maclen = s->in.mac ? s->in.mac->len : 0;
+ s->maclen = s->in.mac ? ssh2_mac_alg(s->in.mac)->len : 0;
if (s->in.cipher &&
(ssh2_cipher_alg(s->in.cipher)->flags & SSH_CIPHER_IS_CBC) &&
@@ -208,9 +208,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
s->bpp.in_raw, s->buf, s->maclen));
s->packetlen = 0;
- s->in.mac->start(s->in.mac_ctx);
- s->sc_mac_bs = s->in.mac->sink(s->in.mac_ctx);
- put_uint32(s->sc_mac_bs, s->in.sequence);
+ ssh2_mac_start(s->in.mac);
+ put_uint32(s->in.mac, s->in.sequence);
for (;;) { /* Once around this loop per cipher block. */
/* Read another cipher-block's worth, and tack it on to
@@ -225,13 +224,12 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
s->buf + s->packetlen, s->cipherblk);
/* Feed that block to the MAC. */
- put_data(s->sc_mac_bs,
+ put_data(s->in.mac,
s->buf + s->packetlen, s->cipherblk);
s->packetlen += s->cipherblk;
/* See if that gives us a valid packet. */
- if (s->in.mac->verresult(
- s->in.mac_ctx, s->buf + s->packetlen) &&
+ if (ssh2_mac_verresult(s->in.mac, s->buf + s->packetlen) &&
((s->len = toint(GET_32BIT(s->buf))) ==
s->packetlen-4))
break;
@@ -314,8 +312,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
/*
* Check the MAC.
*/
- if (s->in.mac && !s->in.mac->verify(
- s->in.mac_ctx, s->data, s->len + 4, s->in.sequence)) {
+ if (s->in.mac && !ssh2_mac_verify(
+ s->in.mac, s->data, s->len + 4, s->in.sequence)) {
s->bpp.error = dupprintf("Incorrect MAC received on packet");
crStopV;
}
@@ -389,8 +387,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
/*
* Check the MAC.
*/
- if (s->in.mac && !s->in.mac->verify(
- s->in.mac_ctx, s->data, s->len + 4, s->in.sequence)) {
+ if (s->in.mac && !ssh2_mac_verify(
+ s->in.mac, s->data, s->len + 4, s->in.sequence)) {
s->bpp.error = dupprintf("Incorrect MAC received on packet");
crStopV;
}
@@ -542,7 +540,7 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
* make the overall packet length come to pkt->minlen.
*/
if (s->out.mac)
- minlen -= s->out.mac->len;
+ minlen -= ssh2_mac_alg(s->out.mac)->len;
minlen -= 8; /* length field + min padding */
}
@@ -565,7 +563,7 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
(cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
% cipherblk;
assert(padding <= 255);
- maclen = s->out.mac ? s->out.mac->len : 0;
+ maclen = s->out.mac ? ssh2_mac_alg(s->out.mac)->len : 0;
origlen = pkt->length;
for (i = 0; i < padding; i++)
put_byte(pkt, random_byte());
@@ -588,16 +586,15 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
if (s->out.cipher)
ssh2_cipher_encrypt(s->out.cipher,
pkt->data + 4, origlen + padding - 4);
- s->out.mac->generate(s->out.mac_ctx, pkt->data, origlen + padding,
- s->out.sequence);
+ ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding,
+ s->out.sequence);
} else {
/*
* SSH-2 standard protocol.
*/
if (s->out.mac)
- s->out.mac->generate(
- s->out.mac_ctx, pkt->data, origlen + padding,
- s->out.sequence);
+ ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding,
+ s->out.sequence);
if (s->out.cipher)
ssh2_cipher_encrypt(s->out.cipher, pkt->data, origlen + padding);
}
@@ -642,7 +639,7 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
length += block-1;
length -= (length % block);
if (s->out.mac)
- length += s->out.mac->len;
+ length += ssh2_mac_alg(s->out.mac)->len;
if (length < pkt->minlen) {
/*
@@ -655,7 +652,7 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
* contained string.
*/
if (s->out.mac)
- length -= s->out.mac->len;
+ length -= ssh2_mac_alg(s->out.mac)->len;
length -= 8; /* length field + min padding */
length -= 5; /* type code + string length prefix */
diff --git a/sshbpp.h b/sshbpp.h
index 41683410..f7e2fe2c 100644
--- a/sshbpp.h
+++ b/sshbpp.h
@@ -40,12 +40,12 @@ BinaryPacketProtocol *ssh2_bpp_new(void);
void ssh2_bpp_new_outgoing_crypto(
BinaryPacketProtocol *bpp,
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
- const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+ const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
const struct ssh_compress *compression);
void ssh2_bpp_new_incoming_crypto(
BinaryPacketProtocol *bpp,
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
- const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+ const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
const struct ssh_compress *compression);
BinaryPacketProtocol *ssh2_bare_bpp_new(void);
diff --git a/sshccp.c b/sshccp.c
index 9919ced2..dfcb02c8 100644
--- a/sshccp.c
+++ b/sshccp.c
@@ -866,27 +866,32 @@ struct ccp_context {
struct poly1305 mac;
BinarySink_IMPLEMENTATION;
- ssh2_cipher vt;
+ ssh2_cipher cvt;
+ ssh2_mac mac_if;
};
-static void *poly_make_context(ssh2_cipher *cipher)
+static ssh2_mac *poly_ssh2_new(
+ const struct ssh2_macalg *alg, ssh2_cipher *cipher)
{
- return FROMFIELD(cipher, struct ccp_context, vt);
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
+ ctx->mac_if.vt = alg;
+ BinarySink_DELEGATE_INIT(&ctx->mac_if, ctx);
+ return &ctx->mac_if;
}
-static void poly_free_context(void *ctx)
+static void poly_ssh2_free(ssh2_mac *mac)
{
/* Not allocated, just forwarded, no need to free */
}
-static void poly_setkey(void *ctx, const void *key)
+static void poly_setkey(ssh2_mac *mac, const void *key)
{
/* Uses the same context as ChaCha20, so ignore */
}
-static void poly_start(void *handle)
+static void poly_start(ssh2_mac *mac)
{
- struct ccp_context *ctx = (struct ccp_context *)handle;
+ struct ccp_context *ctx = FROMFIELD(mac, struct ccp_context, mac_if);
ctx->mac_initialised = 0;
memset(ctx->mac_iv, 0, 8);
@@ -926,63 +931,15 @@ static void poly_BinarySink_write(BinarySink *bs, const void *blkv, size_t len)
}
}
-static BinarySink *poly_sink(void *handle)
+static void poly_genresult(ssh2_mac *mac, unsigned char *blk)
{
- struct ccp_context *ctx = (struct ccp_context *)handle;
- return BinarySink_UPCAST(ctx);
-}
-
-static void poly_genresult(void *handle, unsigned char *blk)
-{
- struct ccp_context *ctx = (struct ccp_context *)handle;
+ struct ccp_context *ctx = FROMFIELD(mac, struct ccp_context, mac_if);
poly1305_finalise(&ctx->mac, blk);
}
-static int poly_verresult(void *handle, unsigned char const *blk)
-{
- struct ccp_context *ctx = (struct ccp_context *)handle;
- int res;
- unsigned char mac[16];
- poly1305_finalise(&ctx->mac, mac);
- res = smemeq(blk, mac, 16);
- return res;
-}
-
-/* The generic poly operation used before generate and verify */
-static void poly_op(void *handle, const unsigned char *blk, int len,
- unsigned long seq)
-{
- struct ccp_context *ctx = (struct ccp_context *)handle;
- poly_start(ctx);
- /* the data receiver expects the first 4 bytes to be the IV */
- put_uint32(ctx, seq);
- put_data(ctx, blk, len);
-}
-
-static void poly_generate(void *handle, void *vblk, int len, unsigned long seq)
-{
- unsigned char *blk = (unsigned char *)vblk;
- poly_op(handle, blk, len, seq);
- poly_genresult(handle, blk+len);
-}
-
-static int poly_verify(void *handle, const void *vblk, int len,
- unsigned long seq)
-{
- const unsigned char *blk = (const unsigned char *)vblk;
- poly_op(handle, blk, len, seq);
- return poly_verresult(handle, blk+len);
-}
-
-static const struct ssh_mac ssh2_poly1305 = {
- poly_make_context, poly_free_context,
- poly_setkey,
-
- /* whole-packet operations */
- poly_generate, poly_verify,
-
- /* partial-packet operations */
- poly_start, poly_sink, poly_genresult, poly_verresult,
+static const struct ssh2_macalg ssh2_poly1305 = {
+ poly_ssh2_new, poly_ssh2_free, poly_setkey,
+ poly_start, poly_genresult,
"", "", /* Not selectable individually, just part of ChaCha20-Poly1305 */
16, 0, "Poly1305"
@@ -993,13 +950,13 @@ static ssh2_cipher *ccp_new(const struct ssh2_cipheralg *alg)
struct ccp_context *ctx = snew(struct ccp_context);
BinarySink_INIT(ctx, poly_BinarySink_write);
poly1305_init(&ctx->mac);
- ctx->vt = alg;
- return &ctx->vt;
+ ctx->cvt = alg;
+ return &ctx->cvt;
}
static void ccp_free(ssh2_cipher *cipher)
{
- struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
smemclr(&ctx->a_cipher, sizeof(ctx->a_cipher));
smemclr(&ctx->b_cipher, sizeof(ctx->b_cipher));
smemclr(&ctx->mac, sizeof(ctx->mac));
@@ -1008,14 +965,14 @@ static void ccp_free(ssh2_cipher *cipher)
static void ccp_iv(ssh2_cipher *cipher, const void *iv)
{
- /* struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt); */
+ /* struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt); */
/* IV is set based on the sequence number */
}
static void ccp_key(ssh2_cipher *cipher, const void *vkey)
{
const unsigned char *key = (const unsigned char *)vkey;
- struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
/* Initialise the a_cipher (for decrypting lengths) with the first 256 bits */
chacha20_key(&ctx->a_cipher, key + 32);
/* Initialise the b_cipher (for content and MAC) with the second 256 bits */
@@ -1024,13 +981,13 @@ static void ccp_key(ssh2_cipher *cipher, const void *vkey)
static void ccp_encrypt(ssh2_cipher *cipher, void *blk, int len)
{
- struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
chacha20_encrypt(&ctx->b_cipher, blk, len);
}
static void ccp_decrypt(ssh2_cipher *cipher, void *blk, int len)
{
- struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
chacha20_decrypt(&ctx->b_cipher, blk, len);
}
@@ -1054,7 +1011,7 @@ static void ccp_length_op(struct ccp_context *ctx, void *blk, int len,
static void ccp_encrypt_length(ssh2_cipher *cipher, void *blk, int len,
unsigned long seq)
{
- struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
ccp_length_op(ctx, blk, len, seq);
chacha20_encrypt(&ctx->a_cipher, blk, len);
}
@@ -1062,7 +1019,7 @@ static void ccp_encrypt_length(ssh2_cipher *cipher, void *blk, int len,
static void ccp_decrypt_length(ssh2_cipher *cipher, void *blk, int len,
unsigned long seq)
{
- struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, vt);
+ struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
ccp_length_op(ctx, blk, len, seq);
chacha20_decrypt(&ctx->a_cipher, blk, len);
}
diff --git a/sshmac.c b/sshmac.c
new file mode 100644
index 00000000..e3b74b38
--- /dev/null
+++ b/sshmac.c
@@ -0,0 +1,42 @@
+/*
+ * Centralised parts of the SSH-2 MAC API, which don't need to vary
+ * with the MAC implementation.
+ */
+
+#include
+
+#include "ssh.h"
+
+int ssh2_mac_verresult(ssh2_mac *mac, const void *candidate)
+{
+ unsigned char correct[64]; /* at least as big as all known MACs */
+ int toret;
+
+ assert(mac->vt->len <= sizeof(correct));
+ ssh2_mac_genresult(mac, correct);
+ toret = smemeq(correct, candidate, mac->vt->len);
+
+ smemclr(correct, sizeof(correct));
+
+ return toret;
+}
+
+static void ssh2_mac_prepare(ssh2_mac *mac, const void *blk, int len,
+ unsigned long seq)
+{
+ ssh2_mac_start(mac);
+ put_uint32(mac, seq);
+ put_data(mac, blk, len);
+}
+
+void ssh2_mac_generate(ssh2_mac *mac, void *blk, int len, unsigned long seq)
+{
+ ssh2_mac_prepare(mac, blk, len, seq);
+ return ssh2_mac_genresult(mac, (unsigned char *)blk + len);
+}
+
+int ssh2_mac_verify(ssh2_mac *mac, const void *blk, int len, unsigned long seq)
+{
+ ssh2_mac_prepare(mac, blk, len, seq);
+ return ssh2_mac_verresult(mac, (const unsigned char *)blk + len);
+}
diff --git a/sshmd5.c b/sshmd5.c
index be3b96cf..87a224d9 100644
--- a/sshmd5.c
+++ b/sshmd5.c
@@ -228,20 +228,40 @@ void MD5Simple(void const *p, unsigned len, unsigned char output[16])
* useful elsewhere (SOCKS5 CHAP authentication uses HMAC-MD5).
*/
-void *hmacmd5_make_context(ssh2_cipher *cipher)
+struct hmacmd5_context {
+ struct MD5Context md5[3];
+ ssh2_mac mac;
+};
+
+struct hmacmd5_context *hmacmd5_make_context(void)
+{
+ struct hmacmd5_context *ctx = snew(struct hmacmd5_context);
+ BinarySink_DELEGATE_INIT(&ctx->mac, &ctx->md5[2]);
+ return ctx;
+}
+
+static ssh2_mac *hmacmd5_ssh2_new(const struct ssh2_macalg *alg,
+ ssh2_cipher *cipher)
+{
+ struct hmacmd5_context *ctx = hmacmd5_make_context();
+ ctx->mac.vt = alg;
+ return &ctx->mac;
+}
+
+void hmacmd5_free_context(struct hmacmd5_context *ctx)
{
- return snewn(3, struct MD5Context);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
}
-void hmacmd5_free_context(void *handle)
+static void hmacmd5_ssh2_free(ssh2_mac *mac)
{
- smemclr(handle, 3*sizeof(struct MD5Context));
- sfree(handle);
+ struct hmacmd5_context *ctx = FROMFIELD(mac, struct hmacmd5_context, mac);
+ hmacmd5_free_context(ctx);
}
-void hmacmd5_key(void *handle, void const *keyv, int len)
+void hmacmd5_key(struct hmacmd5_context *ctx, void const *keyv, int len)
{
- struct MD5Context *keys = (struct MD5Context *)handle;
unsigned char foo[64];
unsigned char const *key = (unsigned char const *)keyv;
int i;
@@ -249,106 +269,59 @@ void hmacmd5_key(void *handle, void const *keyv, int len)
memset(foo, 0x36, 64);
for (i = 0; i < len && i < 64; i++)
foo[i] ^= key[i];
- MD5Init(&keys[0]);
- put_data(&keys[0], foo, 64);
+ MD5Init(&ctx->md5[0]);
+ put_data(&ctx->md5[0], foo, 64);
memset(foo, 0x5C, 64);
for (i = 0; i < len && i < 64; i++)
foo[i] ^= key[i];
- MD5Init(&keys[1]);
- put_data(&keys[1], foo, 64);
+ MD5Init(&ctx->md5[1]);
+ put_data(&ctx->md5[1], foo, 64);
smemclr(foo, 64); /* burn the evidence */
}
-static void hmacmd5_key_16(void *handle, const void *key)
+static void hmacmd5_ssh2_setkey(ssh2_mac *mac, const void *key)
{
- hmacmd5_key(handle, key, 16);
+ struct hmacmd5_context *ctx = FROMFIELD(mac, struct hmacmd5_context, mac);
+ hmacmd5_key(ctx, key, ctx->mac.vt->keylen);
}
-static void hmacmd5_start(void *handle)
+static void hmacmd5_start(ssh2_mac *mac)
{
- struct MD5Context *keys = (struct MD5Context *)handle;
-
- keys[2] = keys[0]; /* structure copy */
- BinarySink_COPIED(&keys[2]);
-}
+ struct hmacmd5_context *ctx = FROMFIELD(mac, struct hmacmd5_context, mac);
-static BinarySink *hmacmd5_sink(void *handle)
-{
- struct MD5Context *keys = (struct MD5Context *)handle;
- return BinarySink_UPCAST(&keys[2]);
+ ctx->md5[2] = ctx->md5[0]; /* structure copy */
+ BinarySink_COPIED(&ctx->md5[2]);
}
-static void hmacmd5_genresult(void *handle, unsigned char *hmac)
+static void hmacmd5_genresult(ssh2_mac *mac, unsigned char *hmac)
{
- struct MD5Context *keys = (struct MD5Context *)handle;
+ struct hmacmd5_context *ctx = FROMFIELD(mac, struct hmacmd5_context, mac);
struct MD5Context s;
unsigned char intermediate[16];
- s = keys[2]; /* structure copy */
+ s = ctx->md5[2]; /* structure copy */
BinarySink_COPIED(&s);
MD5Final(intermediate, &s);
- s = keys[1]; /* structure copy */
+ s = ctx->md5[1]; /* structure copy */
BinarySink_COPIED(&s);
put_data(&s, intermediate, 16);
MD5Final(hmac, &s);
+ smemclr(intermediate, sizeof(intermediate));
}
-static int hmacmd5_verresult(void *handle, unsigned char const *hmac)
-{
- unsigned char correct[16];
- hmacmd5_genresult(handle, correct);
- return smemeq(correct, hmac, 16);
-}
-
-static void hmacmd5_do_hmac_internal(void *handle,
- unsigned char const *blk, int len,
- unsigned char const *blk2, int len2,
- unsigned char *hmac)
-{
- BinarySink *bs = hmacmd5_sink(handle);
- hmacmd5_start(handle);
- put_data(bs, blk, len);
- if (blk2) put_data(bs, blk2, len2);
- hmacmd5_genresult(handle, hmac);
-}
-
-void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
- unsigned char *hmac)
-{
- hmacmd5_do_hmac_internal(handle, blk, len, NULL, 0, hmac);
-}
-
-static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len,
- unsigned long seq, unsigned char *hmac)
-{
- unsigned char seqbuf[16];
-
- PUT_32BIT_MSB_FIRST(seqbuf, seq);
- hmacmd5_do_hmac_internal(handle, seqbuf, 4, blk, len, hmac);
-}
-
-static void hmacmd5_generate(void *handle, void *vblk, int len,
- unsigned long seq)
-{
- unsigned char *blk = (unsigned char *)vblk;
- hmacmd5_do_hmac_ssh(handle, blk, len, seq, blk + len);
-}
-
-static int hmacmd5_verify(void *handle, const void *vblk, int len,
- unsigned long seq)
+void hmacmd5_do_hmac(struct hmacmd5_context *ctx,
+ unsigned char const *blk, int len, unsigned char *hmac)
{
- const unsigned char *blk = (const unsigned char *)vblk;
- unsigned char correct[16];
- hmacmd5_do_hmac_ssh(handle, blk, len, seq, correct);
- return smemeq(correct, blk + len, 16);
+ ssh2_mac_start(&ctx->mac);
+ put_data(&ctx->mac, blk, len);
+ return ssh2_mac_genresult(&ctx->mac, hmac);
}
-const struct ssh_mac ssh_hmac_md5 = {
- hmacmd5_make_context, hmacmd5_free_context, hmacmd5_key_16,
- hmacmd5_generate, hmacmd5_verify,
- hmacmd5_start, hmacmd5_sink, hmacmd5_genresult, hmacmd5_verresult,
+const struct ssh2_macalg ssh_hmac_md5 = {
+ hmacmd5_ssh2_new, hmacmd5_ssh2_free, hmacmd5_ssh2_setkey,
+ hmacmd5_start, hmacmd5_genresult,
"hmac-md5", "hmac-md5-etm@openssh.com",
16, 16,
"HMAC-MD5"
diff --git a/sshsh256.c b/sshsh256.c
index 51788679..e4b14fd7 100644
--- a/sshsh256.c
+++ b/sshsh256.c
@@ -256,111 +256,80 @@ const struct ssh_hash ssh_sha256 = {
* HMAC wrapper on it.
*/
-static void *sha256_make_context(ssh2_cipher *cipher)
+struct hmacsha256 {
+ SHA256_State sha[3];
+ ssh2_mac mac;
+};
+
+static ssh2_mac *hmacsha256_new(
+ const struct ssh2_macalg *alg, ssh2_cipher *cipher)
{
- return snewn(3, SHA256_State);
+ struct hmacsha256 *ctx = snew(struct hmacsha256);
+ ctx->mac.vt = alg;
+ BinarySink_DELEGATE_INIT(&ctx->mac, &ctx->sha[2]);
+ return &ctx->mac;
}
-static void sha256_free_context(void *handle)
+static void hmacsha256_free(ssh2_mac *mac)
{
- smemclr(handle, 3 * sizeof(SHA256_State));
- sfree(handle);
+ struct hmacsha256 *ctx = FROMFIELD(mac, struct hmacsha256, mac);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
}
-static void sha256_key_internal(void *handle,
+static void sha256_key_internal(struct hmacsha256 *ctx,
const unsigned char *key, int len)
{
- SHA256_State *keys = (SHA256_State *)handle;
unsigned char foo[64];
int i;
memset(foo, 0x36, 64);
for (i = 0; i < len && i < 64; i++)
foo[i] ^= key[i];
- SHA256_Init(&keys[0]);
- put_data(&keys[0], foo, 64);
+ SHA256_Init(&ctx->sha[0]);
+ put_data(&ctx->sha[0], foo, 64);
memset(foo, 0x5C, 64);
for (i = 0; i < len && i < 64; i++)
foo[i] ^= key[i];
- SHA256_Init(&keys[1]);
- put_data(&keys[1], foo, 64);
+ SHA256_Init(&ctx->sha[1]);
+ put_data(&ctx->sha[1], foo, 64);
smemclr(foo, 64); /* burn the evidence */
}
-static void sha256_key(void *handle, const void *key)
+static void hmacsha256_key(ssh2_mac *mac, const void *key)
{
- sha256_key_internal(handle, key, 32);
+ struct hmacsha256 *ctx = FROMFIELD(mac, struct hmacsha256, mac);
+ sha256_key_internal(ctx, key, ctx->mac.vt->keylen);
}
-static void hmacsha256_start(void *handle)
+static void hmacsha256_start(ssh2_mac *mac)
{
- SHA256_State *keys = (SHA256_State *)handle;
+ struct hmacsha256 *ctx = FROMFIELD(mac, struct hmacsha256, mac);
- keys[2] = keys[0]; /* structure copy */
- BinarySink_COPIED(&keys[2]);
-}
-
-static BinarySink *hmacsha256_sink(void *handle)
-{
- SHA256_State *keys = (SHA256_State *)handle;
- return BinarySink_UPCAST(&keys[2]);
+ ctx->sha[2] = ctx->sha[0]; /* structure copy */
+ BinarySink_COPIED(&ctx->sha[2]);
}
-static void hmacsha256_genresult(void *handle, unsigned char *hmac)
+static void hmacsha256_genresult(ssh2_mac *mac, unsigned char *hmac)
{
- SHA256_State *keys = (SHA256_State *)handle;
+ struct hmacsha256 *ctx = FROMFIELD(mac, struct hmacsha256, mac);
SHA256_State s;
unsigned char intermediate[32];
- s = keys[2]; /* structure copy */
+ s = ctx->sha[2]; /* structure copy */
BinarySink_COPIED(&s);
SHA256_Final(&s, intermediate);
- s = keys[1]; /* structure copy */
+ s = ctx->sha[1]; /* structure copy */
BinarySink_COPIED(&s);
put_data(&s, intermediate, 32);
SHA256_Final(&s, hmac);
}
-static void sha256_do_hmac(void *handle, const unsigned char *blk, int len,
- unsigned long seq, unsigned char *hmac)
-{
- BinarySink *bs = hmacsha256_sink(handle);
- hmacsha256_start(handle);
- put_uint32(bs, seq);
- put_data(bs, blk, len);
- hmacsha256_genresult(handle, hmac);
-}
-
-static void sha256_generate(void *handle, void *vblk, int len,
- unsigned long seq)
-{
- unsigned char *blk = (unsigned char *)vblk;
- sha256_do_hmac(handle, blk, len, seq, blk + len);
-}
-
-static int hmacsha256_verresult(void *handle, unsigned char const *hmac)
-{
- unsigned char correct[32];
- hmacsha256_genresult(handle, correct);
- return smemeq(correct, hmac, 32);
-}
-
-static int sha256_verify(void *handle, const void *vblk, int len,
- unsigned long seq)
-{
- const unsigned char *blk = (const unsigned char *)vblk;
- unsigned char correct[32];
- sha256_do_hmac(handle, blk, len, seq, correct);
- return smemeq(correct, blk + len, 32);
-}
-
-const struct ssh_mac ssh_hmac_sha256 = {
- sha256_make_context, sha256_free_context, sha256_key,
- sha256_generate, sha256_verify,
- hmacsha256_start, hmacsha256_sink,
- hmacsha256_genresult, hmacsha256_verresult,
+const struct ssh2_macalg ssh_hmac_sha256 = {
+ hmacsha256_new, hmacsha256_free, hmacsha256_key,
+ hmacsha256_start, hmacsha256_genresult,
"hmac-sha2-256", "hmac-sha2-256-etm@openssh.com",
32, 32,
"HMAC-SHA-256"
diff --git a/sshsha.c b/sshsha.c
index e2f2a860..310241a4 100644
--- a/sshsha.c
+++ b/sshsha.c
@@ -283,20 +283,30 @@ const struct ssh_hash ssh_sha1 = {
* HMAC wrapper on it.
*/
-static void *sha1_make_context(ssh2_cipher *cipher)
+struct hmacsha1 {
+ SHA_State sha[3];
+ ssh2_mac mac;
+};
+
+static ssh2_mac *hmacsha1_new(
+ const struct ssh2_macalg *alg, ssh2_cipher *cipher)
{
- return snewn(3, SHA_State);
+ struct hmacsha1 *ctx = snew(struct hmacsha1);
+ ctx->mac.vt = alg;
+ BinarySink_DELEGATE_INIT(&ctx->mac, &ctx->sha[2]);
+ return &ctx->mac;
}
-static void sha1_free_context(void *handle)
+static void hmacsha1_free(ssh2_mac *mac)
{
- smemclr(handle, 3 * sizeof(SHA_State));
- sfree(handle);
+ struct hmacsha1 *ctx = FROMFIELD(mac, struct hmacsha1, mac);
+ smemclr(ctx, sizeof(*ctx));
+ sfree(ctx);
}
-static void sha1_key_internal(void *handle, const unsigned char *key, int len)
+static void sha1_key_internal(SHA_State *keys,
+ const unsigned char *key, int len)
{
- SHA_State *keys = (SHA_State *)handle;
unsigned char foo[64];
int i;
@@ -315,108 +325,38 @@ static void sha1_key_internal(void *handle, const unsigned char *key, int len)
smemclr(foo, 64); /* burn the evidence */
}
-static void sha1_key(void *handle, const void *key)
-{
- sha1_key_internal(handle, key, 20);
-}
-
-static void sha1_key_buggy(void *handle, const void *key)
+static void hmacsha1_key(ssh2_mac *mac, const void *key)
{
- sha1_key_internal(handle, key, 16);
+ struct hmacsha1 *ctx = FROMFIELD(mac, struct hmacsha1, mac);
+ /* Reading the key length out of the ssh2_macalg structure means
+ * this same method can be used for the _buggy variants which use
+ * a shorter key */
+ sha1_key_internal(ctx->sha, key, ctx->mac.vt->keylen);
}
-static void hmacsha1_start(void *handle)
+static void hmacsha1_start(ssh2_mac *mac)
{
- SHA_State *keys = (SHA_State *)handle;
+ struct hmacsha1 *ctx = FROMFIELD(mac, struct hmacsha1, mac);
- keys[2] = keys[0]; /* structure copy */
- BinarySink_COPIED(&keys[2]);
+ ctx->sha[2] = ctx->sha[0]; /* structure copy */
+ BinarySink_COPIED(&ctx->sha[2]);
}
-static BinarySink *hmacsha1_sink(void *handle)
+static void hmacsha1_genresult(ssh2_mac *mac, unsigned char *hmac)
{
- SHA_State *keys = (SHA_State *)handle;
- return BinarySink_UPCAST(&keys[2]);
-}
-
-static void hmacsha1_genresult(void *handle, unsigned char *hmac)
-{
- SHA_State *keys = (SHA_State *)handle;
+ struct hmacsha1 *ctx = FROMFIELD(mac, struct hmacsha1, mac);
SHA_State s;
unsigned char intermediate[20];
- s = keys[2]; /* structure copy */
+ s = ctx->sha[2]; /* structure copy */
BinarySink_COPIED(&s);
SHA_Final(&s, intermediate);
- s = keys[1]; /* structure copy */
+ s = ctx->sha[1]; /* structure copy */
BinarySink_COPIED(&s);
put_data(&s, intermediate, 20);
- SHA_Final(&s, hmac);
-}
-
-static void sha1_do_hmac(void *handle, const unsigned char *blk, int len,
- unsigned long seq, unsigned char *hmac)
-{
- BinarySink *bs = hmacsha1_sink(handle);
- hmacsha1_start(handle);
- put_uint32(bs, seq);
- put_data(bs, blk, len);
- hmacsha1_genresult(handle, hmac);
-}
-
-static void sha1_generate(void *handle, void *vblk, int len,
- unsigned long seq)
-{
- unsigned char *blk = (unsigned char *)vblk;
- sha1_do_hmac(handle, blk, len, seq, blk + len);
-}
-
-static int hmacsha1_verresult(void *handle, unsigned char const *hmac)
-{
- unsigned char correct[20];
- hmacsha1_genresult(handle, correct);
- return smemeq(correct, hmac, 20);
-}
-
-static int sha1_verify(void *handle, const void *vblk, int len,
- unsigned long seq)
-{
- const unsigned char *blk = (const unsigned char *)vblk;
- unsigned char correct[20];
- sha1_do_hmac(handle, blk, len, seq, correct);
- return smemeq(correct, blk + len, 20);
-}
-
-static void hmacsha1_96_genresult(void *handle, unsigned char *hmac)
-{
- unsigned char full[20];
- hmacsha1_genresult(handle, full);
- memcpy(hmac, full, 12);
-}
-
-static void sha1_96_generate(void *handle, void *vblk, int len,
- unsigned long seq)
-{
- unsigned char *blk = (unsigned char *)vblk;
- unsigned char full[20];
- sha1_do_hmac(handle, blk, len, seq, full);
- memcpy(blk + len, full, 12);
-}
-
-static int hmacsha1_96_verresult(void *handle, unsigned char const *hmac)
-{
- unsigned char correct[20];
- hmacsha1_genresult(handle, correct);
- return smemeq(correct, hmac, 12);
-}
-
-static int sha1_96_verify(void *handle, const void *vblk, int len,
- unsigned long seq)
-{
- const unsigned char *blk = (const unsigned char *)vblk;
- unsigned char correct[20];
- sha1_do_hmac(handle, blk, len, seq, correct);
- return smemeq(correct, blk + len, 12);
+ SHA_Final(&s, intermediate);
+ memcpy(hmac, intermediate, ctx->mac.vt->len);
+ smemclr(intermediate, sizeof(intermediate));
}
void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
@@ -432,39 +372,33 @@ void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
SHA_Final(&states[1], output);
}
-const struct ssh_mac ssh_hmac_sha1 = {
- sha1_make_context, sha1_free_context, sha1_key,
- sha1_generate, sha1_verify,
- hmacsha1_start, hmacsha1_sink, hmacsha1_genresult, hmacsha1_verresult,
+const struct ssh2_macalg ssh_hmac_sha1 = {
+ hmacsha1_new, hmacsha1_free, hmacsha1_key,
+ hmacsha1_start, hmacsha1_genresult,
"hmac-sha1", "hmac-sha1-etm@openssh.com",
20, 20,
"HMAC-SHA1"
};
-const struct ssh_mac ssh_hmac_sha1_96 = {
- sha1_make_context, sha1_free_context, sha1_key,
- sha1_96_generate, sha1_96_verify,
- hmacsha1_start, hmacsha1_sink,
- hmacsha1_96_genresult, hmacsha1_96_verresult,
+const struct ssh2_macalg ssh_hmac_sha1_96 = {
+ hmacsha1_new, hmacsha1_free, hmacsha1_key,
+ hmacsha1_start, hmacsha1_genresult,
"hmac-sha1-96", "hmac-sha1-96-etm@openssh.com",
12, 20,
"HMAC-SHA1-96"
};
-const struct ssh_mac ssh_hmac_sha1_buggy = {
- sha1_make_context, sha1_free_context, sha1_key_buggy,
- sha1_generate, sha1_verify,
- hmacsha1_start, hmacsha1_sink, hmacsha1_genresult, hmacsha1_verresult,
+const struct ssh2_macalg ssh_hmac_sha1_buggy = {
+ hmacsha1_new, hmacsha1_free, hmacsha1_key,
+ hmacsha1_start, hmacsha1_genresult,
"hmac-sha1", NULL,
20, 16,
"bug-compatible HMAC-SHA1"
};
-const struct ssh_mac ssh_hmac_sha1_96_buggy = {
- sha1_make_context, sha1_free_context, sha1_key_buggy,
- sha1_96_generate, sha1_96_verify,
- hmacsha1_start, hmacsha1_sink,
- hmacsha1_96_genresult, hmacsha1_96_verresult,
+const struct ssh2_macalg ssh_hmac_sha1_96_buggy = {
+ hmacsha1_new, hmacsha1_free, hmacsha1_key,
+ hmacsha1_start, hmacsha1_genresult,
"hmac-sha1-96", NULL,
12, 16,
"bug-compatible HMAC-SHA1-96"
From 4f9a90fc1adae90166d258ba17af6aa44e0d32c2 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 13 Sep 2018 16:41:46 +0100
Subject: [PATCH 402/607] Turn SSH hashes into a classoid.
The new version of ssh_hash has the same nice property as ssh2_mac,
that I can make the generic interface object type function directly as
a BinarySink so that clients don't have to call h->sink() and worry
about the separate sink object they get back from that.
---
ssh.c | 99 ++++++++++++++++++++++++++----------------------------
ssh.h | 37 +++++++++++++-------
sshecc.c | 18 +++++-----
sshrsa.c | 22 ++++++------
sshsh256.c | 60 ++++++++++++++++-----------------
sshsh512.c | 87 +++++++++++++++++++++++------------------------
sshsha.c | 59 ++++++++++++++++----------------
7 files changed, 194 insertions(+), 188 deletions(-)
diff --git a/ssh.c b/ssh.c
index 814d28b7..ab6d6fbe 100644
--- a/ssh.c
+++ b/ssh.c
@@ -668,8 +668,7 @@ enum RekeyClass {
struct ssh_tag {
char *v_c, *v_s;
- void *exhash;
- BinarySink *exhash_bs;
+ ssh_hash *exhash;
Socket s;
@@ -4780,11 +4779,10 @@ static void add_to_commasep(strbuf *buf, const char *data)
static void ssh2_mkkey(Ssh ssh, strbuf *out, Bignum K, unsigned char *H,
char chr, int keylen)
{
- const struct ssh_hash *h = ssh->kex->hash;
+ const struct ssh_hashalg *h = ssh->kex->hash;
int keylen_padded;
unsigned char *key;
- void *s, *s2;
- BinarySink *bs;
+ ssh_hash *s, *s2;
if (keylen == 0)
return;
@@ -4805,32 +4803,30 @@ static void ssh2_mkkey(Ssh ssh, strbuf *out, Bignum K, unsigned char *H,
key = strbuf_append(out, keylen_padded);
/* First hlen bytes. */
- s = h->init();
- bs = h->sink(s);
+ s = ssh_hash_new(h);
if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
- put_mp_ssh2(bs, K);
- put_data(bs, H, h->hlen);
- put_byte(bs, chr);
- put_data(bs, ssh->v2_session_id, ssh->v2_session_id_len);
- h->final(s, key);
+ put_mp_ssh2(s, K);
+ put_data(s, H, h->hlen);
+ put_byte(s, chr);
+ put_data(s, ssh->v2_session_id, ssh->v2_session_id_len);
+ ssh_hash_final(s, key);
/* Subsequent blocks of hlen bytes. */
if (keylen_padded > h->hlen) {
int offset;
- s = h->init();
- bs = h->sink(s);
+ s = ssh_hash_new(h);
if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
- put_mp_ssh2(bs, K);
- put_data(bs, H, h->hlen);
+ put_mp_ssh2(s, K);
+ put_data(s, H, h->hlen);
for (offset = h->hlen; offset < keylen_padded; offset += h->hlen) {
- put_data(bs, key + offset - h->hlen, h->hlen);
- s2 = h->copy(s);
- h->final(s2, key + offset);
+ put_data(s, key + offset - h->hlen, h->hlen);
+ s2 = ssh_hash_copy(s);
+ ssh_hash_final(s2, key + offset);
}
- h->free(s);
+ ssh_hash_free(s);
}
}
@@ -5600,14 +5596,13 @@ static void do_ssh2_transport(void *vctx)
get_string(pktin); /* server->client language */
s->ignorepkt = get_bool(pktin) && !s->guessok;
- ssh->exhash = ssh->kex->hash->init();
- ssh->exhash_bs = ssh->kex->hash->sink(ssh->exhash);
- put_stringz(ssh->exhash_bs, ssh->v_c);
- put_stringz(ssh->exhash_bs, ssh->v_s);
- put_string(ssh->exhash_bs, s->our_kexinit, s->our_kexinitlen);
+ ssh->exhash = ssh_hash_new(ssh->kex->hash);
+ put_stringz(ssh->exhash, ssh->v_c);
+ put_stringz(ssh->exhash, ssh->v_s);
+ put_string(ssh->exhash, s->our_kexinit, s->our_kexinitlen);
sfree(s->our_kexinit);
/* Include the type byte in the hash of server's KEXINIT */
- put_string(ssh->exhash_bs,
+ put_string(ssh->exhash,
(const char *)BinarySource_UPCAST(pktin)->data - 1,
BinarySource_UPCAST(pktin)->len + 1);
@@ -5844,18 +5839,18 @@ static void do_ssh2_transport(void *vctx)
* involve user interaction. */
set_busy_status(ssh->frontend, BUSY_NOT);
- put_stringpl(ssh->exhash_bs, s->hostkeydata);
+ put_stringpl(ssh->exhash, s->hostkeydata);
if (dh_is_gex(ssh->kex)) {
if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX))
- put_uint32(ssh->exhash_bs, DH_MIN_SIZE);
- put_uint32(ssh->exhash_bs, s->pbits);
+ put_uint32(ssh->exhash, DH_MIN_SIZE);
+ put_uint32(ssh->exhash, s->pbits);
if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX))
- put_uint32(ssh->exhash_bs, DH_MAX_SIZE);
- put_mp_ssh2(ssh->exhash_bs, s->p);
- put_mp_ssh2(ssh->exhash_bs, s->g);
+ put_uint32(ssh->exhash, DH_MAX_SIZE);
+ put_mp_ssh2(ssh->exhash, s->p);
+ put_mp_ssh2(ssh->exhash, s->g);
}
- put_mp_ssh2(ssh->exhash_bs, s->e);
- put_mp_ssh2(ssh->exhash_bs, s->f);
+ put_mp_ssh2(ssh->exhash, s->e);
+ put_mp_ssh2(ssh->exhash, s->f);
dh_cleanup(ssh->kex_ctx);
freebn(s->f);
@@ -5893,19 +5888,19 @@ static void do_ssh2_transport(void *vctx)
}
s->hostkeydata = get_string(pktin);
- put_stringpl(ssh->exhash_bs, s->hostkeydata);
+ put_stringpl(ssh->exhash, s->hostkeydata);
s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
{
strbuf *pubpoint = strbuf_new();
ssh_ecdhkex_getpublic(s->eckey, BinarySink_UPCAST(pubpoint));
- put_string(ssh->exhash_bs, pubpoint->u, pubpoint->len);
+ put_string(ssh->exhash, pubpoint->u, pubpoint->len);
strbuf_free(pubpoint);
}
{
ptrlen keydata = get_string(pktin);
- put_stringpl(ssh->exhash_bs, keydata);
+ put_stringpl(ssh->exhash, keydata);
s->K = ssh_ecdhkex_getkey(s->eckey, keydata.ptr, keydata.len);
if (!get_err(pktin) && !s->K) {
ssh_ecdhkex_freekey(s->eckey);
@@ -6100,7 +6095,7 @@ static void do_ssh2_transport(void *vctx)
if (ssh->hostkey_alg) {
s->hkey = ssh_key_new_pub(ssh->hostkey_alg,
s->hostkeydata);
- put_string(ssh->exhash_bs,
+ put_string(ssh->exhash,
s->hostkeydata.ptr, s->hostkeydata.len);
}
/*
@@ -6149,18 +6144,18 @@ static void do_ssh2_transport(void *vctx)
set_busy_status(ssh->frontend, BUSY_NOT);
if (!s->hkey)
- put_stringz(ssh->exhash_bs, "");
+ put_stringz(ssh->exhash, "");
if (dh_is_gex(ssh->kex)) {
/* min, preferred, max */
- put_uint32(ssh->exhash_bs, s->pbits);
- put_uint32(ssh->exhash_bs, s->pbits);
- put_uint32(ssh->exhash_bs, s->pbits * 2);
+ put_uint32(ssh->exhash, s->pbits);
+ put_uint32(ssh->exhash, s->pbits);
+ put_uint32(ssh->exhash, s->pbits * 2);
- put_mp_ssh2(ssh->exhash_bs, s->p);
- put_mp_ssh2(ssh->exhash_bs, s->g);
+ put_mp_ssh2(ssh->exhash, s->p);
+ put_mp_ssh2(ssh->exhash, s->g);
}
- put_mp_ssh2(ssh->exhash_bs, s->e);
- put_mp_ssh2(ssh->exhash_bs, s->f);
+ put_mp_ssh2(ssh->exhash, s->e);
+ put_mp_ssh2(ssh->exhash, s->f);
/*
* MIC verification is done below, after we compute the hash
@@ -6192,7 +6187,7 @@ static void do_ssh2_transport(void *vctx)
}
s->hostkeydata = get_string(pktin);
- put_stringpl(ssh->exhash_bs, s->hostkeydata);
+ put_stringpl(ssh->exhash, s->hostkeydata);
s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
rsakeydata = get_string(pktin);
@@ -6203,7 +6198,7 @@ static void do_ssh2_transport(void *vctx)
crStopV;
}
- put_stringpl(ssh->exhash_bs, rsakeydata);
+ put_stringpl(ssh->exhash, rsakeydata);
/*
* Next, set up a shared secret K, of precisely KLEN -
@@ -6249,7 +6244,7 @@ static void do_ssh2_transport(void *vctx)
put_string(s->pktout, outstr, outstrlen);
ssh_pkt_write(ssh, s->pktout);
- put_string(ssh->exhash_bs, outstr, outstrlen);
+ put_string(ssh->exhash, outstr, outstrlen);
strbuf_free(buf);
sfree(outstr);
@@ -6270,9 +6265,9 @@ static void do_ssh2_transport(void *vctx)
}
}
- put_mp_ssh2(ssh->exhash_bs, s->K);
- assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
- ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
+ put_mp_ssh2(ssh->exhash, s->K);
+ assert(ssh_hash_alg(ssh->exhash)->hlen <= sizeof(s->exchange_hash));
+ ssh_hash_final(ssh->exhash, s->exchange_hash);
#ifndef NO_GSSAPI
if (ssh->kex->main_type == KEXTYPE_GSS) {
diff --git a/ssh.h b/ssh.h
index 6b768a8a..ff4305b0 100644
--- a/ssh.h
+++ b/ssh.h
@@ -331,11 +331,12 @@ int detect_attack(struct crcda_ctx *ctx, unsigned char *buf, uint32 len,
/*
* SSH2 RSA key exchange functions
*/
-struct ssh_hash;
+struct ssh_hashalg;
struct RSAKey *ssh_rsakex_newkey(const void *data, int len);
void ssh_rsakex_freekey(struct RSAKey *key);
int ssh_rsakex_klen(struct RSAKey *key);
-void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
+void ssh_rsakex_encrypt(const struct ssh_hashalg *h,
+ unsigned char *in, int inlen,
unsigned char *out, int outlen, struct RSAKey *key);
/*
@@ -527,20 +528,30 @@ int ssh2_mac_verresult(ssh2_mac *, const void *);
void ssh2_mac_generate(ssh2_mac *, void *, int, unsigned long seq);
int ssh2_mac_verify(ssh2_mac *, const void *, int, unsigned long seq);
-struct ssh_hash {
- void *(*init)(void); /* also allocates context */
- void *(*copy)(const void *);
- BinarySink *(*sink) (void *);
- void (*final)(void *, unsigned char *); /* also frees context */
- void (*free)(void *);
+typedef struct ssh_hash {
+ const struct ssh_hashalg *vt;
+ BinarySink_DELEGATE_IMPLEMENTATION;
+} ssh_hash;
+
+struct ssh_hashalg {
+ ssh_hash *(*new)(const struct ssh_hashalg *alg);
+ ssh_hash *(*copy)(ssh_hash *);
+ void (*final)(ssh_hash *, unsigned char *); /* ALSO FREES THE ssh_hash! */
+ void (*free)(ssh_hash *);
int hlen; /* output length in bytes */
const char *text_name;
};
+#define ssh_hash_new(alg) ((alg)->new(alg))
+#define ssh_hash_copy(ctx) ((ctx)->vt->copy(ctx))
+#define ssh_hash_final(ctx, out) ((ctx)->vt->final(ctx, out))
+#define ssh_hash_free(ctx) ((ctx)->vt->free(ctx))
+#define ssh_hash_alg(ctx) ((ctx)->vt)
+
struct ssh_kex {
const char *name, *groupname;
enum { KEXTYPE_DH, KEXTYPE_RSA, KEXTYPE_ECDH, KEXTYPE_GSS } main_type;
- const struct ssh_hash *hash;
+ const struct ssh_hashalg *hash;
const void *extra; /* private to the kex methods */
};
@@ -625,10 +636,10 @@ extern const struct ssh2_ciphers ssh2_aes;
extern const struct ssh2_ciphers ssh2_blowfish;
extern const struct ssh2_ciphers ssh2_arcfour;
extern const struct ssh2_ciphers ssh2_ccp;
-extern const struct ssh_hash ssh_sha1;
-extern const struct ssh_hash ssh_sha256;
-extern const struct ssh_hash ssh_sha384;
-extern const struct ssh_hash ssh_sha512;
+extern const struct ssh_hashalg ssh_sha1;
+extern const struct ssh_hashalg ssh_sha256;
+extern const struct ssh_hashalg ssh_sha384;
+extern const struct ssh_hashalg ssh_sha512;
extern const struct ssh_kexes ssh_diffiehellman_group1;
extern const struct ssh_kexes ssh_diffiehellman_group14;
extern const struct ssh_kexes ssh_diffiehellman_gex;
diff --git a/sshecc.c b/sshecc.c
index ce663bfb..34d0e948 100644
--- a/sshecc.c
+++ b/sshecc.c
@@ -1688,7 +1688,7 @@ static int BinarySource_get_point(BinarySource *src, struct ec_point *point)
struct ecsign_extra {
struct ec_curve *(*curve)(void);
- const struct ssh_hash *hash;
+ const struct ssh_hashalg *hash;
/* These fields are used by the OpenSSH PEM format importer/exporter */
const unsigned char *oid;
@@ -2227,7 +2227,7 @@ static int ecdsa_verify(ssh_key *key, ptrlen sig, ptrlen data)
Bignum r, s;
unsigned char digest[512 / 8];
int digestLen;
- void *hashctx;
+ ssh_hash *hashctx;
BinarySource_BARE_INIT(src, sigstr.ptr, sigstr.len);
@@ -2241,9 +2241,9 @@ static int ecdsa_verify(ssh_key *key, ptrlen sig, ptrlen data)
digestLen = extra->hash->hlen;
assert(digestLen <= sizeof(digest));
- hashctx = extra->hash->init();
- put_data(extra->hash->sink(hashctx), data.ptr, data.len);
- extra->hash->final(hashctx, digest);
+ hashctx = ssh_hash_new(extra->hash);
+ put_data(hashctx, data.ptr, data.len);
+ ssh_hash_final(hashctx, digest);
/* Verify the signature */
ret = _ecdsa_verify(&ec->publicKey, digest, digestLen, r, s);
@@ -2363,14 +2363,14 @@ static void ecdsa_sign(ssh_key *key, const void *data, int datalen,
put_byte(bs, bignum_byte(s, i));
freebn(s);
} else {
- void *hashctx;
+ ssh_hash *hashctx;
strbuf *substr;
digestLen = extra->hash->hlen;
assert(digestLen <= sizeof(digest));
- hashctx = extra->hash->init();
- put_data(extra->hash->sink(hashctx), data, datalen);
- extra->hash->final(hashctx, digest);
+ hashctx = ssh_hash_new(extra->hash);
+ put_data(hashctx, data, datalen);
+ ssh_hash_final(hashctx, digest);
/* Do the signature */
_ecdsa_sign(ec->privateKey, ec->publicKey.curve, digest, digestLen, &r, &s);
diff --git a/sshrsa.c b/sshrsa.c
index f66af550..c2bc4bfb 100644
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -783,7 +783,7 @@ int ssh_rsakex_klen(struct RSAKey *rsa)
return bignum_bitcount(rsa->modulus);
}
-static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
+static void oaep_mask(const struct ssh_hashalg *h, void *seed, int seedlen,
void *vdata, int datalen)
{
unsigned char *data = (unsigned char *)vdata;
@@ -791,16 +791,14 @@ static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
while (datalen > 0) {
int i, max = (datalen > h->hlen ? h->hlen : datalen);
- void *s;
- BinarySink *bs;
+ ssh_hash *s;
unsigned char hash[SSH2_KEX_MAX_HASH_LEN];
assert(h->hlen <= SSH2_KEX_MAX_HASH_LEN);
- s = h->init();
- bs = h->sink(s);
- put_data(bs, seed, seedlen);
- put_uint32(bs, count);
- h->final(s, hash);
+ s = ssh_hash_new(h);
+ put_data(s, seed, seedlen);
+ put_uint32(s, count);
+ ssh_hash_final(s, hash);
count++;
for (i = 0; i < max; i++)
@@ -811,7 +809,8 @@ static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
}
}
-void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
+void ssh_rsakex_encrypt(const struct ssh_hashalg *h,
+ unsigned char *in, int inlen,
unsigned char *out, int outlen, struct RSAKey *rsa)
{
Bignum b1, b2;
@@ -866,7 +865,10 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
out[i + 1] = random_byte();
/* At position 1+HLEN, the data block DB, consisting of: */
/* The hash of the label (we only support an empty label here) */
- h->final(h->init(), out + HLEN + 1);
+ {
+ ssh_hash *s = ssh_hash_new(h);
+ ssh_hash_final(s, out + HLEN + 1);
+ }
/* A bunch of zero octets */
memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1));
/* A single 1 octet, followed by the input message data. */
diff --git a/sshsh256.c b/sshsh256.c
index e4b14fd7..75a04495 100644
--- a/sshsh256.c
+++ b/sshsh256.c
@@ -204,51 +204,51 @@ void SHA256_Simple(const void *p, int len, unsigned char *output) {
* Thin abstraction for things where hashes are pluggable.
*/
-static void *sha256_init(void)
-{
- SHA256_State *s;
+struct sha256_hash {
+ SHA256_State state;
+ ssh_hash hash;
+};
- s = snew(SHA256_State);
- SHA256_Init(s);
- return s;
+static ssh_hash *sha256_new(const struct ssh_hashalg *alg)
+{
+ struct sha256_hash *h = snew(struct sha256_hash);
+ SHA256_Init(&h->state);
+ h->hash.vt = alg;
+ BinarySink_DELEGATE_INIT(&h->hash, &h->state);
+ return &h->hash;
}
-static void *sha256_copy(const void *vold)
+static ssh_hash *sha256_copy(ssh_hash *hashold)
{
- const SHA256_State *old = (const SHA256_State *)vold;
- SHA256_State *s;
+ struct sha256_hash *hold, *hnew;
+ ssh_hash *hashnew = sha256_new(hashold->vt);
- s = snew(SHA256_State);
- *s = *old;
- BinarySink_COPIED(s);
- return s;
-}
+ hold = FROMFIELD(hashold, struct sha256_hash, hash);
+ hnew = FROMFIELD(hashnew, struct sha256_hash, hash);
-static void sha256_free(void *handle)
-{
- SHA256_State *s = handle;
+ hnew->state = hold->state;
+ BinarySink_COPIED(&hnew->state);
- smemclr(s, sizeof(*s));
- sfree(s);
+ return hashnew;
}
-static BinarySink *sha256_sink(void *handle)
+static void sha256_free(ssh_hash *hash)
{
- SHA256_State *s = handle;
- return BinarySink_UPCAST(s);
+ struct sha256_hash *h = FROMFIELD(hash, struct sha256_hash, hash);
+
+ smemclr(h, sizeof(*h));
+ sfree(h);
}
-static void sha256_final(void *handle, unsigned char *output)
+static void sha256_final(ssh_hash *hash, unsigned char *output)
{
- SHA256_State *s = handle;
-
- SHA256_Final(s, output);
- sha256_free(s);
+ struct sha256_hash *h = FROMFIELD(hash, struct sha256_hash, hash);
+ SHA256_Final(&h->state, output);
+ sha256_free(hash);
}
-const struct ssh_hash ssh_sha256 = {
- sha256_init, sha256_copy, sha256_sink, sha256_final, sha256_free,
- 32, "SHA-256"
+const struct ssh_hashalg ssh_sha256 = {
+ sha256_new, sha256_copy, sha256_final, sha256_free, 32, "SHA-256"
};
/* ----------------------------------------------------------------------
diff --git a/sshsh512.c b/sshsh512.c
index e16e8c9f..85df1f29 100644
--- a/sshsh512.c
+++ b/sshsh512.c
@@ -327,74 +327,71 @@ void SHA384_Simple(const void *p, int len, unsigned char *output) {
* Thin abstraction for things where hashes are pluggable.
*/
-static void *sha512_init(void)
-{
- SHA512_State *s;
+struct sha512_hash {
+ SHA512_State state;
+ ssh_hash hash;
+};
- s = snew(SHA512_State);
- SHA512_Init(s);
- return s;
+static ssh_hash *sha512_new(const struct ssh_hashalg *alg)
+{
+ struct sha512_hash *h = snew(struct sha512_hash);
+ SHA512_Init(&h->state);
+ h->hash.vt = alg;
+ BinarySink_DELEGATE_INIT(&h->hash, &h->state);
+ return &h->hash;
}
-static void *sha512_copy(const void *vold)
+static ssh_hash *sha512_copy(ssh_hash *hashold)
{
- const SHA512_State *old = (const SHA512_State *)vold;
- SHA512_State *s;
+ struct sha512_hash *hold, *hnew;
+ ssh_hash *hashnew = sha512_new(hashold->vt);
- s = snew(SHA512_State);
- *s = *old;
- BinarySink_COPIED(s);
- return s;
-}
+ hold = FROMFIELD(hashold, struct sha512_hash, hash);
+ hnew = FROMFIELD(hashnew, struct sha512_hash, hash);
-static void sha512_free(void *handle)
-{
- SHA512_State *s = handle;
+ hnew->state = hold->state;
+ BinarySink_COPIED(&hnew->state);
- smemclr(s, sizeof(*s));
- sfree(s);
+ return hashnew;
}
-static BinarySink *sha512_sink(void *handle)
+static void sha512_free(ssh_hash *hash)
{
- SHA512_State *s = handle;
- return BinarySink_UPCAST(s);
+ struct sha512_hash *h = FROMFIELD(hash, struct sha512_hash, hash);
+
+ smemclr(h, sizeof(*h));
+ sfree(h);
}
-static void sha512_final(void *handle, unsigned char *output)
+static void sha512_final(ssh_hash *hash, unsigned char *output)
{
- SHA512_State *s = handle;
-
- SHA512_Final(s, output);
- sha512_free(s);
+ struct sha512_hash *h = FROMFIELD(hash, struct sha512_hash, hash);
+ SHA512_Final(&h->state, output);
+ sha512_free(hash);
}
-const struct ssh_hash ssh_sha512 = {
- sha512_init, sha512_copy, sha512_sink, sha512_final, sha512_free,
- 64, "SHA-512"
+const struct ssh_hashalg ssh_sha512 = {
+ sha512_new, sha512_copy, sha512_final, sha512_free, 64, "SHA-512"
};
-static void *sha384_init(void)
+static ssh_hash *sha384_new(const struct ssh_hashalg *alg)
{
- SHA512_State *s;
-
- s = snew(SHA512_State);
- SHA384_Init(s);
- return s;
+ struct sha512_hash *h = snew(struct sha512_hash);
+ SHA384_Init(&h->state);
+ h->hash.vt = alg;
+ BinarySink_DELEGATE_INIT(&h->hash, &h->state);
+ return &h->hash;
}
-static void sha384_final(void *handle, unsigned char *output)
+static void sha384_final(ssh_hash *hash, unsigned char *output)
{
- SHA512_State *s = handle;
-
- SHA384_Final(s, output);
- smemclr(s, sizeof(*s));
- sfree(s);
+ struct sha512_hash *h = FROMFIELD(hash, struct sha512_hash, hash);
+ SHA384_Final(&h->state, output);
+ sha512_free(hash);
}
-const struct ssh_hash ssh_sha384 = {
- sha384_init, sha512_copy, sha512_sink, sha384_final, sha512_free,
- 48, "SHA-384"
+const struct ssh_hashalg ssh_sha384 = {
+ sha384_new, sha512_copy, sha384_final, sha512_free, 48, "SHA-384"
};
#ifdef TEST
diff --git a/sshsha.c b/sshsha.c
index 310241a4..f93c2a1f 100644
--- a/sshsha.c
+++ b/sshsha.c
@@ -232,50 +232,51 @@ void SHA_Simple(const void *p, int len, unsigned char *output)
* Thin abstraction for things where hashes are pluggable.
*/
-static void *sha1_init(void)
-{
- SHA_State *s;
+struct sha1_hash {
+ SHA_State state;
+ ssh_hash hash;
+};
- s = snew(SHA_State);
- SHA_Init(s);
- return s;
+static ssh_hash *sha1_new(const struct ssh_hashalg *alg)
+{
+ struct sha1_hash *h = snew(struct sha1_hash);
+ SHA_Init(&h->state);
+ h->hash.vt = alg;
+ BinarySink_DELEGATE_INIT(&h->hash, &h->state);
+ return &h->hash;
}
-static void *sha1_copy(const void *vold)
+static ssh_hash *sha1_copy(ssh_hash *hashold)
{
- const SHA_State *old = (const SHA_State *)vold;
- SHA_State *s;
+ struct sha1_hash *hold, *hnew;
+ ssh_hash *hashnew = sha1_new(hashold->vt);
- s = snew(SHA_State);
- *s = *old;
- BinarySink_COPIED(s);
- return s;
-}
+ hold = FROMFIELD(hashold, struct sha1_hash, hash);
+ hnew = FROMFIELD(hashnew, struct sha1_hash, hash);
-static void sha1_free(void *handle)
-{
- SHA_State *s = handle;
+ hnew->state = hold->state;
+ BinarySink_COPIED(&hnew->state);
- smemclr(s, sizeof(*s));
- sfree(s);
+ return hashnew;
}
-static BinarySink *sha1_sink(void *handle)
+static void sha1_free(ssh_hash *hash)
{
- SHA_State *s = handle;
- return BinarySink_UPCAST(s);
+ struct sha1_hash *h = FROMFIELD(hash, struct sha1_hash, hash);
+
+ smemclr(h, sizeof(*h));
+ sfree(h);
}
-static void sha1_final(void *handle, unsigned char *output)
+static void sha1_final(ssh_hash *hash, unsigned char *output)
{
- SHA_State *s = handle;
-
- SHA_Final(s, output);
- sha1_free(s);
+ struct sha1_hash *h = FROMFIELD(hash, struct sha1_hash, hash);
+ SHA_Final(&h->state, output);
+ sha1_free(hash);
}
-const struct ssh_hash ssh_sha1 = {
- sha1_init, sha1_copy, sha1_sink, sha1_final, sha1_free, 20, "SHA-1"
+const struct ssh_hashalg ssh_sha1 = {
+ sha1_new, sha1_copy, sha1_final, sha1_free, 20, "SHA-1"
};
/* ----------------------------------------------------------------------
From 9738e042f90942ef782381f747d21709f3b37c62 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 13 Sep 2018 16:43:30 +0100
Subject: [PATCH 403/607] Clean up a couple of consts and char pointers.
hmacmd5_do_hmac and hmac_sha1_simple should be consistently referring
to input memory blocks as 'const void *', but one had pointlessly
typed the pointer as 'const unsigned char *' and the other had missed
out the consts.
---
ssh.h | 5 +++--
sshmd5.c | 2 +-
sshsha.c | 3 ++-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/ssh.h b/ssh.h
index ff4305b0..0b2d78ec 100644
--- a/ssh.h
+++ b/ssh.h
@@ -380,7 +380,7 @@ struct hmacmd5_context *hmacmd5_make_context(void);
void hmacmd5_free_context(struct hmacmd5_context *ctx);
void hmacmd5_key(struct hmacmd5_context *ctx, void const *key, int len);
void hmacmd5_do_hmac(struct hmacmd5_context *ctx,
- unsigned char const *blk, int len, unsigned char *hmac);
+ const void *blk, int len, unsigned char *hmac);
int supports_sha_ni(void);
@@ -396,7 +396,8 @@ void SHA_Init(SHA_State * s);
void SHA_Final(SHA_State * s, unsigned char *output);
void SHA_Simple(const void *p, int len, unsigned char *output);
-void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
+void hmac_sha1_simple(const void *key, int keylen,
+ const void *data, int datalen,
unsigned char *output);
typedef struct SHA256_State {
uint32 h[8];
diff --git a/sshmd5.c b/sshmd5.c
index 87a224d9..35a21df4 100644
--- a/sshmd5.c
+++ b/sshmd5.c
@@ -312,7 +312,7 @@ static void hmacmd5_genresult(ssh2_mac *mac, unsigned char *hmac)
}
void hmacmd5_do_hmac(struct hmacmd5_context *ctx,
- unsigned char const *blk, int len, unsigned char *hmac)
+ const void *blk, int len, unsigned char *hmac)
{
ssh2_mac_start(&ctx->mac);
put_data(&ctx->mac, blk, len);
diff --git a/sshsha.c b/sshsha.c
index f93c2a1f..282e98a3 100644
--- a/sshsha.c
+++ b/sshsha.c
@@ -360,7 +360,8 @@ static void hmacsha1_genresult(ssh2_mac *mac, unsigned char *hmac)
smemclr(intermediate, sizeof(intermediate));
}
-void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
+void hmac_sha1_simple(const void *key, int keylen,
+ const void *data, int datalen,
unsigned char *output) {
SHA_State states[2];
unsigned char intermediate[20];
From 7efa4a53051e6ee43d30ee1573f55cb0d2797769 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 14 Sep 2018 08:30:09 +0100
Subject: [PATCH 404/607] Clean up a 'void *' in a unix.h typedef.
'struct draw_ctx' has a structure tag inside gtkwin.c, so as per this
week's standard practice, let's expose the tag elsewhere so that
pointers declared that way can't be confused with anything else.
---
fuzzterm.c | 6 +-----
unix/gtkwin.c | 16 ++++++----------
unix/unix.h | 2 +-
3 files changed, 8 insertions(+), 16 deletions(-)
diff --git a/fuzzterm.c b/fuzzterm.c
index fda9f6c8..fb68482c 100644
--- a/fuzzterm.c
+++ b/fuzzterm.c
@@ -73,11 +73,7 @@ void set_sbar(Frontend *frontend, int a, int b, int c) { }
void ldisc_send(Ldisc *ldisc, const void *buf, int len, int interactive) {}
void ldisc_echoedit_update(Ldisc *ldisc) {}
-Context get_ctx(Frontend *frontend) {
- static char x;
-
- return &x;
-}
+Context get_ctx(Frontend *frontend) { return NULL; }
void free_ctx(Context ctx) { }
void palette_set(Frontend *frontend, int a, int b, int c, int d) { }
void palette_reset(Frontend *frontend) { }
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index aebeff8f..46b62782 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -3481,9 +3481,8 @@ Context get_ctx(Frontend *inst)
return dctx;
}
-void free_ctx(Context ctx)
+void free_ctx(Context dctx)
{
- struct draw_ctx *dctx = (struct draw_ctx *)ctx;
/* Frontend *inst = dctx->inst; */
#ifdef DRAW_TEXT_GDK
if (dctx->uctx.type == DRAWTYPE_GDK) {
@@ -3796,10 +3795,9 @@ static void draw_backing_rect(Frontend *inst)
*
* We are allowed to fiddle with the contents of `text'.
*/
-void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
+void do_text_internal(Context dctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr, truecolour truecolour)
{
- struct draw_ctx *dctx = (struct draw_ctx *)ctx;
Frontend *inst = dctx->inst;
int ncombining;
int nfg, nbg, t, fontid, shadow, rlen, widefactor, bold;
@@ -3953,14 +3951,13 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
}
}
-void do_text(Context ctx, int x, int y, wchar_t *text, int len,
+void do_text(Context dctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr, truecolour truecolour)
{
- struct draw_ctx *dctx = (struct draw_ctx *)ctx;
Frontend *inst = dctx->inst;
int widefactor;
- do_text_internal(ctx, x, y, text, len, attr, lattr, truecolour);
+ do_text_internal(dctx, x, y, text, len, attr, lattr, truecolour);
if (attr & ATTR_WIDE) {
widefactor = 2;
@@ -3983,10 +3980,9 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
len*widefactor*inst->font_width, inst->font_height);
}
-void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
+void do_cursor(Context dctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr, truecolour truecolour)
{
- struct draw_ctx *dctx = (struct draw_ctx *)ctx;
Frontend *inst = dctx->inst;
int active, passive, widefactor;
@@ -4001,7 +3997,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
active = 1;
} else
active = 0;
- do_text_internal(ctx, x, y, text, len, attr, lattr, truecolour);
+ do_text_internal(dctx, x, y, text, len, attr, lattr, truecolour);
if (attr & TATTR_COMBINING)
len = 1;
diff --git a/unix/unix.h b/unix/unix.h
index 3dd2864a..9a93a059 100644
--- a/unix/unix.h
+++ b/unix/unix.h
@@ -65,7 +65,7 @@ struct FontSpec {
};
struct FontSpec *fontspec_new(const char *name);
-typedef void *Context; /* FIXME: probably needs changing */
+typedef struct draw_ctx *Context;
extern const struct Backend_vtable pty_backend;
From 733fcca2cdf7e4719d4d298cf980537eca2580d8 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 14 Sep 2018 08:45:42 +0100
Subject: [PATCH 405/607] Invent structure tags for the storage.h abstractions.
Most of these were 'void *' because they weren't even reliably a
structure type underneath - the per-OS storage systems would directly
cast read/write/enum settings handles to and from random things like
FILE *, Unix DIR *, or Windows HKEY. So I've wrapped them in tiny
structs for the sake of having a sensible structure tag visible
elsewhere in the code.
---
defs.h | 4 ++
putty.h | 4 +-
settings.c | 10 ++---
storage.h | 32 ++++++++-------
unix/uxstore.c | 98 ++++++++++++++++++++++++++--------------------
windows/winjump.c | 3 +-
windows/winstore.c | 77 +++++++++++++++++++++---------------
7 files changed, 131 insertions(+), 97 deletions(-)
diff --git a/defs.h b/defs.h
index 1bc8f9b8..ba61708b 100644
--- a/defs.h
+++ b/defs.h
@@ -61,6 +61,10 @@ typedef struct share_channel share_channel;
typedef struct dlgparam dlgparam;
+typedef struct settings_w settings_w;
+typedef struct settings_r settings_r;
+typedef struct settings_e settings_e;
+
/* Note indirection: for historical reasons (it used to be closer to
* the OS socket type), the type that most code uses for a socket is
* 'Socket', not 'Socket *'. So an implementation of Socket or Plug
diff --git a/putty.h b/putty.h
index 7b1f90e4..dd43e8c4 100644
--- a/putty.h
+++ b/putty.h
@@ -1074,9 +1074,9 @@ const struct Backend_vtable *backend_vt_from_name(const char *name);
const struct Backend_vtable *backend_vt_from_proto(int proto);
char *get_remote_username(Conf *conf); /* dynamically allocated */
char *save_settings(const char *section, Conf *conf);
-void save_open_settings(void *sesskey, Conf *conf);
+void save_open_settings(settings_w *sesskey, Conf *conf);
void load_settings(const char *section, Conf *conf);
-void load_open_settings(void *sesskey, Conf *conf);
+void load_open_settings(settings_r *sesskey, Conf *conf);
void get_sesslist(struct sesslist *, int allocate);
void do_defaults(const char *, Conf *);
void registry_cleanup(void);
diff --git a/settings.c b/settings.c
index ecdb3a4b..ab735488 100644
--- a/settings.c
+++ b/settings.c
@@ -502,7 +502,7 @@ static void read_clip_setting(void *handle, const char *savekey,
char *save_settings(const char *section, Conf *conf)
{
- void *sesskey;
+ struct settings_w *sesskey;
char *errmsg;
sesskey = open_settings_w(section, &errmsg);
@@ -513,7 +513,7 @@ char *save_settings(const char *section, Conf *conf)
return NULL;
}
-void save_open_settings(void *sesskey, Conf *conf)
+void save_open_settings(settings_w *sesskey, Conf *conf)
{
int i;
const char *p;
@@ -760,7 +760,7 @@ void save_open_settings(void *sesskey, Conf *conf)
void load_settings(const char *section, Conf *conf)
{
- void *sesskey;
+ settings_r *sesskey;
sesskey = open_settings_r(section);
load_open_settings(sesskey, conf);
@@ -770,7 +770,7 @@ void load_settings(const char *section, Conf *conf)
add_session_to_jumplist(section);
}
-void load_open_settings(void *sesskey, Conf *conf)
+void load_open_settings(settings_r *sesskey, Conf *conf)
{
int i;
char *prot;
@@ -1242,7 +1242,7 @@ void get_sesslist(struct sesslist *list, int allocate)
char otherbuf[2048];
int buflen, bufsize, i;
char *p, *ret;
- void *handle;
+ settings_e *handle;
if (allocate) {
diff --git a/storage.h b/storage.h
index 8e07ef0c..6186e91d 100644
--- a/storage.h
+++ b/storage.h
@@ -28,12 +28,14 @@
*
* Any returned error message must be freed after use.
*/
-void *open_settings_w(const char *sessionname, char **errmsg);
-void write_setting_s(void *handle, const char *key, const char *value);
-void write_setting_i(void *handle, const char *key, int value);
-void write_setting_filename(void *handle, const char *key, Filename *value);
-void write_setting_fontspec(void *handle, const char *key, FontSpec *font);
-void close_settings_w(void *handle);
+settings_w *open_settings_w(const char *sessionname, char **errmsg);
+void write_setting_s(settings_w *handle, const char *key, const char *value);
+void write_setting_i(settings_w *handle, const char *key, int value);
+void write_setting_filename(settings_w *handle,
+ const char *key, Filename *value);
+void write_setting_fontspec(settings_w *handle,
+ const char *key, FontSpec *font);
+void close_settings_w(settings_w *handle);
/*
* Read a saved session. The caller is expected to call
@@ -51,12 +53,12 @@ void close_settings_w(void *handle);
* should invent a sensible default. If an integer setting is not
* present, read_setting_i() returns its provided default.
*/
-void *open_settings_r(const char *sessionname);
-char *read_setting_s(void *handle, const char *key);
-int read_setting_i(void *handle, const char *key, int defvalue);
-Filename *read_setting_filename(void *handle, const char *key);
-FontSpec *read_setting_fontspec(void *handle, const char *key);
-void close_settings_r(void *handle);
+settings_r *open_settings_r(const char *sessionname);
+char *read_setting_s(settings_r *handle, const char *key);
+int read_setting_i(settings_r *handle, const char *key, int defvalue);
+Filename *read_setting_filename(settings_r *handle, const char *key);
+FontSpec *read_setting_fontspec(settings_r *handle, const char *key);
+void close_settings_r(settings_r *handle);
/*
* Delete a whole saved session.
@@ -66,9 +68,9 @@ void del_settings(const char *sessionname);
/*
* Enumerate all saved sessions.
*/
-void *enum_settings_start(void);
-char *enum_settings_next(void *handle, char *buffer, int buflen);
-void enum_settings_finish(void *handle);
+settings_e *enum_settings_start(void);
+char *enum_settings_next(settings_e *handle, char *buffer, int buflen);
+void enum_settings_finish(settings_e *handle);
/* ----------------------------------------------------------------------
* Functions to access PuTTY's host key database.
diff --git a/unix/uxstore.c b/unix/uxstore.c
index 15801785..54a20b8a 100644
--- a/unix/uxstore.c
+++ b/unix/uxstore.c
@@ -218,7 +218,11 @@ static char *make_filename(int index, const char *subname)
return ret;
}
-void *open_settings_w(const char *sessionname, char **errmsg)
+struct settings_w {
+ FILE *fp;
+};
+
+settings_w *open_settings_w(const char *sessionname, char **errmsg)
{
char *filename, *err;
FILE *fp;
@@ -256,25 +260,26 @@ void *open_settings_w(const char *sessionname, char **errmsg)
return NULL; /* can't open */
}
sfree(filename);
- return fp;
+
+ settings_w *toret = snew(settings_w);
+ toret->fp = fp;
+ return toret;
}
-void write_setting_s(void *handle, const char *key, const char *value)
+void write_setting_s(settings_w *handle, const char *key, const char *value)
{
- FILE *fp = (FILE *)handle;
- fprintf(fp, "%s=%s\n", key, value);
+ fprintf(handle->fp, "%s=%s\n", key, value);
}
-void write_setting_i(void *handle, const char *key, int value)
+void write_setting_i(settings_w *handle, const char *key, int value)
{
- FILE *fp = (FILE *)handle;
- fprintf(fp, "%s=%d\n", key, value);
+ fprintf(handle->fp, "%s=%d\n", key, value);
}
-void close_settings_w(void *handle)
+void close_settings_w(settings_w *handle)
{
- FILE *fp = (FILE *)handle;
- fclose(fp);
+ fclose(handle->fp);
+ sfree(handle);
}
/*
@@ -347,12 +352,16 @@ const char *get_setting(const char *key)
return x_get_default(key);
}
-void *open_settings_r(const char *sessionname)
+struct settings_r {
+ tree234 *t;
+};
+
+settings_r *open_settings_r(const char *sessionname)
{
char *filename;
FILE *fp;
char *line;
- tree234 *ret;
+ settings_r *toret;
filename = make_filename(INDEX_SESSION, sessionname);
fp = fopen(filename, "r");
@@ -360,7 +369,8 @@ void *open_settings_r(const char *sessionname)
if (!fp)
return NULL; /* can't open */
- ret = newtree234(keycmp);
+ toret = snew(settings_r);
+ toret->t = newtree234(keycmp);
while ( (line = fgetline(fp)) ) {
char *value = strchr(line, '=');
@@ -376,25 +386,24 @@ void *open_settings_r(const char *sessionname)
kv = snew(struct skeyval);
kv->key = dupstr(line);
kv->value = dupstr(value);
- add234(ret, kv);
+ add234(toret->t, kv);
sfree(line);
}
fclose(fp);
- return ret;
+ return toret;
}
-char *read_setting_s(void *handle, const char *key)
+char *read_setting_s(settings_r *handle, const char *key)
{
- tree234 *tree = (tree234 *)handle;
const char *val;
struct skeyval tmp, *kv;
tmp.key = key;
- if (tree != NULL &&
- (kv = find234(tree, &tmp, NULL)) != NULL) {
+ if (handle != NULL &&
+ (kv = find234(handle->t, &tmp, NULL)) != NULL) {
val = kv->value;
assert(val != NULL);
} else
@@ -406,15 +415,14 @@ char *read_setting_s(void *handle, const char *key)
return dupstr(val);
}
-int read_setting_i(void *handle, const char *key, int defvalue)
+int read_setting_i(settings_r *handle, const char *key, int defvalue)
{
- tree234 *tree = (tree234 *)handle;
const char *val;
struct skeyval tmp, *kv;
tmp.key = key;
- if (tree != NULL &&
- (kv = find234(tree, &tmp, NULL)) != NULL) {
+ if (handle != NULL &&
+ (kv = find234(handle->t, &tmp, NULL)) != NULL) {
val = kv->value;
assert(val != NULL);
} else
@@ -426,7 +434,7 @@ int read_setting_i(void *handle, const char *key, int defvalue)
return atoi(val);
}
-FontSpec *read_setting_fontspec(void *handle, const char *name)
+FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
{
/*
* In GTK1-only PuTTY, we used to store font names simply as a
@@ -464,7 +472,7 @@ FontSpec *read_setting_fontspec(void *handle, const char *name)
return NULL;
}
}
-Filename *read_setting_filename(void *handle, const char *name)
+Filename *read_setting_filename(settings_r *handle, const char *name)
{
char *tmp = read_setting_s(handle, name);
if (tmp) {
@@ -475,7 +483,7 @@ Filename *read_setting_filename(void *handle, const char *name)
return NULL;
}
-void write_setting_fontspec(void *handle, const char *name, FontSpec *fs)
+void write_setting_fontspec(settings_w *handle, const char *name, FontSpec *fs)
{
/*
* read_setting_fontspec had to handle two cases, but when
@@ -486,27 +494,28 @@ void write_setting_fontspec(void *handle, const char *name, FontSpec *fs)
write_setting_s(handle, suffname, fs->name);
sfree(suffname);
}
-void write_setting_filename(void *handle, const char *name, Filename *result)
+void write_setting_filename(settings_w *handle,
+ const char *name, Filename *result)
{
write_setting_s(handle, name, result->path);
}
-void close_settings_r(void *handle)
+void close_settings_r(settings_r *handle)
{
- tree234 *tree = (tree234 *)handle;
struct skeyval *kv;
- if (!tree)
+ if (!handle)
return;
- while ( (kv = index234(tree, 0)) != NULL) {
- del234(tree, kv);
+ while ( (kv = index234(handle->t, 0)) != NULL) {
+ del234(handle->t, kv);
sfree((char *)kv->key);
sfree((char *)kv->value);
sfree(kv);
}
- freetree234(tree);
+ freetree234(handle->t);
+ sfree(handle);
}
void del_settings(const char *sessionname)
@@ -517,7 +526,11 @@ void del_settings(const char *sessionname)
sfree(filename);
}
-void *enum_settings_start(void)
+struct settings_e {
+ DIR *dp;
+};
+
+settings_e *enum_settings_start(void)
{
DIR *dp;
char *filename;
@@ -526,12 +539,13 @@ void *enum_settings_start(void)
dp = opendir(filename);
sfree(filename);
- return dp;
+ settings_e *toret = snew(settings_e);
+ toret->dp = dp;
+ return toret;
}
-char *enum_settings_next(void *handle, char *buffer, int buflen)
+char *enum_settings_next(settings_e *handle, char *buffer, int buflen)
{
- DIR *dp = (DIR *)handle;
struct dirent *de;
struct stat st;
char *fullpath;
@@ -541,7 +555,7 @@ char *enum_settings_next(void *handle, char *buffer, int buflen)
fullpath = make_filename(INDEX_SESSIONDIR, NULL);
maxlen = len = strlen(fullpath);
- while ( (de = readdir(dp)) != NULL ) {
+ while ( (de = readdir(handle->dp)) != NULL ) {
thislen = len + 1 + strlen(de->d_name);
if (maxlen < thislen) {
maxlen = thislen;
@@ -566,10 +580,10 @@ char *enum_settings_next(void *handle, char *buffer, int buflen)
return NULL;
}
-void enum_settings_finish(void *handle)
+void enum_settings_finish(settings_e *handle)
{
- DIR *dp = (DIR *)handle;
- closedir(dp);
+ closedir(handle->dp);
+ sfree(handle);
}
/*
diff --git a/windows/winjump.c b/windows/winjump.c
index 7e7b34e8..e42ba275 100644
--- a/windows/winjump.c
+++ b/windows/winjump.c
@@ -383,7 +383,6 @@ static IShellLink *make_shell_link(const char *appname,
{
IShellLink *ret;
char *app_path, *param_string, *desc_string;
- void *psettings_tmp;
IPropertyStore *pPS;
PROPVARIANT pv;
@@ -409,7 +408,7 @@ static IShellLink *make_shell_link(const char *appname,
/* Check if this is a valid session, otherwise don't add. */
if (sessionname) {
- psettings_tmp = open_settings_r(sessionname);
+ settings_r *psettings_tmp = open_settings_r(sessionname);
if (!psettings_tmp) {
sfree(app_path);
return NULL;
diff --git a/windows/winstore.c b/windows/winstore.c
index 26cbf634..7c4be074 100644
--- a/windows/winstore.c
+++ b/windows/winstore.c
@@ -74,7 +74,11 @@ static void unmungestr(const char *in, char *out, int outlen)
return;
}
-void *open_settings_w(const char *sessionname, char **errmsg)
+struct settings_w {
+ HKEY sesskey;
+};
+
+settings_w *open_settings_w(const char *sessionname, char **errmsg)
{
HKEY subkey1, sesskey;
int ret;
@@ -104,29 +108,37 @@ void *open_settings_w(const char *sessionname, char **errmsg)
return NULL;
}
sfree(p);
- return (void *) sesskey;
+
+ settings_w *toret = snew(settings_w);
+ toret->sesskey = sesskey;
+ return toret;
}
-void write_setting_s(void *handle, const char *key, const char *value)
+void write_setting_s(settings_w *handle, const char *key, const char *value)
{
if (handle)
- RegSetValueEx((HKEY) handle, key, 0, REG_SZ, (CONST BYTE *)value,
+ RegSetValueEx(handle->sesskey, key, 0, REG_SZ, (CONST BYTE *)value,
1 + strlen(value));
}
-void write_setting_i(void *handle, const char *key, int value)
+void write_setting_i(settings_w *handle, const char *key, int value)
{
if (handle)
- RegSetValueEx((HKEY) handle, key, 0, REG_DWORD,
+ RegSetValueEx(handle->sesskey, key, 0, REG_DWORD,
(CONST BYTE *) &value, sizeof(value));
}
-void close_settings_w(void *handle)
+void close_settings_w(settings_w *handle)
{
- RegCloseKey((HKEY) handle);
+ RegCloseKey(handle->sesskey);
+ sfree(handle);
}
-void *open_settings_r(const char *sessionname)
+struct settings_r {
+ HKEY sesskey;
+};
+
+settings_r *open_settings_r(const char *sessionname)
{
HKEY subkey1, sesskey;
char *p;
@@ -148,10 +160,12 @@ void *open_settings_r(const char *sessionname)
sfree(p);
- return (void *) sesskey;
+ settings_r *toret = snew(settings_r);
+ toret->sesskey = sesskey;
+ return toret;
}
-char *read_setting_s(void *handle, const char *key)
+char *read_setting_s(settings_r *handle, const char *key)
{
DWORD type, allocsize, size;
char *ret;
@@ -160,14 +174,14 @@ char *read_setting_s(void *handle, const char *key)
return NULL;
/* Find out the type and size of the data. */
- if (RegQueryValueEx((HKEY) handle, key, 0,
+ if (RegQueryValueEx(handle->sesskey, key, 0,
&type, NULL, &size) != ERROR_SUCCESS ||
type != REG_SZ)
return NULL;
allocsize = size+1; /* allow for an extra NUL if needed */
ret = snewn(allocsize, char);
- if (RegQueryValueEx((HKEY) handle, key, 0,
+ if (RegQueryValueEx(handle->sesskey, key, 0,
&type, (BYTE *)ret, &size) != ERROR_SUCCESS ||
type != REG_SZ) {
sfree(ret);
@@ -180,13 +194,13 @@ char *read_setting_s(void *handle, const char *key)
return ret;
}
-int read_setting_i(void *handle, const char *key, int defvalue)
+int read_setting_i(settings_r *handle, const char *key, int defvalue)
{
DWORD type, val, size;
size = sizeof(val);
if (!handle ||
- RegQueryValueEx((HKEY) handle, key, 0, &type,
+ RegQueryValueEx(handle->sesskey, key, 0, &type,
(BYTE *) &val, &size) != ERROR_SUCCESS ||
size != sizeof(val) || type != REG_DWORD)
return defvalue;
@@ -194,7 +208,7 @@ int read_setting_i(void *handle, const char *key, int defvalue)
return val;
}
-FontSpec *read_setting_fontspec(void *handle, const char *name)
+FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
{
char *settingname;
char *fontname;
@@ -234,7 +248,8 @@ FontSpec *read_setting_fontspec(void *handle, const char *name)
return ret;
}
-void write_setting_fontspec(void *handle, const char *name, FontSpec *font)
+void write_setting_fontspec(settings_w *handle,
+ const char *name, FontSpec *font)
{
char *settingname;
@@ -250,7 +265,7 @@ void write_setting_fontspec(void *handle, const char *name, FontSpec *font)
sfree(settingname);
}
-Filename *read_setting_filename(void *handle, const char *name)
+Filename *read_setting_filename(settings_r *handle, const char *name)
{
char *tmp = read_setting_s(handle, name);
if (tmp) {
@@ -261,14 +276,16 @@ Filename *read_setting_filename(void *handle, const char *name)
return NULL;
}
-void write_setting_filename(void *handle, const char *name, Filename *result)
+void write_setting_filename(settings_w *handle,
+ const char *name, Filename *result)
{
write_setting_s(handle, name, result->path);
}
-void close_settings_r(void *handle)
+void close_settings_r(settings_r *handle)
{
- RegCloseKey((HKEY) handle);
+ RegCloseKey(handle->sesskey);
+ sfree(handle);
}
void del_settings(const char *sessionname)
@@ -289,20 +306,20 @@ void del_settings(const char *sessionname)
remove_session_from_jumplist(sessionname);
}
-struct enumsettings {
+struct settings_e {
HKEY key;
int i;
};
-void *enum_settings_start(void)
+settings_e *enum_settings_start(void)
{
- struct enumsettings *ret;
+ settings_e *ret;
HKEY key;
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS)
return NULL;
- ret = snew(struct enumsettings);
+ ret = snew(settings_e);
if (ret) {
ret->key = key;
ret->i = 0;
@@ -311,9 +328,8 @@ void *enum_settings_start(void)
return ret;
}
-char *enum_settings_next(void *handle, char *buffer, int buflen)
+char *enum_settings_next(settings_e *e, char *buffer, int buflen)
{
- struct enumsettings *e = (struct enumsettings *) handle;
char *otherbuf;
otherbuf = snewn(3 * buflen, char);
if (RegEnumKey(e->key, e->i++, otherbuf, 3 * buflen) == ERROR_SUCCESS) {
@@ -326,9 +342,8 @@ char *enum_settings_next(void *handle, char *buffer, int buflen)
}
}
-void enum_settings_finish(void *handle)
+void enum_settings_finish(settings_e *e)
{
- struct enumsettings *e = (struct enumsettings *) handle;
RegCloseKey(e->key);
sfree(e);
}
@@ -656,7 +671,7 @@ static int transform_jumplist_registry
(const char *add, const char *rem, char **out)
{
int ret;
- HKEY pjumplist_key, psettings_tmp;
+ HKEY pjumplist_key;
DWORD type;
DWORD value_length;
char *old_value, *new_value;
@@ -741,7 +756,7 @@ static int transform_jumplist_registry
while (*piterator_old != '\0') {
if (!rem || strcmp(piterator_old, rem) != 0) {
/* Check if this is a valid session, otherwise don't add. */
- psettings_tmp = open_settings_r(piterator_old);
+ settings_r *psettings_tmp = open_settings_r(piterator_old);
if (psettings_tmp != NULL) {
close_settings_r(psettings_tmp);
strcpy(piterator_new, piterator_old);
From 03fb4423af303637029ad1318884a207d6aa66da Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 14 Sep 2018 08:48:54 +0100
Subject: [PATCH 406/607] Expose the 'dh_ctx' struct tag used for
Diffie-Hellman.
---
ssh.c | 32 ++++++++++++++++----------------
ssh.h | 13 +++++++------
sshdh.c | 16 ++++++----------
3 files changed, 29 insertions(+), 32 deletions(-)
diff --git a/ssh.c b/ssh.c
index ab6d6fbe..f4061894 100644
--- a/ssh.c
+++ b/ssh.c
@@ -691,7 +691,7 @@ struct ssh_tag {
int v2_session_id_len;
int v2_cbc_ignore_workaround;
int v2_out_cipherblksize;
- void *kex_ctx;
+ struct dh_ctx *dh_ctx;
int bare_connection;
int attempting_connshare;
@@ -5787,12 +5787,12 @@ static void do_ssh2_transport(void *vctx)
bombout(("unable to read mp-ints from incoming group packet"));
crStopV;
}
- ssh->kex_ctx = dh_setup_gex(s->p, s->g);
+ ssh->dh_ctx = dh_setup_gex(s->p, s->g);
s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
} else {
ssh->pls.kctx = SSH2_PKTCTX_DHGROUP;
- ssh->kex_ctx = dh_setup_group(ssh->kex);
+ ssh->dh_ctx = dh_setup_group(ssh->kex);
s->kex_init_value = SSH2_MSG_KEXDH_INIT;
s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",
@@ -5805,7 +5805,7 @@ static void do_ssh2_transport(void *vctx)
* Now generate and send e for Diffie-Hellman.
*/
set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
- s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
+ s->e = dh_create_e(ssh->dh_ctx, s->nbits * 2);
s->pktout = ssh_bpp_new_pktout(ssh->bpp, s->kex_init_value);
put_mp_ssh2(s->pktout, s->e);
ssh_pkt_write(ssh, s->pktout);
@@ -5827,13 +5827,13 @@ static void do_ssh2_transport(void *vctx)
}
{
- const char *err = dh_validate_f(ssh->kex_ctx, s->f);
+ const char *err = dh_validate_f(ssh->dh_ctx, s->f);
if (err) {
bombout(("key exchange reply failed validation: %s", err));
crStopV;
}
}
- s->K = dh_find_K(ssh->kex_ctx, s->f);
+ s->K = dh_find_K(ssh->dh_ctx, s->f);
/* We assume everything from now on will be quick, and it might
* involve user interaction. */
@@ -5852,7 +5852,7 @@ static void do_ssh2_transport(void *vctx)
put_mp_ssh2(ssh->exhash, s->e);
put_mp_ssh2(ssh->exhash, s->f);
- dh_cleanup(ssh->kex_ctx);
+ dh_cleanup(ssh->dh_ctx);
freebn(s->f);
if (dh_is_gex(ssh->kex)) {
freebn(s->g);
@@ -5972,9 +5972,9 @@ static void do_ssh2_transport(void *vctx)
bombout(("unable to read mp-ints from incoming group packet"));
crStopV;
}
- ssh->kex_ctx = dh_setup_gex(s->p, s->g);
+ ssh->dh_ctx = dh_setup_gex(s->p, s->g);
} else {
- ssh->kex_ctx = dh_setup_group(ssh->kex);
+ ssh->dh_ctx = dh_setup_group(ssh->kex);
logeventf(ssh, "Using GSSAPI (with Kerberos V5) Diffie-Hellman with standard group \"%s\"",
ssh->kex->groupname);
}
@@ -5983,7 +5983,7 @@ static void do_ssh2_transport(void *vctx)
ssh->kex->hash->text_name);
/* Now generate e for Diffie-Hellman. */
set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
- s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
+ s->e = dh_create_e(ssh->dh_ctx, s->nbits * 2);
if (ssh->gsslib->gsslogmsg)
logevent(ssh->gsslib->gsslogmsg);
@@ -6137,7 +6137,7 @@ static void do_ssh2_transport(void *vctx)
s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED ||
!s->complete_rcvd);
- s->K = dh_find_K(ssh->kex_ctx, s->f);
+ s->K = dh_find_K(ssh->dh_ctx, s->f);
/* We assume everything from now on will be quick, and it might
* involve user interaction. */
@@ -6162,7 +6162,7 @@ static void do_ssh2_transport(void *vctx)
* used as the MIC input.
*/
- dh_cleanup(ssh->kex_ctx);
+ dh_cleanup(ssh->dh_ctx);
freebn(s->f);
if (dh_is_gex(ssh->kex)) {
freebn(s->g);
@@ -6313,7 +6313,7 @@ static void do_ssh2_transport(void *vctx)
}
#endif
- ssh->kex_ctx = NULL;
+ ssh->dh_ctx = NULL;
#if 0
debug(("Exchange hash is:\n"));
@@ -10554,7 +10554,7 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
ssh->version = 0; /* when not ready yet */
ssh->s = NULL;
ssh->kex = NULL;
- ssh->kex_ctx = NULL;
+ ssh->dh_ctx = NULL;
ssh->hostkey_alg = NULL;
ssh->hostkey_str = NULL;
ssh->exitcode = -1;
@@ -10711,8 +10711,8 @@ static void ssh_free(Backend *be)
struct X11FakeAuth *auth;
int need_random_unref;
- if (ssh->kex_ctx)
- dh_cleanup(ssh->kex_ctx);
+ if (ssh->dh_ctx)
+ dh_cleanup(ssh->dh_ctx);
sfree(ssh->savedhost);
while (ssh->queuelen-- > 0)
diff --git a/ssh.h b/ssh.h
index 0b2d78ec..377c0568 100644
--- a/ssh.h
+++ b/ssh.h
@@ -874,12 +874,13 @@ void diagbn(char *prefix, Bignum md);
#endif
int dh_is_gex(const struct ssh_kex *kex);
-void *dh_setup_group(const struct ssh_kex *kex);
-void *dh_setup_gex(Bignum pval, Bignum gval);
-void dh_cleanup(void *);
-Bignum dh_create_e(void *, int nbits);
-const char *dh_validate_f(void *handle, Bignum f);
-Bignum dh_find_K(void *, Bignum f);
+struct dh_ctx;
+struct dh_ctx *dh_setup_group(const struct ssh_kex *kex);
+struct dh_ctx *dh_setup_gex(Bignum pval, Bignum gval);
+void dh_cleanup(struct dh_ctx *);
+Bignum dh_create_e(struct dh_ctx *, int nbits);
+const char *dh_validate_f(struct dh_ctx *, Bignum f);
+Bignum dh_find_K(struct dh_ctx *, Bignum f);
int rsa_ssh1_encrypted(const Filename *filename, char **comment);
int rsa_ssh1_loadpub(const Filename *filename, BinarySink *bs,
diff --git a/sshdh.c b/sshdh.c
index 1c27b021..c18da320 100644
--- a/sshdh.c
+++ b/sshdh.c
@@ -187,7 +187,7 @@ int dh_is_gex(const struct ssh_kex *kex)
/*
* Initialise DH for a standard group.
*/
-void *dh_setup_group(const struct ssh_kex *kex)
+struct dh_ctx *dh_setup_group(const struct ssh_kex *kex)
{
const struct dh_extra *extra = (const struct dh_extra *)kex->extra;
struct dh_ctx *ctx = snew(struct dh_ctx);
@@ -200,7 +200,7 @@ void *dh_setup_group(const struct ssh_kex *kex)
/*
* Initialise DH for a server-supplied group.
*/
-void *dh_setup_gex(Bignum pval, Bignum gval)
+struct dh_ctx *dh_setup_gex(Bignum pval, Bignum gval)
{
struct dh_ctx *ctx = snew(struct dh_ctx);
ctx->p = copybn(pval);
@@ -212,9 +212,8 @@ void *dh_setup_gex(Bignum pval, Bignum gval)
/*
* Clean up and free a context.
*/
-void dh_cleanup(void *handle)
+void dh_cleanup(struct dh_ctx *ctx)
{
- struct dh_ctx *ctx = (struct dh_ctx *)handle;
freebn(ctx->x);
freebn(ctx->e);
freebn(ctx->p);
@@ -239,9 +238,8 @@ void dh_cleanup(void *handle)
* Advances in Cryptology: Proceedings of Eurocrypt '96
* Springer-Verlag, May 1996.
*/
-Bignum dh_create_e(void *handle, int nbits)
+Bignum dh_create_e(struct dh_ctx *ctx, int nbits)
{
- struct dh_ctx *ctx = (struct dh_ctx *)handle;
int i;
int nbytes;
@@ -295,9 +293,8 @@ Bignum dh_create_e(void *handle, int nbits)
* they lead to obviously weak keys that even a passive eavesdropper
* can figure out.)
*/
-const char *dh_validate_f(void *handle, Bignum f)
+const char *dh_validate_f(struct dh_ctx *ctx, Bignum f)
{
- struct dh_ctx *ctx = (struct dh_ctx *)handle;
if (bignum_cmp(f, One) <= 0) {
return "f value received is too small";
} else {
@@ -313,9 +310,8 @@ const char *dh_validate_f(void *handle, Bignum f)
/*
* DH stage 2: given a number f, compute K = f^x mod p.
*/
-Bignum dh_find_K(void *handle, Bignum f)
+Bignum dh_find_K(struct dh_ctx *ctx, Bignum f)
{
- struct dh_ctx *ctx = (struct dh_ctx *)handle;
Bignum ret;
ret = modpow(f, ctx->x, ctx->p);
return ret;
From d437e5402ead209315c38cd9daff8cce8b4d7966 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 14 Sep 2018 09:16:41 +0100
Subject: [PATCH 407/607] Make ssh_compress into a pair of linked classoids.
This was mildly fiddly because there's a single vtable structure that
implements two distinct interface types, one for compression and one
for decompression - and I have actually confused them before now
(commit d4304f1b7), so I think it's important to make them actually be
separate types!
---
ssh.c | 32 +++++++++++++++++----------
ssh.h | 50 ++++++++++++++++++++++--------------------
ssh1bpp.c | 21 +++++++++---------
ssh2bpp.c | 57 ++++++++++++++++++++++++------------------------
sshbpp.h | 4 ++--
sshzlib.c | 65 +++++++++++++++++++++++++++++++++++--------------------
6 files changed, 130 insertions(+), 99 deletions(-)
diff --git a/ssh.c b/ssh.c
index f4061894..29f25466 100644
--- a/ssh.c
+++ b/ssh.c
@@ -335,31 +335,39 @@ const static struct ssh2_macalg *const buggymacs[] = {
&ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
};
-static void *ssh_comp_none_init(void)
+static ssh_compressor *ssh_comp_none_init(void)
{
return NULL;
}
-static void ssh_comp_none_cleanup(void *handle)
+static void ssh_comp_none_cleanup(ssh_compressor *handle)
{
}
-static void ssh_comp_none_block(void *handle, unsigned char *block, int len,
+static ssh_decompressor *ssh_decomp_none_init(void)
+{
+ return NULL;
+}
+static void ssh_decomp_none_cleanup(ssh_decompressor *handle)
+{
+}
+static void ssh_comp_none_block(ssh_compressor *handle,
+ unsigned char *block, int len,
unsigned char **outblock, int *outlen,
int minlen)
{
}
-static int ssh_decomp_none_block(void *handle, unsigned char *block, int len,
+static int ssh_decomp_none_block(ssh_decompressor *handle,
+ unsigned char *block, int len,
unsigned char **outblock, int *outlen)
{
return 0;
}
-const static struct ssh_compress ssh_comp_none = {
+const static struct ssh_compression_alg ssh_comp_none = {
"none", NULL,
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
- ssh_comp_none_init, ssh_comp_none_cleanup, ssh_decomp_none_block,
+ ssh_decomp_none_init, ssh_decomp_none_cleanup, ssh_decomp_none_block,
NULL
};
-extern const struct ssh_compress ssh_zlib;
-const static struct ssh_compress *const compressions[] = {
+const static struct ssh_compression_alg *const compressions[] = {
&ssh_zlib, &ssh_comp_none
};
@@ -4853,7 +4861,7 @@ struct kexinit_algorithm {
const struct ssh2_macalg *mac;
int etm;
} mac;
- const struct ssh_compress *comp;
+ const struct ssh_compression_alg *comp;
} u;
};
@@ -5025,7 +5033,7 @@ static void do_ssh2_transport(void *vctx)
const struct ssh2_cipheralg *cipher;
const struct ssh2_macalg *mac;
int etm_mode;
- const struct ssh_compress *comp;
+ const struct ssh_compression_alg *comp;
} in, out;
ptrlen hostkeydata, sigdata;
char *keystr, *fingerprint;
@@ -5042,7 +5050,7 @@ static void do_ssh2_transport(void *vctx)
int preferred_hk[HK_MAX];
int n_preferred_ciphers;
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
- const struct ssh_compress *preferred_comp;
+ const struct ssh_compression_alg *preferred_comp;
int userauth_succeeded; /* for delayed compression */
int pending_compression;
int got_session_id;
@@ -5413,7 +5421,7 @@ static void do_ssh2_transport(void *vctx)
alg->u.comp = s->preferred_comp;
}
for (i = 0; i < lenof(compressions); i++) {
- const struct ssh_compress *c = compressions[i];
+ const struct ssh_compression_alg *c = compressions[i];
alg = ssh2_kexinit_addalg(s->kexlists[j], c->name);
alg->u.comp = c;
if (s->userauth_succeeded && c->delayed_name) {
diff --git a/ssh.h b/ssh.h
index 377c0568..7f038b9c 100644
--- a/ssh.h
+++ b/ssh.h
@@ -603,23 +603,39 @@ struct ssh_keyalg {
#define ssh_key_ssh_id(key) ((*(key))->ssh_id)
#define ssh_key_cache_id(key) ((*(key))->cache_id)
-struct ssh_compress {
+typedef struct ssh_compressor {
+ const struct ssh_compression_alg *vt;
+} ssh_compressor;
+typedef struct ssh_decompressor {
+ const struct ssh_compression_alg *vt;
+} ssh_decompressor;
+
+struct ssh_compression_alg {
const char *name;
/* For zlib@openssh.com: if non-NULL, this name will be considered once
* userauth has completed successfully. */
const char *delayed_name;
- void *(*compress_init) (void);
- void (*compress_cleanup) (void *);
- void (*compress) (void *, unsigned char *block, int len,
- unsigned char **outblock, int *outlen,
- int minlen);
- void *(*decompress_init) (void);
- void (*decompress_cleanup) (void *);
- int (*decompress) (void *, unsigned char *block, int len,
- unsigned char **outblock, int *outlen);
+ ssh_compressor *(*compress_new)(void);
+ void (*compress_free)(ssh_compressor *);
+ void (*compress)(ssh_compressor *, unsigned char *block, int len,
+ unsigned char **outblock, int *outlen,
+ int minlen);
+ ssh_decompressor *(*decompress_new)(void);
+ void (*decompress_free)(ssh_decompressor *);
+ int (*decompress)(ssh_decompressor *, unsigned char *block, int len,
+ unsigned char **outblock, int *outlen);
const char *text_name;
};
+#define ssh_compressor_new(alg) ((alg)->compress_new())
+#define ssh_compressor_free(comp) ((comp)->vt->compress_free(comp))
+#define ssh_compressor_compress(comp, in, inlen, out, outlen, minlen) \
+ ((comp)->vt->compress(comp, in, inlen, out, outlen, minlen))
+#define ssh_decompressor_new(alg) ((alg)->decompress_new())
+#define ssh_decompressor_free(comp) ((comp)->vt->decompress_free(comp))
+#define ssh_decompressor_decompress(comp, in, inlen, out, outlen) \
+ ((comp)->vt->decompress(comp, in, inlen, out, outlen))
+
struct ssh2_userkey {
ssh_key *key; /* the key itself */
char *comment; /* the key comment */
@@ -659,6 +675,7 @@ extern const struct ssh2_macalg ssh_hmac_sha1_buggy;
extern const struct ssh2_macalg ssh_hmac_sha1_96;
extern const struct ssh2_macalg ssh_hmac_sha1_96_buggy;
extern const struct ssh2_macalg ssh_hmac_sha256;
+extern const struct ssh_compression_alg ssh_zlib;
typedef struct AESContext AESContext;
AESContext *aes_make_context(void);
@@ -1017,19 +1034,6 @@ Bignum primegen(int bits, int modulus, int residue, Bignum factor,
int phase, progfn_t pfn, void *pfnparam, unsigned firstbits);
void invent_firstbits(unsigned *one, unsigned *two);
-
-/*
- * zlib compression.
- */
-void *zlib_compress_init(void);
-void zlib_compress_cleanup(void *);
-void *zlib_decompress_init(void);
-void zlib_decompress_cleanup(void *);
-void zlib_compress_block(void *, unsigned char *block, int len,
- unsigned char **outblock, int *outlen, int minlen);
-int zlib_decompress_block(void *, unsigned char *block, int len,
- unsigned char **outblock, int *outlen);
-
/*
* Connection-sharing API provided by platforms. This function must
* either:
diff --git a/ssh1bpp.c b/ssh1bpp.c
index 627d55a5..a1fd78c9 100644
--- a/ssh1bpp.c
+++ b/ssh1bpp.c
@@ -21,7 +21,8 @@ struct ssh1_bpp_state {
struct crcda_ctx *crcda_ctx;
- void *compctx, *decompctx;
+ ssh_compressor *compctx;
+ ssh_decompressor *decompctx;
BinaryPacketProtocol bpp;
};
@@ -52,9 +53,9 @@ static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
if (s->cipher)
ssh1_cipher_free(s->cipher);
if (s->compctx)
- zlib_compress_cleanup(s->compctx);
+ ssh_compressor_free(s->compctx);
if (s->decompctx)
- zlib_decompress_cleanup(s->decompctx);
+ ssh_decompressor_free(s->decompctx);
if (s->crcda_ctx)
crcda_free_context(s->crcda_ctx);
if (s->pktin)
@@ -90,8 +91,8 @@ void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp)
assert(!s->compctx);
assert(!s->decompctx);
- s->compctx = zlib_compress_init();
- s->decompctx = zlib_decompress_init();
+ s->compctx = ssh_compressor_new(&ssh_zlib);
+ s->decompctx = ssh_decompressor_new(&ssh_zlib);
}
static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
@@ -157,9 +158,9 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
if (s->decompctx) {
unsigned char *decompblk;
int decomplen;
- if (!zlib_decompress_block(s->decompctx,
- s->data + s->pad, s->length + 1,
- &decompblk, &decomplen)) {
+ if (!ssh_decompressor_decompress(
+ s->decompctx, s->data + s->pad, s->length + 1,
+ &decompblk, &decomplen)) {
s->bpp.error = dupprintf(
"Zlib decompression encountered invalid data");
crStopV;
@@ -248,8 +249,8 @@ static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
if (s->compctx) {
unsigned char *compblk;
int complen;
- zlib_compress_block(s->compctx, pkt->data + 12, pkt->length - 12,
- &compblk, &complen, 0);
+ ssh_compressor_compress(s->compctx, pkt->data + 12, pkt->length - 12,
+ &compblk, &complen, 0);
/* Replace the uncompressed packet data with the compressed
* version. */
pkt->length = 12;
diff --git a/ssh2bpp.c b/ssh2bpp.c
index ff957b05..0b95d7fe 100644
--- a/ssh2bpp.c
+++ b/ssh2bpp.c
@@ -14,8 +14,6 @@ struct ssh2_bpp_direction {
ssh2_cipher *cipher;
ssh2_mac *mac;
int etm_mode;
- const struct ssh_compress *comp;
- void *comp_ctx;
};
struct ssh2_bpp_state {
@@ -28,6 +26,11 @@ struct ssh2_bpp_state {
PktIn *pktin;
struct ssh2_bpp_direction in, out;
+ /* comp and decomp logically belong in the per-direction
+ * substructure, except that they have different types */
+ ssh_decompressor *in_decomp;
+ ssh_compressor *out_comp;
+
int pending_newkeys;
BinaryPacketProtocol bpp;
@@ -61,14 +64,14 @@ static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
ssh2_cipher_free(s->out.cipher);
if (s->out.mac)
ssh2_mac_free(s->out.mac);
- if (s->out.comp_ctx)
- s->out.comp->compress_cleanup(s->out.comp_ctx);
+ if (s->out_comp)
+ ssh_compressor_free(s->out_comp);
if (s->in.cipher)
ssh2_cipher_free(s->in.cipher);
if (s->in.mac)
ssh2_mac_free(s->in.mac);
- if (s->in.comp_ctx)
- s->in.comp->decompress_cleanup(s->in.comp_ctx);
+ if (s->in_decomp)
+ ssh_decompressor_free(s->in_decomp);
if (s->pktin)
ssh_unref_packet(s->pktin);
sfree(s);
@@ -78,7 +81,7 @@ void ssh2_bpp_new_outgoing_crypto(
BinaryPacketProtocol *bpp,
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
- const struct ssh_compress *compression)
+ const struct ssh_compression_alg *compression)
{
struct ssh2_bpp_state *s;
assert(bpp->vt == &ssh2_bpp_vtable);
@@ -88,8 +91,8 @@ void ssh2_bpp_new_outgoing_crypto(
ssh2_cipher_free(s->out.cipher);
if (s->out.mac)
ssh2_mac_free(s->out.mac);
- if (s->out.comp_ctx)
- s->out.comp->compress_cleanup(s->out.comp_ctx);
+ if (s->out_comp)
+ ssh_compressor_free(s->out_comp);
if (cipher) {
s->out.cipher = ssh2_cipher_new(cipher);
@@ -106,18 +109,17 @@ void ssh2_bpp_new_outgoing_crypto(
s->out.mac = NULL;
}
- s->out.comp = compression;
- /* out_comp is always non-NULL, because no compression is
- * indicated by ssh_comp_none. So compress_init always exists, but
- * it may return a null out_comp_ctx. */
- s->out.comp_ctx = compression->compress_init();
+ /* 'compression' is always non-NULL, because no compression is
+ * indicated by ssh_comp_none. But this setup call may return a
+ * null out_comp. */
+ s->out_comp = ssh_compressor_new(compression);
}
void ssh2_bpp_new_incoming_crypto(
BinaryPacketProtocol *bpp,
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
- const struct ssh_compress *compression)
+ const struct ssh_compression_alg *compression)
{
struct ssh2_bpp_state *s;
assert(bpp->vt == &ssh2_bpp_vtable);
@@ -127,8 +129,8 @@ void ssh2_bpp_new_incoming_crypto(
ssh2_cipher_free(s->in.cipher);
if (s->in.mac)
ssh2_mac_free(s->in.mac);
- if (s->in.comp_ctx)
- s->in.comp->decompress_cleanup(s->in.comp_ctx);
+ if (s->in_decomp)
+ ssh_decompressor_free(s->in_decomp);
if (cipher) {
s->in.cipher = ssh2_cipher_new(cipher);
@@ -145,11 +147,10 @@ void ssh2_bpp_new_incoming_crypto(
s->in.mac = NULL;
}
- s->in.comp = compression;
- /* in_comp is always non-NULL, because no compression is
- * indicated by ssh_comp_none. So compress_init always exists, but
- * it may return a null in_comp_ctx. */
- s->in.comp_ctx = compression->decompress_init();
+ /* 'compression' is always non-NULL, because no compression is
+ * indicated by ssh_comp_none. But this setup call may return a
+ * null in_decomp. */
+ s->in_decomp = ssh_decompressor_new(compression);
/* Clear the pending_newkeys flag, so that handle_input below will
* start consuming the input data again. */
@@ -419,8 +420,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
{
unsigned char *newpayload;
int newlen;
- if (s->in.comp && s->in.comp->decompress(
- s->in.comp_ctx, s->data + 5, s->length - 5,
+ if (s->in_decomp && ssh_decompressor_decompress(
+ s->in_decomp, s->data + 5, s->length - 5,
&newpayload, &newlen)) {
if (s->maxlen < newlen + 5) {
PktIn *old_pktin = s->pktin;
@@ -526,7 +527,7 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
cipherblk = s->out.cipher ? ssh2_cipher_alg(s->out.cipher)->blksize : 8;
cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
- if (s->out.comp && s->out.comp_ctx) {
+ if (s->out_comp) {
unsigned char *newpayload;
int minlen, newlen;
@@ -544,8 +545,8 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
minlen -= 8; /* length field + min padding */
}
- s->out.comp->compress(s->out.comp_ctx, pkt->data + 5, pkt->length - 5,
- &newpayload, &newlen, minlen);
+ ssh_compressor_compress(s->out_comp, pkt->data + 5, pkt->length - 5,
+ &newpayload, &newlen, minlen);
pkt->length = 5;
put_data(pkt, newpayload, newlen);
sfree(newpayload);
@@ -608,7 +609,7 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
{
struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
- if (pkt->minlen > 0 && !(s->out.comp && s->out.comp_ctx)) {
+ if (pkt->minlen > 0 && !s->out_comp) {
/*
* If we've been told to pad the packet out to a given minimum
* length, but we're not compressing (and hence can't get the
diff --git a/sshbpp.h b/sshbpp.h
index f7e2fe2c..6e45262e 100644
--- a/sshbpp.h
+++ b/sshbpp.h
@@ -41,12 +41,12 @@ void ssh2_bpp_new_outgoing_crypto(
BinaryPacketProtocol *bpp,
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
- const struct ssh_compress *compression);
+ const struct ssh_compression_alg *compression);
void ssh2_bpp_new_incoming_crypto(
BinaryPacketProtocol *bpp,
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
- const struct ssh_compress *compression);
+ const struct ssh_compression_alg *compression);
BinaryPacketProtocol *ssh2_bare_bpp_new(void);
diff --git a/sshzlib.c b/sshzlib.c
index 17550477..ec8708b5 100644
--- a/sshzlib.c
+++ b/sshzlib.c
@@ -66,6 +66,10 @@
#define TRUE 1
#endif
+typedef struct { const struct dummy *vt; } ssh_compressor;
+typedef struct { const struct dummy *vt; } ssh_decompressor;
+static const struct dummy { int i; } ssh_zlib;
+
#else
#include "ssh.h"
#endif
@@ -600,37 +604,45 @@ static void zlib_match(struct LZ77Context *ectx, int distance, int len)
}
}
-void *zlib_compress_init(void)
+struct ssh_zlib_compressor {
+ struct LZ77Context ectx;
+ ssh_compressor sc;
+};
+
+ssh_compressor *zlib_compress_init(void)
{
struct Outbuf *out;
- struct LZ77Context *ectx = snew(struct LZ77Context);
+ struct ssh_zlib_compressor *comp = snew(struct ssh_zlib_compressor);
- lz77_init(ectx);
- ectx->literal = zlib_literal;
- ectx->match = zlib_match;
+ lz77_init(&comp->ectx);
+ comp->sc.vt = &ssh_zlib;
+ comp->ectx.literal = zlib_literal;
+ comp->ectx.match = zlib_match;
out = snew(struct Outbuf);
out->outbits = out->noutbits = 0;
out->firstblock = 1;
- ectx->userdata = out;
+ comp->ectx.userdata = out;
- return ectx;
+ return &comp->sc;
}
-void zlib_compress_cleanup(void *handle)
+void zlib_compress_cleanup(ssh_compressor *sc)
{
- struct LZ77Context *ectx = (struct LZ77Context *)handle;
- sfree(ectx->userdata);
- sfree(ectx->ictx);
- sfree(ectx);
+ struct ssh_zlib_compressor *comp =
+ FROMFIELD(sc, struct ssh_zlib_compressor, sc);
+ sfree(comp->ectx.userdata);
+ sfree(comp->ectx.ictx);
+ sfree(comp);
}
-void zlib_compress_block(void *handle, unsigned char *block, int len,
+void zlib_compress_block(ssh_compressor *sc, unsigned char *block, int len,
unsigned char **outblock, int *outlen,
int minlen)
{
- struct LZ77Context *ectx = (struct LZ77Context *)handle;
- struct Outbuf *out = (struct Outbuf *) ectx->userdata;
+ struct ssh_zlib_compressor *comp =
+ FROMFIELD(sc, struct ssh_zlib_compressor, sc);
+ struct Outbuf *out = (struct Outbuf *) comp->ectx.userdata;
int in_block;
out->outbuf = NULL;
@@ -662,7 +674,7 @@ void zlib_compress_block(void *handle, unsigned char *block, int len,
/*
* Do the compression.
*/
- lz77_compress(ectx, block, len, TRUE);
+ lz77_compress(&comp->ectx, block, len, TRUE);
/*
* End the block (by transmitting code 256, which is
@@ -875,9 +887,11 @@ struct zlib_decompress_ctx {
int winpos;
unsigned char *outblk;
int outlen, outsize;
+
+ ssh_decompressor dc;
};
-void *zlib_decompress_init(void)
+ssh_decompressor *zlib_decompress_init(void)
{
struct zlib_decompress_ctx *dctx = snew(struct zlib_decompress_ctx);
unsigned char lengths[288];
@@ -895,12 +909,14 @@ void *zlib_decompress_init(void)
dctx->nbits = 0;
dctx->winpos = 0;
- return dctx;
+ dctx->dc.vt = &ssh_zlib;
+ return &dctx->dc;
}
-void zlib_decompress_cleanup(void *handle)
+void zlib_decompress_cleanup(ssh_decompressor *dc)
{
- struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle;
+ struct zlib_decompress_ctx *dctx =
+ FROMFIELD(dc, struct zlib_decompress_ctx, dc);
if (dctx->currlentable && dctx->currlentable != dctx->staticlentable)
zlib_freetable(&dctx->currlentable);
@@ -958,10 +974,11 @@ static void zlib_emit_char(struct zlib_decompress_ctx *dctx, int c)
#define EATBITS(n) ( dctx->nbits -= (n), dctx->bits >>= (n) )
-int zlib_decompress_block(void *handle, unsigned char *block, int len,
+int zlib_decompress_block(ssh_decompressor *dc, unsigned char *block, int len,
unsigned char **outblock, int *outlen)
{
- struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle;
+ struct zlib_decompress_ctx *dctx =
+ FROMFIELD(dc, struct zlib_decompress_ctx, dc);
const coderecord *rec;
int code, blktype, rep, dist, nlen, header;
static const unsigned char lenlenmap[] = {
@@ -1217,7 +1234,7 @@ int main(int argc, char **argv)
{
unsigned char buf[16], *outbuf;
int ret, outlen;
- void *handle;
+ ssh_decompressor *handle;
int noheader = FALSE, opts = TRUE;
char *filename = NULL;
FILE *fp;
@@ -1289,7 +1306,7 @@ int main(int argc, char **argv)
#else
-const struct ssh_compress ssh_zlib = {
+const struct ssh_compression_alg ssh_zlib = {
"zlib",
"zlib@openssh.com", /* delayed version */
zlib_compress_init,
From aa08e6ca9157db234142abc383176475c48c0537 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 14 Sep 2018 13:47:13 +0100
Subject: [PATCH 408/607] Put a layer of abstraction in front of struct
ssh_channel.
Clients outside ssh.c - all implementations of Channel - will now not
see the ssh_channel data type itself, but only a subobject of the
interface type SshChannel. All the sshfwd_* functions have become
methods in that interface type's vtable (though, wrapped in the usual
kind of macros, the call sites look identical).
This paves the way for me to split up the SSH-1 and SSH-2 connection
layers and have each one lay out its channel bookkeeping structure as
it sees fit; as long as they each provide an implementation of the
sshfwd_ method family, the types behind that need not look different.
A minor good effect of this is that the sshfwd_ methods are no longer
global symbols, so they don't have to be stubbed in Unix Pageant to
get it to compile.
---
agentf.c | 4 +--
defs.h | 1 +
portfwd.c | 8 ++---
ssh.c | 89 +++++++++++++++++++++++++++++++++++----------------
ssh.h | 23 +++----------
sshchan.h | 36 ++++++++++++++++++++-
unix/uxpgnt.c | 14 --------
x11fwd.c | 4 +--
8 files changed, 110 insertions(+), 69 deletions(-)
diff --git a/agentf.c b/agentf.c
index 65a971da..dbc1e4af 100644
--- a/agentf.c
+++ b/agentf.c
@@ -12,7 +12,7 @@
#include "sshchan.h"
typedef struct agentf {
- struct ssh_channel *c;
+ SshChannel *c;
bufchain inbuffer;
agent_pending_query *pending;
int input_wanted;
@@ -158,7 +158,7 @@ static const struct ChannelVtable agentf_channelvt = {
chan_no_eager_close,
};
-Channel *agentf_new(struct ssh_channel *c)
+Channel *agentf_new(SshChannel *c)
{
agentf *af = snew(agentf);
af->c = c;
diff --git a/defs.h b/defs.h
index ba61708b..e6efe0a9 100644
--- a/defs.h
+++ b/defs.h
@@ -54,6 +54,7 @@ typedef struct Frontend Frontend;
typedef struct ssh_tag *Ssh;
typedef struct Channel Channel;
+typedef struct SshChannel SshChannel;
typedef struct ssh_sharing_state ssh_sharing_state;
typedef struct ssh_sharing_connstate ssh_sharing_connstate;
diff --git a/portfwd.c b/portfwd.c
index d7ad74d2..53d5234a 100644
--- a/portfwd.c
+++ b/portfwd.c
@@ -23,7 +23,7 @@ typedef enum {
} SocksState;
typedef struct PortForwarding {
- struct ssh_channel *c; /* channel structure held by ssh.c */
+ SshChannel *c; /* channel structure held by SSH backend */
Ssh ssh; /* instance of SSH backend itself */
/* Note that ssh need not be filled in if c is non-NULL */
Socket s;
@@ -146,11 +146,11 @@ static void pfl_closing(Plug plug, const char *error_msg, int error_code,
pfl_terminate(pl);
}
-static struct ssh_channel *wrap_send_port_open(
+static SshChannel *wrap_send_port_open(
Ssh ssh, const char *hostname, int port, Socket s, Channel *chan)
{
char *peerinfo, *description;
- struct ssh_channel *toret;
+ SshChannel *toret;
peerinfo = sk_peer_info(s);
if (peerinfo) {
@@ -455,7 +455,7 @@ static const struct ChannelVtable PortForwarding_channelvt = {
* dynamically allocated error message string.
*/
char *pfd_connect(Channel **chan_ret, char *hostname,int port,
- struct ssh_channel *c, Conf *conf, int addressfamily)
+ SshChannel *c, Conf *conf, int addressfamily)
{
SockAddr addr;
const char *err;
diff --git a/ssh.c b/ssh.c
index 29f25466..eaf1f386 100644
--- a/ssh.c
+++ b/ssh.c
@@ -471,6 +471,28 @@ struct ssh_channel {
ssh_sharing_connstate *sharectx; /* sharing context, if this is a
* downstream channel */
Channel *chan; /* handle the client side of this channel, if not */
+ SshChannel sc; /* entry point for chan to talk back to */
+};
+
+static int sshchannel_write(SshChannel *c, const void *buf, int len);
+static void sshchannel_write_eof(SshChannel *c);
+static void sshchannel_unclean_close(SshChannel *c, const char *err);
+static void sshchannel_unthrottle(SshChannel *c, int bufsize);
+static Conf *sshchannel_get_conf(SshChannel *c);
+static void sshchannel_window_override_removed(SshChannel *c);
+static void sshchannel_x11_sharing_handover(
+ SshChannel *c, ssh_sharing_connstate *share_cs, share_channel *share_chan,
+ const char *peer_addr, int peer_port, int endian,
+ int protomajor, int protominor, const void *initial_data, int initial_len);
+
+const struct SshChannelVtable sshchannel_vtable = {
+ sshchannel_write,
+ sshchannel_write_eof,
+ sshchannel_unclean_close,
+ sshchannel_unthrottle,
+ sshchannel_get_conf,
+ sshchannel_window_override_removed,
+ sshchannel_x11_sharing_handover,
};
/*
@@ -3665,14 +3687,16 @@ static void ssh_channel_try_eof(struct ssh_channel *c)
}
}
-Conf *sshfwd_get_conf(struct ssh_channel *c)
+static Conf *sshchannel_get_conf(SshChannel *sc)
{
+ struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
Ssh ssh = c->ssh;
return ssh->conf;
}
-void sshfwd_write_eof(struct ssh_channel *c)
+static void sshchannel_write_eof(SshChannel *sc)
{
+ struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
Ssh ssh = c->ssh;
if (ssh->state == SSH_STATE_CLOSED)
@@ -3685,8 +3709,9 @@ void sshfwd_write_eof(struct ssh_channel *c)
ssh_channel_try_eof(c);
}
-void sshfwd_unclean_close(struct ssh_channel *c, const char *err)
+static void sshchannel_unclean_close(SshChannel *sc, const char *err)
{
+ struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
Ssh ssh = c->ssh;
char *reason;
@@ -3701,8 +3726,9 @@ void sshfwd_unclean_close(struct ssh_channel *c, const char *err)
ssh2_channel_check_close(c);
}
-int sshfwd_write(struct ssh_channel *c, const void *buf, int len)
+static int sshchannel_write(SshChannel *sc, const void *buf, int len)
{
+ struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
Ssh ssh = c->ssh;
if (ssh->state == SSH_STATE_CLOSED)
@@ -3711,8 +3737,9 @@ int sshfwd_write(struct ssh_channel *c, const void *buf, int len)
return ssh_send_channel_data(c, buf, len);
}
-void sshfwd_unthrottle(struct ssh_channel *c, int bufsize)
+static void sshchannel_unthrottle(SshChannel *sc, int bufsize)
{
+ struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
Ssh ssh = c->ssh;
if (ssh->state == SSH_STATE_CLOSED)
@@ -4225,7 +4252,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, PktIn *pktin)
c->ssh = ssh;
ssh_channel_init(c);
- c->chan = x11_new_channel(ssh->x11authtree, c, NULL, -1, FALSE);
+ c->chan = x11_new_channel(ssh->x11authtree, &c->sc, NULL, -1, FALSE);
c->remoteid = remoteid;
c->halfopen = FALSE;
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
@@ -4255,7 +4282,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, PktIn *pktin)
ssh_channel_init(c);
c->remoteid = remoteid;
c->halfopen = FALSE;
- c->chan = agentf_new(c);
+ c->chan = agentf_new(&c->sc);
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
put_uint32(pkt, c->remoteid);
put_uint32(pkt, c->localid);
@@ -4295,7 +4322,7 @@ static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
logeventf(ssh, "Received remote port open request for %s:%d",
pf.dhost, port);
err = pfd_connect(&c->chan, pf.dhost, port,
- c, ssh->conf, pfp->pfrec->addressfamily);
+ &c->sc, ssh->conf, pfp->pfrec->addressfamily);
if (err != NULL) {
logeventf(ssh, "Port open failed: %s", err);
sfree(err);
@@ -6931,6 +6958,7 @@ static void ssh_channel_init(struct ssh_channel *c)
c->v.v2.throttle_state = UNTHROTTLED;
bufchain_init(&c->v.v2.outbuffer);
}
+ c->sc.vt = &sshchannel_vtable;
add234(ssh->channels, c);
}
@@ -7454,7 +7482,7 @@ static void ssh2_msg_channel_close(Ssh ssh, PktIn *pktin)
/*
* Send outgoing EOF.
*/
- sshfwd_write_eof(c);
+ sshfwd_write_eof(&c->sc);
/*
* Make sure we don't read any more from whatever our local
@@ -7790,7 +7818,8 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
if (!ssh->X11_fwd_enabled && !ssh->connshare)
error = "X11 forwarding is not enabled";
else {
- c->chan = x11_new_channel(ssh->x11authtree, c, addrstr, peerport,
+ c->chan = x11_new_channel(ssh->x11authtree, &c->sc,
+ addrstr, peerport,
ssh->connshare != NULL);
logevent("Opened X11 forward channel");
}
@@ -7830,7 +7859,7 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
}
err = pfd_connect(&c->chan, realpf->dhost, realpf->dport,
- c, ssh->conf, realpf->pfrec->addressfamily);
+ &c->sc, ssh->conf, realpf->pfrec->addressfamily);
logeventf(ssh, "Attempting to forward remote port to "
"%s:%d", realpf->dhost, realpf->dport);
if (err != NULL) {
@@ -7845,7 +7874,7 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
if (!ssh->agentfwd_enabled)
error = "Agent forwarding is not enabled";
else
- c->chan = agentf_new(c);
+ c->chan = agentf_new(&c->sc);
} else {
error = "Unsupported channel type requested";
}
@@ -7879,13 +7908,13 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
}
}
-void sshfwd_x11_sharing_handover(struct ssh_channel *c,
- ssh_sharing_connstate *share_cs,
- share_channel *share_chan,
- const char *peer_addr, int peer_port,
- int endian, int protomajor, int protominor,
- const void *initial_data, int initial_len)
+static void sshchannel_x11_sharing_handover(
+ SshChannel *sc, ssh_sharing_connstate *share_cs, share_channel *share_chan,
+ const char *peer_addr, int peer_port, int endian,
+ int protomajor, int protominor, const void *initial_data, int initial_len)
{
+ struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
+
/*
* This function is called when we've just discovered that an X
* forwarding channel on which we'd been handling the initial auth
@@ -7905,8 +7934,10 @@ void sshfwd_x11_sharing_handover(struct ssh_channel *c,
c->chan = NULL;
}
-void sshfwd_window_override_removed(struct ssh_channel *c)
+static void sshchannel_window_override_removed(SshChannel *sc)
{
+ struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
+
/*
* This function is called when a client-side Channel has just
* stopped requiring an initial fixed-size window.
@@ -9661,7 +9692,7 @@ static void ssh2_connection_setup(Ssh ssh)
typedef struct mainchan {
Ssh ssh;
- struct ssh_channel *c;
+ SshChannel *sc;
Channel chan;
} mainchan;
@@ -9689,7 +9720,7 @@ static mainchan *mainchan_new(Ssh ssh)
{
mainchan *mc = snew(mainchan);
mc->ssh = ssh;
- mc->c = NULL;
+ mc->sc = NULL;
mc->chan.vt = &mainchan_channelvt;
mc->chan.initial_fixed_window_size = 0;
return mc;
@@ -9737,7 +9768,7 @@ static void mainchan_send_eof(Channel *chan)
* decided to do that because we've allocated a remote pty and
* hence EOF isn't a particularly meaningful concept.
*/
- sshfwd_write_eof(mc->c);
+ sshfwd_write_eof(mc->sc);
}
mc->ssh->sent_console_eof = TRUE;
}
@@ -9796,13 +9827,15 @@ static void do_ssh2_connection(void *vctx)
* Just start a direct-tcpip channel and use it as the main
* channel.
*/
- ssh->mainchan = mc->c = ssh_send_port_open
+ mc->sc = ssh_send_port_open
(ssh, conf_get_str(ssh->conf, CONF_ssh_nc_host),
conf_get_int(ssh->conf, CONF_ssh_nc_port),
"main channel", &mc->chan);
+ ssh->mainchan = FROMFIELD(mc->sc, struct ssh_channel, sc);
ssh->ncmode = TRUE;
} else {
- ssh->mainchan = mc->c = snew(struct ssh_channel);
+ ssh->mainchan = snew(struct ssh_channel);
+ mc->sc = &ssh->mainchan->sc;
ssh->mainchan->ssh = ssh;
ssh_channel_init(ssh->mainchan);
ssh->mainchan->chan = &mc->chan;
@@ -11100,7 +11133,7 @@ static void ssh_special(Backend *be, Telnet_Special code)
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_EOF);
ssh_pkt_write(ssh, pktout);
} else if (ssh->mainchan) {
- sshfwd_write_eof(ssh->mainchan);
+ sshfwd_write_eof(&ssh->mainchan->sc);
ssh->send_ok = 0; /* now stop trying to read from stdin */
}
logevent("Sent EOF message");
@@ -11237,8 +11270,8 @@ static void ssh_unthrottle(Backend *be, int bufsize)
queue_idempotent_callback(&ssh->incoming_data_consumer);
}
-struct ssh_channel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
- const char *org, Channel *chan)
+SshChannel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
+ const char *org, Channel *chan)
{
struct ssh_channel *c = snew(struct ssh_channel);
PktOut *pktout;
@@ -11280,7 +11313,7 @@ struct ssh_channel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
ssh2_pkt_send(ssh, pktout);
}
- return c;
+ return &c->sc;
}
static int ssh_connected(Backend *be)
diff --git a/ssh.h b/ssh.h
index 7f038b9c..a4aea86e 100644
--- a/ssh.h
+++ b/ssh.h
@@ -9,19 +9,6 @@
struct ssh_channel;
-extern int sshfwd_write(struct ssh_channel *c, const void *, int);
-extern void sshfwd_write_eof(struct ssh_channel *c);
-extern void sshfwd_unclean_close(struct ssh_channel *c, const char *err);
-extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize);
-Conf *sshfwd_get_conf(struct ssh_channel *c);
-void sshfwd_window_override_removed(struct ssh_channel *c);
-void sshfwd_x11_sharing_handover(struct ssh_channel *c,
- ssh_sharing_connstate *share_cs,
- share_channel *share_chan,
- const char *peer_addr, int peer_port,
- int endian, int protomajor, int protominor,
- const void *initial_data, int initial_len);
-
/*
* Buffer management constants. There are several of these for
* various different purposes:
@@ -734,12 +721,12 @@ void logevent(Frontend *, const char *);
struct PortForwarding;
/* Allocate and register a new channel for port forwarding */
-struct ssh_channel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
- const char *org, Channel *chan);
+SshChannel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
+ const char *org, Channel *chan);
/* Exports from portfwd.c */
extern char *pfd_connect(Channel **chan_ret, char *hostname, int port,
- struct ssh_channel *c, Conf *conf, int addressfamily);
+ SshChannel *c, Conf *conf, int addressfamily);
struct PortListener;
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
extern char *pfl_listen(char *desthost, int destport, char *srcaddr,
@@ -813,7 +800,7 @@ extern struct X11Display *x11_setup_display(const char *display, Conf *);
void x11_free_display(struct X11Display *disp);
struct X11FakeAuth *x11_invent_fake_auth(tree234 *t, int authtype);
void x11_free_fake_auth(struct X11FakeAuth *auth);
-Channel *x11_new_channel(tree234 *authtree, struct ssh_channel *c,
+Channel *x11_new_channel(tree234 *authtree, SshChannel *c,
const char *peeraddr, int peerport,
int connection_sharing_possible);
char *x11_display(const char *display);
@@ -845,7 +832,7 @@ void x11_get_auth_from_authfile(struct X11Display *display,
int x11_identify_auth_proto(ptrlen protoname);
void *x11_dehexify(ptrlen hex, int *outlen);
-Channel *agentf_new(struct ssh_channel *c);
+Channel *agentf_new(SshChannel *c);
Bignum copybn(Bignum b);
Bignum bn_power_2(int n);
diff --git a/sshchan.h b/sshchan.h
index 26462bc6..271cdb09 100644
--- a/sshchan.h
+++ b/sshchan.h
@@ -40,7 +40,7 @@ struct Channel {
#define chan_send_eof(ch) ((ch)->vt->send_eof(ch))
#define chan_set_input_wanted(ch, wanted) \
((ch)->vt->set_input_wanted(ch, wanted))
-#define chan_log_close_msg(ch) ((ch)->vt->send_eof(ch))
+#define chan_log_close_msg(ch) ((ch)->vt->log_close_msg(ch))
#define chan_want_close(ch, leof, reof) ((ch)->vt->want_close(ch, leof, reof))
/*
@@ -56,4 +56,38 @@ void chan_remotely_opened_failure(Channel *chan, const char *errtext);
* closing until both directions have had an EOF */
int chan_no_eager_close(Channel *, int, int);
+/* ----------------------------------------------------------------------
+ * This structure is owned by an SSH connection layer, and identifies
+ * the connection layer's end of the channel, for the Channel
+ * implementation to talk back to.
+ */
+
+struct SshChannelVtable {
+ int (*write)(SshChannel *c, const void *, int);
+ void (*write_eof)(SshChannel *c);
+ void (*unclean_close)(SshChannel *c, const char *err);
+ void (*unthrottle)(SshChannel *c, int bufsize);
+ Conf *(*get_conf)(SshChannel *c);
+ void (*window_override_removed)(SshChannel *c);
+ void (*x11_sharing_handover)(SshChannel *c,
+ ssh_sharing_connstate *share_cs,
+ share_channel *share_chan,
+ const char *peer_addr, int peer_port,
+ int endian, int protomajor, int protominor,
+ const void *initial_data, int initial_len);
+};
+
+struct SshChannel {
+ const struct SshChannelVtable *vt;
+};
+
+#define sshfwd_write(c, buf, len) ((c)->vt->write(c, buf, len))
+#define sshfwd_write_eof(c) ((c)->vt->write_eof(c))
+#define sshfwd_unclean_close(c, err) ((c)->vt->unclean_close(c, err))
+#define sshfwd_unthrottle(c, bufsize) ((c)->vt->unthrottle(c, bufsize))
+#define sshfwd_get_conf(c) ((c)->vt->get_conf(c))
+#define sshfwd_window_override_removed(c) ((c)->vt->window_override_removed(c))
+#define sshfwd_x11_sharing_handover(c, cs, ch, pa, pp, e, pmaj, pmin, d, l) \
+ ((c)->vt->x11_sharing_handover(c, cs, ch, pa, pp, e, pmaj, pmin, d, l))
+
#endif /* PUTTY_SSHCHAN_H */
diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
index efd93726..1b7004f8 100644
--- a/unix/uxpgnt.c
+++ b/unix/uxpgnt.c
@@ -156,24 +156,10 @@ static int time_to_die = FALSE;
* used, because in LIFE_X11 mode we connect to the X server using a
* straightforward Socket and don't try to create an ersatz SSH
* forwarding too. */
-int sshfwd_write(struct ssh_channel *c, const void *data, int len)
-{ return 0; }
-void sshfwd_write_eof(struct ssh_channel *c) { }
-void sshfwd_unclean_close(struct ssh_channel *c, const char *err) { }
-void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) {}
-void sshfwd_window_override_removed(struct ssh_channel *c) { }
void chan_remotely_opened_confirmation(Channel *chan) { }
void chan_remotely_opened_failure(Channel *chan, const char *err) { }
int chan_no_eager_close(Channel *chan, int s, int r) { return FALSE; }
-Conf *sshfwd_get_conf(struct ssh_channel *c) { return NULL; }
-void sshfwd_x11_sharing_handover(struct ssh_channel *c,
- ssh_sharing_connstate *share_cs,
- share_channel *share_chan,
- const char *peer_addr, int peer_port,
- int endian, int protomajor, int protominor,
- const void *initial_data, int initial_len) {}
-
/*
* These functions are part of the plug for our connection to the X
* display, so they do get called. They needn't actually do anything,
diff --git a/x11fwd.c b/x11fwd.c
index f3ea5397..ed2f3b60 100644
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -39,7 +39,7 @@ typedef struct X11Connection {
int no_data_sent_to_x_client;
char *peer_addr;
int peer_port;
- struct ssh_channel *c; /* channel structure held by ssh.c */
+ SshChannel *c; /* channel structure held by SSH backend */
Socket s;
const Plug_vtable *plugvt;
@@ -728,7 +728,7 @@ static const struct ChannelVtable X11Connection_channelvt = {
* Called to set up the X11Connection structure, though this does not
* yet connect to an actual server.
*/
-Channel *x11_new_channel(tree234 *authtree, struct ssh_channel *c,
+Channel *x11_new_channel(tree234 *authtree, SshChannel *c,
const char *peeraddr, int peerport,
int connection_sharing_possible)
{
From 895b09a4c65243265d4f155bd1b32070bbda4ec4 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Fri, 14 Sep 2018 17:04:39 +0100
Subject: [PATCH 409/607] Move port-forwarding setup out of ssh.c.
The tree234 storing currently active port forwardings - both local and
remote - now lives in portfwd.c, as does the complicated function that
updates it based on a Conf listing the new set of desired forwardings.
Local port forwardings are passed to ssh.c via the same route as
before - once the listening port receives a connection and portfwd.c
knows where it should be directed to (in particular, after the SOCKS
exchange, if any), it calls ssh_send_port_open.
Remote forwardings are now initiated by calling ssh_rportfwd_alloc,
which adds an entry to the rportfwds tree (which _is_ still in ssh.c,
and still confusingly sorted by a different criterion depending on SSH
protocol version) and sends out the appropriate protocol request.
ssh_rportfwd_remove cancels one again, sending a protocol request too.
Those functions look enough like ssh_{alloc,remove}_sharing_rportfwd
that I've merged those into the new pair as well - now allocating an
rportfwd allows you to specify either a destination host/port or a
sharing context, and returns a handy pointer you can use to cancel the
forwarding later.
---
defs.h | 3 +
misc.c | 11 ++
misc.h | 6 +
portfwd.c | 511 ++++++++++++++++++++++++++++++++++++++++++------
ssh.c | 556 ++++++++++-------------------------------------------
ssh.h | 33 ++--
sshshare.c | 16 +-
7 files changed, 597 insertions(+), 539 deletions(-)
diff --git a/defs.h b/defs.h
index e6efe0a9..822ee71d 100644
--- a/defs.h
+++ b/defs.h
@@ -60,6 +60,9 @@ typedef struct ssh_sharing_state ssh_sharing_state;
typedef struct ssh_sharing_connstate ssh_sharing_connstate;
typedef struct share_channel share_channel;
+typedef struct PortFwdManager PortFwdManager;
+typedef struct PortFwdRecord PortFwdRecord;
+
typedef struct dlgparam dlgparam;
typedef struct settings_w settings_w;
diff --git a/misc.c b/misc.c
index fdd5635c..f4334a04 100644
--- a/misc.c
+++ b/misc.c
@@ -1181,6 +1181,17 @@ int smemeq(const void *av, const void *bv, size_t len)
return (0x100 - val) >> 8;
}
+int nullstrcmp(const char *a, const char *b)
+{
+ if (a == NULL && b == NULL)
+ return 0;
+ if (a == NULL)
+ return -1;
+ if (b == NULL)
+ return +1;
+ return strcmp(a, b);
+}
+
ptrlen make_ptrlen(const void *ptr, size_t len)
{
ptrlen pl;
diff --git a/misc.h b/misc.h
index 35d9de6c..242d1a94 100644
--- a/misc.h
+++ b/misc.h
@@ -87,6 +87,12 @@ int validate_manual_hostkey(char *key);
struct tm ltime(void);
+/*
+ * Special form of strcmp which can cope with NULL inputs. NULL is
+ * defined to sort before even the empty string.
+ */
+int nullstrcmp(const char *a, const char *b);
+
ptrlen make_ptrlen(const void *ptr, size_t len);
int ptrlen_eq_string(ptrlen pl, const char *str);
char *mkstr(ptrlen pl);
diff --git a/portfwd.c b/portfwd.c
index 53d5234a..d200c78b 100644
--- a/portfwd.c
+++ b/portfwd.c
@@ -10,6 +10,18 @@
#include "ssh.h"
#include "sshchan.h"
+static void logeventf(Frontend *frontend, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+
+ va_start(ap, fmt);
+ buf = dupvprintf(fmt, ap);
+ va_end(ap);
+ logevent(frontend, buf);
+ sfree(buf);
+}
+
/*
* Enumeration of values that live in the 'socks_state' field of
* struct PortForwarding.
@@ -139,6 +151,8 @@ static void pfd_closing(Plug plug, const char *error_msg, int error_code,
}
}
+static void pfl_terminate(struct PortListener *pl);
+
static void pfl_closing(Plug plug, const char *error_msg, int error_code,
int calling_back)
{
@@ -447,61 +461,6 @@ static const struct ChannelVtable PortForwarding_channelvt = {
chan_no_eager_close,
};
-/*
- * Called when receiving a PORT OPEN from the server to make a
- * connection to a destination host.
- *
- * On success, returns NULL and fills in *pf_ret. On error, returns a
- * dynamically allocated error message string.
- */
-char *pfd_connect(Channel **chan_ret, char *hostname,int port,
- SshChannel *c, Conf *conf, int addressfamily)
-{
- SockAddr addr;
- const char *err;
- char *dummy_realhost = NULL;
- struct PortForwarding *pf;
-
- /*
- * Try to find host.
- */
- addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily,
- NULL, NULL);
- if ((err = sk_addr_error(addr)) != NULL) {
- char *err_ret = dupstr(err);
- sk_addr_free(addr);
- sfree(dummy_realhost);
- return err_ret;
- }
-
- /*
- * Open socket.
- */
- pf = new_portfwd_state();
- *chan_ret = &pf->chan;
- pf->plugvt = &PortForwarding_plugvt;
- pf->chan.initial_fixed_window_size = 0;
- pf->chan.vt = &PortForwarding_channelvt;
- pf->input_wanted = TRUE;
- pf->ready = 1;
- pf->c = c;
- pf->ssh = NULL; /* we shouldn't need this */
- pf->socks_state = SOCKS_NONE;
-
- pf->s = new_connection(addr, dummy_realhost, port,
- 0, 1, 0, 0, &pf->plugvt, conf);
- sfree(dummy_realhost);
- if ((err = sk_socket_error(pf->s)) != NULL) {
- char *err_ret = dupstr(err);
- sk_close(pf->s);
- free_portfwd_state(pf);
- *chan_ret = NULL;
- return err_ret;
- }
-
- return NULL;
-}
-
/*
called when someone connects to the local port
*/
@@ -560,12 +519,14 @@ static const Plug_vtable PortListener_plugvt = {
/*
* Add a new port-forwarding listener from srcaddr:port -> desthost:destport.
*
+ * desthost == NULL indicates dynamic SOCKS port forwarding.
+ *
* On success, returns NULL and fills in *pl_ret. On error, returns a
* dynamically allocated error message string.
*/
-char *pfl_listen(char *desthost, int destport, char *srcaddr,
- int port, Ssh ssh, Conf *conf,
- struct PortListener **pl_ret, int address_family)
+static char *pfl_listen(char *desthost, int destport, char *srcaddr,
+ int port, Ssh ssh, Conf *conf,
+ struct PortListener **pl_ret, int address_family)
{
const char *err;
struct PortListener *pl;
@@ -614,7 +575,7 @@ static void pfd_close(struct PortForwarding *pf)
/*
* Terminate a listener.
*/
-void pfl_terminate(struct PortListener *pl)
+static void pfl_terminate(struct PortListener *pl)
{
if (!pl)
return;
@@ -676,9 +637,431 @@ static void pfd_open_failure(Channel *chan, const char *errtext)
assert(chan->vt == &PortForwarding_channelvt);
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
- char *msg = dupprintf(
- "Forwarded connection refused by server%s%s",
- errtext ? ": " : "", errtext ? errtext : "");
- logevent(ssh_get_frontend(pf->ssh), msg);
- sfree(msg);
+ logeventf(ssh_get_frontend(pf->ssh),
+ "Forwarded connection refused by server%s%s",
+ errtext ? ": " : "", errtext ? errtext : "");
+}
+
+/* ----------------------------------------------------------------------
+ * Code to manage the complete set of currently active port
+ * forwardings, and update it from Conf.
+ */
+
+struct PortFwdRecord {
+ enum { DESTROY, KEEP, CREATE } status;
+ int type;
+ unsigned sport, dport;
+ char *saddr, *daddr;
+ char *sserv, *dserv;
+ struct ssh_rportfwd *remote;
+ int addressfamily;
+ struct PortListener *local;
+};
+
+static int pfr_cmp(void *av, void *bv)
+{
+ PortFwdRecord *a = (PortFwdRecord *) av;
+ PortFwdRecord *b = (PortFwdRecord *) bv;
+ int i;
+ if (a->type > b->type)
+ return +1;
+ if (a->type < b->type)
+ return -1;
+ if (a->addressfamily > b->addressfamily)
+ return +1;
+ if (a->addressfamily < b->addressfamily)
+ return -1;
+ if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)
+ return i < 0 ? -1 : +1;
+ if (a->sport > b->sport)
+ return +1;
+ if (a->sport < b->sport)
+ return -1;
+ if (a->type != 'D') {
+ if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)
+ return i < 0 ? -1 : +1;
+ if (a->dport > b->dport)
+ return +1;
+ if (a->dport < b->dport)
+ return -1;
+ }
+ return 0;
+}
+
+void pfr_free(PortFwdRecord *pfr)
+{
+ /* Dispose of any listening socket. */
+ if (pfr->local)
+ pfl_terminate(pfr->local);
+
+ sfree(pfr->saddr);
+ sfree(pfr->daddr);
+ sfree(pfr->sserv);
+ sfree(pfr->dserv);
+ sfree(pfr);
+}
+
+struct PortFwdManager {
+ Ssh ssh;
+ Frontend *frontend;
+ Conf *conf;
+ tree234 *forwardings;
+};
+
+PortFwdManager *portfwdmgr_new(Ssh ssh)
+{
+ PortFwdManager *mgr = snew(PortFwdManager);
+
+ mgr->ssh = ssh;
+ mgr->frontend = ssh_get_frontend(ssh);
+ mgr->conf = NULL;
+ mgr->forwardings = newtree234(pfr_cmp);
+
+ return mgr;
+}
+
+void portfwdmgr_close(PortFwdManager *mgr, PortFwdRecord *pfr)
+{
+ PortFwdRecord *realpfr = del234(mgr->forwardings, pfr);
+ if (realpfr == pfr)
+ pfr_free(pfr);
+}
+
+void portfwdmgr_close_all(PortFwdManager *mgr)
+{
+ PortFwdRecord *pfr;
+
+ while ((pfr = delpos234(mgr->forwardings, 0)) != NULL)
+ pfr_free(pfr);
+}
+
+void portfwdmgr_free(PortFwdManager *mgr)
+{
+ portfwdmgr_close_all(mgr);
+ freetree234(mgr->forwardings);
+ if (mgr->conf)
+ conf_free(mgr->conf);
+ sfree(mgr);
+}
+
+void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
+{
+ PortFwdRecord *pfr;
+ int i;
+ char *key, *val;
+
+ if (mgr->conf)
+ conf_free(mgr->conf);
+ mgr->conf = conf_copy(conf);
+
+ /*
+ * Go through the existing port forwardings and tag them
+ * with status==DESTROY. Any that we want to keep will be
+ * re-enabled (status==KEEP) as we go through the
+ * configuration and find out which bits are the same as
+ * they were before.
+ */
+ for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++)
+ pfr->status = DESTROY;
+
+ for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
+ val != NULL;
+ val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
+ char *kp, *kp2, *vp, *vp2;
+ char address_family, type;
+ int sport, dport, sserv, dserv;
+ char *sports, *dports, *saddr, *host;
+
+ kp = key;
+
+ address_family = 'A';
+ type = 'L';
+ if (*kp == 'A' || *kp == '4' || *kp == '6')
+ address_family = *kp++;
+ if (*kp == 'L' || *kp == 'R')
+ type = *kp++;
+
+ if ((kp2 = host_strchr(kp, ':')) != NULL) {
+ /*
+ * There's a colon in the middle of the source port
+ * string, which means that the part before it is
+ * actually a source address.
+ */
+ char *saddr_tmp = dupprintf("%.*s", (int)(kp2 - kp), kp);
+ saddr = host_strduptrim(saddr_tmp);
+ sfree(saddr_tmp);
+ sports = kp2+1;
+ } else {
+ saddr = NULL;
+ sports = kp;
+ }
+ sport = atoi(sports);
+ sserv = 0;
+ if (sport == 0) {
+ sserv = 1;
+ sport = net_service_lookup(sports);
+ if (!sport) {
+ logeventf(mgr->frontend, "Service lookup failed for source"
+ " port \"%s\"", sports);
+ }
+ }
+
+ if (type == 'L' && !strcmp(val, "D")) {
+ /* dynamic forwarding */
+ host = NULL;
+ dports = NULL;
+ dport = -1;
+ dserv = 0;
+ type = 'D';
+ } else {
+ /* ordinary forwarding */
+ vp = val;
+ vp2 = vp + host_strcspn(vp, ":");
+ host = dupprintf("%.*s", (int)(vp2 - vp), vp);
+ if (*vp2)
+ vp2++;
+ dports = vp2;
+ dport = atoi(dports);
+ dserv = 0;
+ if (dport == 0) {
+ dserv = 1;
+ dport = net_service_lookup(dports);
+ if (!dport) {
+ logeventf(mgr->frontend,
+ "Service lookup failed for destination"
+ " port \"%s\"", dports);
+ }
+ }
+ }
+
+ if (sport && dport) {
+ /* Set up a description of the source port. */
+ pfr = snew(PortFwdRecord);
+ pfr->type = type;
+ pfr->saddr = saddr;
+ pfr->sserv = sserv ? dupstr(sports) : NULL;
+ pfr->sport = sport;
+ pfr->daddr = host;
+ pfr->dserv = dserv ? dupstr(dports) : NULL;
+ pfr->dport = dport;
+ pfr->local = NULL;
+ pfr->remote = NULL;
+ pfr->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 :
+ address_family == '6' ? ADDRTYPE_IPV6 :
+ ADDRTYPE_UNSPEC);
+
+ PortFwdRecord *existing = add234(mgr->forwardings, pfr);
+ if (existing != pfr) {
+ if (existing->status == DESTROY) {
+ /*
+ * We already have a port forwarding up and running
+ * with precisely these parameters. Hence, no need
+ * to do anything; simply re-tag the existing one
+ * as KEEP.
+ */
+ existing->status = KEEP;
+ }
+ /*
+ * Anything else indicates that there was a duplicate
+ * in our input, which we'll silently ignore.
+ */
+ pfr_free(pfr);
+ } else {
+ pfr->status = CREATE;
+ }
+ } else {
+ sfree(saddr);
+ sfree(host);
+ }
+ }
+
+ /*
+ * Now go through and destroy any port forwardings which were
+ * not re-enabled.
+ */
+ for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) {
+ if (pfr->status == DESTROY) {
+ char *message;
+
+ message = dupprintf("%s port forwarding from %s%s%d",
+ pfr->type == 'L' ? "local" :
+ pfr->type == 'R' ? "remote" : "dynamic",
+ pfr->saddr ? pfr->saddr : "",
+ pfr->saddr ? ":" : "",
+ pfr->sport);
+
+ if (pfr->type != 'D') {
+ char *msg2 = dupprintf("%s to %s:%d", message,
+ pfr->daddr, pfr->dport);
+ sfree(message);
+ message = msg2;
+ }
+
+ logeventf(mgr->frontend, "Cancelling %s", message);
+ sfree(message);
+
+ /* pfr->remote or pfr->local may be NULL if setting up a
+ * forwarding failed. */
+ if (pfr->remote) {
+ /*
+ * Cancel the port forwarding at the server
+ * end.
+ *
+ * Actually closing the listening port on the server
+ * side may fail - because in SSH-1 there's no message
+ * in the protocol to request it!
+ *
+ * Instead, we simply remove the record of the
+ * forwarding from our local end, so that any
+ * connections the server tries to make on it are
+ * rejected.
+ */
+ ssh_rportfwd_remove(mgr->ssh, pfr->remote);
+ } else if (pfr->local) {
+ pfl_terminate(pfr->local);
+ }
+
+ delpos234(mgr->forwardings, i);
+ pfr_free(pfr);
+ i--; /* so we don't skip one in the list */
+ }
+ }
+
+ /*
+ * And finally, set up any new port forwardings (status==CREATE).
+ */
+ for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) {
+ if (pfr->status == CREATE) {
+ char *sportdesc, *dportdesc;
+ sportdesc = dupprintf("%s%s%s%s%d%s",
+ pfr->saddr ? pfr->saddr : "",
+ pfr->saddr ? ":" : "",
+ pfr->sserv ? pfr->sserv : "",
+ pfr->sserv ? "(" : "",
+ pfr->sport,
+ pfr->sserv ? ")" : "");
+ if (pfr->type == 'D') {
+ dportdesc = NULL;
+ } else {
+ dportdesc = dupprintf("%s:%s%s%d%s",
+ pfr->daddr,
+ pfr->dserv ? pfr->dserv : "",
+ pfr->dserv ? "(" : "",
+ pfr->dport,
+ pfr->dserv ? ")" : "");
+ }
+
+ if (pfr->type == 'L') {
+ char *err = pfl_listen(pfr->daddr, pfr->dport,
+ pfr->saddr, pfr->sport,
+ mgr->ssh, conf, &pfr->local,
+ pfr->addressfamily);
+
+ logeventf(mgr->frontend,
+ "Local %sport %s forwarding to %s%s%s",
+ pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
+ pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
+ sportdesc, dportdesc,
+ err ? " failed: " : "", err ? err : "");
+ if (err)
+ sfree(err);
+ } else if (pfr->type == 'D') {
+ char *err = pfl_listen(NULL, -1, pfr->saddr, pfr->sport,
+ mgr->ssh, conf, &pfr->local,
+ pfr->addressfamily);
+
+ logeventf(mgr->frontend,
+ "Local %sport %s SOCKS dynamic forwarding%s%s",
+ pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
+ pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
+ sportdesc,
+ err ? " failed: " : "", err ? err : "");
+
+ if (err)
+ sfree(err);
+ } else {
+ const char *shost;
+
+ if (pfr->saddr) {
+ shost = pfr->saddr;
+ } else if (conf_get_int(conf, CONF_rport_acceptall)) {
+ shost = "";
+ } else {
+ shost = "localhost";
+ }
+
+ pfr->remote = ssh_rportfwd_alloc(
+ mgr->ssh, shost, pfr->sport, pfr->daddr, pfr->dport,
+ pfr->addressfamily, sportdesc, pfr, NULL);
+
+ if (!pfr->remote) {
+ logeventf(mgr->frontend,
+ "Duplicate remote port forwarding to %s:%d",
+ pfr->daddr, pfr->dport);
+ pfr_free(pfr);
+ } else {
+ logeventf(mgr->frontend, "Requesting remote port %s"
+ " forward to %s", sportdesc, dportdesc);
+ }
+ }
+ sfree(sportdesc);
+ sfree(dportdesc);
+ }
+ }
+}
+
+/*
+ * Called when receiving a PORT OPEN from the server to make a
+ * connection to a destination host.
+ *
+ * On success, returns NULL and fills in *pf_ret. On error, returns a
+ * dynamically allocated error message string.
+ */
+char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
+ char *hostname, int port, SshChannel *c,
+ int addressfamily)
+{
+ SockAddr addr;
+ const char *err;
+ char *dummy_realhost = NULL;
+ struct PortForwarding *pf;
+
+ /*
+ * Try to find host.
+ */
+ addr = name_lookup(hostname, port, &dummy_realhost, mgr->conf,
+ addressfamily, NULL, NULL);
+ if ((err = sk_addr_error(addr)) != NULL) {
+ char *err_ret = dupstr(err);
+ sk_addr_free(addr);
+ sfree(dummy_realhost);
+ return err_ret;
+ }
+
+ /*
+ * Open socket.
+ */
+ pf = new_portfwd_state();
+ *chan_ret = &pf->chan;
+ pf->plugvt = &PortForwarding_plugvt;
+ pf->chan.initial_fixed_window_size = 0;
+ pf->chan.vt = &PortForwarding_channelvt;
+ pf->input_wanted = TRUE;
+ pf->ready = 1;
+ pf->c = c;
+ pf->ssh = mgr->ssh;
+ pf->socks_state = SOCKS_NONE;
+
+ pf->s = new_connection(addr, dummy_realhost, port,
+ 0, 1, 0, 0, &pf->plugvt, mgr->conf);
+ sfree(dummy_realhost);
+ if ((err = sk_socket_error(pf->s)) != NULL) {
+ char *err_ret = dupstr(err);
+ sk_close(pf->s);
+ free_portfwd_state(pf);
+ *chan_ret = NULL;
+ return err_ret;
+ }
+
+ return NULL;
}
diff --git a/ssh.c b/ssh.c
index eaf1f386..bedfe6af 100644
--- a/ssh.c
+++ b/ssh.c
@@ -527,42 +527,22 @@ struct ssh_portfwd; /* forward declaration */
struct ssh_rportfwd {
unsigned sport, dport;
char *shost, *dhost;
- char *sportdesc;
+ int addressfamily;
+ char *log_description; /* name of remote listening port, for logging */
ssh_sharing_connstate *share_ctx;
- struct ssh_portfwd *pfrec;
+ PortFwdRecord *pfr;
};
static void free_rportfwd(struct ssh_rportfwd *pf)
{
if (pf) {
- sfree(pf->sportdesc);
+ sfree(pf->log_description);
sfree(pf->shost);
sfree(pf->dhost);
sfree(pf);
}
}
-/*
- * Separately to the rportfwd tree (which is for looking up port
- * open requests from the server), a tree of _these_ structures is
- * used to keep track of all the currently open port forwardings,
- * so that we can reconfigure in mid-session if the user requests
- * it.
- */
-struct ssh_portfwd {
- enum { DESTROY, KEEP, CREATE } status;
- int type;
- unsigned sport, dport;
- char *saddr, *daddr;
- char *sserv, *dserv;
- struct ssh_rportfwd *remote;
- int addressfamily;
- struct PortListener *local;
-};
-#define free_portfwd(pf) ( \
- ((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
- sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )
-
static void ssh1_protocol_setup(Ssh ssh);
static void ssh2_protocol_setup(Ssh ssh);
static void ssh2_bare_connection_protocol_setup(Ssh ssh);
@@ -746,7 +726,8 @@ struct ssh_tag {
int clean_exit;
int disconnect_message_seen;
- tree234 *rportfwds, *portfwds;
+ tree234 *rportfwds;
+ PortFwdManager *portfwdmgr;
enum {
SSH_STATE_PREPACKET,
@@ -1063,51 +1044,6 @@ static int ssh_rportcmp_ssh2(void *av, void *bv)
return 0;
}
-/*
- * Special form of strcmp which can cope with NULL inputs. NULL is
- * defined to sort before even the empty string.
- */
-static int nullstrcmp(const char *a, const char *b)
-{
- if (a == NULL && b == NULL)
- return 0;
- if (a == NULL)
- return -1;
- if (b == NULL)
- return +1;
- return strcmp(a, b);
-}
-
-static int ssh_portcmp(void *av, void *bv)
-{
- struct ssh_portfwd *a = (struct ssh_portfwd *) av;
- struct ssh_portfwd *b = (struct ssh_portfwd *) bv;
- int i;
- if (a->type > b->type)
- return +1;
- if (a->type < b->type)
- return -1;
- if (a->addressfamily > b->addressfamily)
- return +1;
- if (a->addressfamily < b->addressfamily)
- return -1;
- if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)
- return i < 0 ? -1 : +1;
- if (a->sport > b->sport)
- return +1;
- if (a->sport < b->sport)
- return -1;
- if (a->type != 'D') {
- if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)
- return i < 0 ? -1 : +1;
- if (a->dport > b->dport)
- return +1;
- if (a->dport < b->dport)
- return -1;
- }
- return 0;
-}
-
static int alloc_channel_id(Ssh ssh)
{
const unsigned CHANNEL_NUMBER_OFFSET = 256;
@@ -2230,18 +2166,7 @@ static int ssh_do_close(Ssh ssh, int notify_exit)
* Go through port-forwardings, and close any associated
* listening sockets.
*/
- if (ssh->portfwds) {
- struct ssh_portfwd *pf;
- while (NULL != (pf = index234(ssh->portfwds, 0))) {
- /* Dispose of any listening socket. */
- if (pf->local)
- pfl_terminate(pf->local);
- del234(ssh->portfwds, pf); /* moving next one to index 0 */
- free_portfwd(pf);
- }
- freetree234(ssh->portfwds);
- ssh->portfwds = NULL;
- }
+ portfwdmgr_close_all(ssh->portfwdmgr);
/*
* Also stop attempting to connection-share.
@@ -3816,58 +3741,110 @@ static void ssh_queue_handler(Ssh ssh, int msg1, int msg2,
static void ssh_rportfwd_succfail(Ssh ssh, PktIn *pktin, void *ctx)
{
- struct ssh_rportfwd *rpf, *pf = (struct ssh_rportfwd *)ctx;
+ struct ssh_rportfwd *rpf = (struct ssh_rportfwd *)ctx;
if (pktin->type == (ssh->version == 1 ? SSH1_SMSG_SUCCESS :
SSH2_MSG_REQUEST_SUCCESS)) {
logeventf(ssh, "Remote port forwarding from %s enabled",
- pf->sportdesc);
+ rpf->log_description);
} else {
logeventf(ssh, "Remote port forwarding from %s refused",
- pf->sportdesc);
+ rpf->log_description);
- rpf = del234(ssh->rportfwds, pf);
- assert(rpf == pf);
- pf->pfrec->remote = NULL;
- free_rportfwd(pf);
+ struct ssh_rportfwd *realpf = del234(ssh->rportfwds, rpf);
+ assert(realpf == rpf);
+ portfwdmgr_close(ssh->portfwdmgr, rpf->pfr);
+ free_rportfwd(rpf);
}
}
-int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
- ssh_sharing_connstate *share_ctx)
+struct ssh_rportfwd *ssh_rportfwd_alloc(
+ Ssh ssh, const char *shost, int sport, const char *dhost, int dport,
+ int addressfamily, const char *log_description, PortFwdRecord *pfr,
+ ssh_sharing_connstate *share_ctx)
{
- struct ssh_rportfwd *pf = snew(struct ssh_rportfwd);
- pf->dhost = NULL;
- pf->dport = 0;
- pf->share_ctx = share_ctx;
- pf->shost = dupstr(shost);
- pf->sport = sport;
- pf->sportdesc = NULL;
+ /*
+ * Ensure the remote port forwardings tree exists.
+ */
if (!ssh->rportfwds) {
- assert(ssh->version == 2);
- ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
+ if (ssh->version == 1)
+ ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);
+ else
+ ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
}
- if (add234(ssh->rportfwds, pf) != pf) {
- sfree(pf->shost);
- sfree(pf);
- return FALSE;
+
+ struct ssh_rportfwd *rpf = snew(struct ssh_rportfwd);
+
+ rpf->shost = dupstr(shost);
+ rpf->sport = sport;
+ rpf->dhost = dupstr(dhost);
+ rpf->dport = dport;
+ rpf->addressfamily = addressfamily;
+ rpf->log_description = dupstr(log_description);
+ rpf->pfr = pfr;
+ rpf->share_ctx = share_ctx;
+
+ if (add234(ssh->rportfwds, rpf) != rpf) {
+ free_rportfwd(rpf);
+ return NULL;
}
- return TRUE;
+
+ if (!rpf->share_ctx) {
+ PktOut *pktout;
+
+ if (ssh->version == 1) {
+ pktout = ssh_bpp_new_pktout(
+ ssh->bpp, SSH1_CMSG_PORT_FORWARD_REQUEST);
+ put_uint32(pktout, rpf->sport);
+ put_stringz(pktout, rpf->dhost);
+ put_uint32(pktout, rpf->dport);
+ ssh_pkt_write(ssh, pktout);
+ ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS,
+ SSH1_SMSG_FAILURE,
+ ssh_rportfwd_succfail, rpf);
+ } else {
+ pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
+ put_stringz(pktout, "tcpip-forward");
+ put_bool(pktout, 1); /* want reply */
+ put_stringz(pktout, rpf->shost);
+ put_uint32(pktout, rpf->sport);
+ ssh2_pkt_send(ssh, pktout);
+
+ ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS,
+ SSH2_MSG_REQUEST_FAILURE,
+ ssh_rportfwd_succfail, rpf);
+ }
+ }
+
+ return rpf;
}
-void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
- ssh_sharing_connstate *share_ctx)
+void ssh_rportfwd_remove(Ssh ssh, struct ssh_rportfwd *rpf)
{
- struct ssh_rportfwd pf, *realpf;
+ if (ssh->version == 1) {
+ /*
+ * We cannot cancel listening ports on the server side in
+ * SSH-1! There's no message to support it.
+ */
+ } else if (rpf->share_ctx) {
+ /*
+ * We don't manufacture a cancel-tcpip-forward message for
+ * remote port forwardings being removed on behalf of a
+ * downstream; we just pass through the one the downstream
+ * sent to us.
+ */
+ } else {
+ PktOut *pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
+ put_stringz(pktout, "cancel-tcpip-forward");
+ put_bool(pktout, 0); /* _don't_ want reply */
+ put_stringz(pktout, rpf->shost);
+ put_uint32(pktout, rpf->sport);
+ ssh2_pkt_send(ssh, pktout);
+ }
- assert(ssh->rportfwds);
- pf.shost = dupstr(shost);
- pf.sport = sport;
- realpf = del234(ssh->rportfwds, &pf);
- assert(realpf);
- assert(realpf->share_ctx == share_ctx);
- sfree(realpf->shost);
- sfree(realpf);
+ struct ssh_rportfwd *realpf = del234(ssh->rportfwds, rpf);
+ assert(realpf == rpf);
+ free_rportfwd(rpf);
}
static void ssh_sharing_global_request_response(Ssh ssh, PktIn *pktin,
@@ -3885,334 +3862,6 @@ void ssh_sharing_queue_global_request(Ssh ssh,
ssh_sharing_global_request_response, share_ctx);
}
-static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
-{
- struct ssh_portfwd *epf;
- int i;
- char *key, *val;
-
- if (!ssh->portfwds) {
- ssh->portfwds = newtree234(ssh_portcmp);
- } else {
- /*
- * Go through the existing port forwardings and tag them
- * with status==DESTROY. Any that we want to keep will be
- * re-enabled (status==KEEP) as we go through the
- * configuration and find out which bits are the same as
- * they were before.
- */
- struct ssh_portfwd *epf;
- int i;
- for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
- epf->status = DESTROY;
- }
-
- for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
- val != NULL;
- val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
- char *kp, *kp2, *vp, *vp2;
- char address_family, type;
- int sport,dport,sserv,dserv;
- char *sports, *dports, *saddr, *host;
-
- kp = key;
-
- address_family = 'A';
- type = 'L';
- if (*kp == 'A' || *kp == '4' || *kp == '6')
- address_family = *kp++;
- if (*kp == 'L' || *kp == 'R')
- type = *kp++;
-
- if ((kp2 = host_strchr(kp, ':')) != NULL) {
- /*
- * There's a colon in the middle of the source port
- * string, which means that the part before it is
- * actually a source address.
- */
- char *saddr_tmp = dupprintf("%.*s", (int)(kp2 - kp), kp);
- saddr = host_strduptrim(saddr_tmp);
- sfree(saddr_tmp);
- sports = kp2+1;
- } else {
- saddr = NULL;
- sports = kp;
- }
- sport = atoi(sports);
- sserv = 0;
- if (sport == 0) {
- sserv = 1;
- sport = net_service_lookup(sports);
- if (!sport) {
- logeventf(ssh, "Service lookup failed for source"
- " port \"%s\"", sports);
- }
- }
-
- if (type == 'L' && !strcmp(val, "D")) {
- /* dynamic forwarding */
- host = NULL;
- dports = NULL;
- dport = -1;
- dserv = 0;
- type = 'D';
- } else {
- /* ordinary forwarding */
- vp = val;
- vp2 = vp + host_strcspn(vp, ":");
- host = dupprintf("%.*s", (int)(vp2 - vp), vp);
- if (*vp2)
- vp2++;
- dports = vp2;
- dport = atoi(dports);
- dserv = 0;
- if (dport == 0) {
- dserv = 1;
- dport = net_service_lookup(dports);
- if (!dport) {
- logeventf(ssh, "Service lookup failed for destination"
- " port \"%s\"", dports);
- }
- }
- }
-
- if (sport && dport) {
- /* Set up a description of the source port. */
- struct ssh_portfwd *pfrec, *epfrec;
-
- pfrec = snew(struct ssh_portfwd);
- pfrec->type = type;
- pfrec->saddr = saddr;
- pfrec->sserv = sserv ? dupstr(sports) : NULL;
- pfrec->sport = sport;
- pfrec->daddr = host;
- pfrec->dserv = dserv ? dupstr(dports) : NULL;
- pfrec->dport = dport;
- pfrec->local = NULL;
- pfrec->remote = NULL;
- pfrec->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 :
- address_family == '6' ? ADDRTYPE_IPV6 :
- ADDRTYPE_UNSPEC);
-
- epfrec = add234(ssh->portfwds, pfrec);
- if (epfrec != pfrec) {
- if (epfrec->status == DESTROY) {
- /*
- * We already have a port forwarding up and running
- * with precisely these parameters. Hence, no need
- * to do anything; simply re-tag the existing one
- * as KEEP.
- */
- epfrec->status = KEEP;
- }
- /*
- * Anything else indicates that there was a duplicate
- * in our input, which we'll silently ignore.
- */
- free_portfwd(pfrec);
- } else {
- pfrec->status = CREATE;
- }
- } else {
- sfree(saddr);
- sfree(host);
- }
- }
-
- /*
- * Now go through and destroy any port forwardings which were
- * not re-enabled.
- */
- for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
- if (epf->status == DESTROY) {
- char *message;
-
- message = dupprintf("%s port forwarding from %s%s%d",
- epf->type == 'L' ? "local" :
- epf->type == 'R' ? "remote" : "dynamic",
- epf->saddr ? epf->saddr : "",
- epf->saddr ? ":" : "",
- epf->sport);
-
- if (epf->type != 'D') {
- char *msg2 = dupprintf("%s to %s:%d", message,
- epf->daddr, epf->dport);
- sfree(message);
- message = msg2;
- }
-
- logeventf(ssh, "Cancelling %s", message);
- sfree(message);
-
- /* epf->remote or epf->local may be NULL if setting up a
- * forwarding failed. */
- if (epf->remote) {
- struct ssh_rportfwd *rpf = epf->remote;
- PktOut *pktout;
-
- /*
- * Cancel the port forwarding at the server
- * end.
- */
- if (ssh->version == 1) {
- /*
- * We cannot cancel listening ports on the
- * server side in SSH-1! There's no message
- * to support it. Instead, we simply remove
- * the rportfwd record from the local end
- * so that any connections the server tries
- * to make on it are rejected.
- */
- } else {
- pktout = ssh_bpp_new_pktout(
- ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
- put_stringz(pktout, "cancel-tcpip-forward");
- put_bool(pktout, 0);/* _don't_ want reply */
- if (epf->saddr) {
- put_stringz(pktout, epf->saddr);
- } else if (conf_get_int(conf, CONF_rport_acceptall)) {
- /* XXX: rport_acceptall may not represent
- * what was used to open the original connection,
- * since it's reconfigurable. */
- put_stringz(pktout, "");
- } else {
- put_stringz(pktout, "localhost");
- }
- put_uint32(pktout, epf->sport);
- ssh2_pkt_send(ssh, pktout);
- }
-
- del234(ssh->rportfwds, rpf);
- free_rportfwd(rpf);
- } else if (epf->local) {
- pfl_terminate(epf->local);
- }
-
- delpos234(ssh->portfwds, i);
- free_portfwd(epf);
- i--; /* so we don't skip one in the list */
- }
-
- /*
- * And finally, set up any new port forwardings (status==CREATE).
- */
- for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
- if (epf->status == CREATE) {
- char *sportdesc, *dportdesc;
- sportdesc = dupprintf("%s%s%s%s%d%s",
- epf->saddr ? epf->saddr : "",
- epf->saddr ? ":" : "",
- epf->sserv ? epf->sserv : "",
- epf->sserv ? "(" : "",
- epf->sport,
- epf->sserv ? ")" : "");
- if (epf->type == 'D') {
- dportdesc = NULL;
- } else {
- dportdesc = dupprintf("%s:%s%s%d%s",
- epf->daddr,
- epf->dserv ? epf->dserv : "",
- epf->dserv ? "(" : "",
- epf->dport,
- epf->dserv ? ")" : "");
- }
-
- if (epf->type == 'L') {
- char *err = pfl_listen(epf->daddr, epf->dport,
- epf->saddr, epf->sport,
- ssh, conf, &epf->local,
- epf->addressfamily);
-
- logeventf(ssh, "Local %sport %s forwarding to %s%s%s",
- epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
- epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
- sportdesc, dportdesc,
- err ? " failed: " : "", err ? err : "");
- if (err)
- sfree(err);
- } else if (epf->type == 'D') {
- char *err = pfl_listen(NULL, -1, epf->saddr, epf->sport,
- ssh, conf, &epf->local,
- epf->addressfamily);
-
- logeventf(ssh, "Local %sport %s SOCKS dynamic forwarding%s%s",
- epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
- epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
- sportdesc,
- err ? " failed: " : "", err ? err : "");
-
- if (err)
- sfree(err);
- } else {
- struct ssh_rportfwd *pf;
-
- /*
- * Ensure the remote port forwardings tree exists.
- */
- if (!ssh->rportfwds) {
- if (ssh->version == 1)
- ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);
- else
- ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
- }
-
- pf = snew(struct ssh_rportfwd);
- pf->share_ctx = NULL;
- pf->dhost = dupstr(epf->daddr);
- pf->dport = epf->dport;
- if (epf->saddr) {
- pf->shost = dupstr(epf->saddr);
- } else if (conf_get_int(conf, CONF_rport_acceptall)) {
- pf->shost = dupstr("");
- } else {
- pf->shost = dupstr("localhost");
- }
- pf->sport = epf->sport;
- if (add234(ssh->rportfwds, pf) != pf) {
- logeventf(ssh, "Duplicate remote port forwarding to %s:%d",
- epf->daddr, epf->dport);
- sfree(pf);
- } else {
- PktOut *pktout;
-
- logeventf(ssh, "Requesting remote port %s"
- " forward to %s", sportdesc, dportdesc);
-
- pf->sportdesc = sportdesc;
- sportdesc = NULL;
- epf->remote = pf;
- pf->pfrec = epf;
-
- if (ssh->version == 1) {
- pktout = ssh_bpp_new_pktout(
- ssh->bpp, SSH1_CMSG_PORT_FORWARD_REQUEST);
- put_uint32(pktout, epf->sport);
- put_stringz(pktout, epf->daddr);
- put_uint32(pktout, epf->dport);
- ssh_pkt_write(ssh, pktout);
- ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS,
- SSH1_SMSG_FAILURE,
- ssh_rportfwd_succfail, pf);
- } else {
- pktout = ssh_bpp_new_pktout(
- ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
- put_stringz(pktout, "tcpip-forward");
- put_bool(pktout, 1);/* want reply */
- put_stringz(pktout, pf->shost);
- put_uint32(pktout, pf->sport);
- ssh2_pkt_send(ssh, pktout);
-
- ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS,
- SSH2_MSG_REQUEST_FAILURE,
- ssh_rportfwd_succfail, pf);
- }
- }
- }
- sfree(sportdesc);
- sfree(dportdesc);
- }
-}
-
static void ssh1_smsg_stdout_stderr_data(Ssh ssh, PktIn *pktin)
{
ptrlen string;
@@ -4321,8 +3970,8 @@ static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
logeventf(ssh, "Received remote port open request for %s:%d",
pf.dhost, port);
- err = pfd_connect(&c->chan, pf.dhost, port,
- &c->sc, ssh->conf, pfp->pfrec->addressfamily);
+ err = portfwdmgr_connect(ssh->portfwdmgr, &c->chan, pf.dhost, port,
+ &c->sc, pfp->addressfamily);
if (err != NULL) {
logeventf(ssh, "Port open failed: %s", err);
sfree(err);
@@ -4562,7 +4211,7 @@ static void do_ssh1_connection(void *vctx)
}
}
- ssh_setup_portfwd(ssh, ssh->conf);
+ portfwdmgr_config(ssh->portfwdmgr, ssh->conf);
ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;
if (!conf_get_int(ssh->conf, CONF_nopty)) {
@@ -7858,8 +7507,9 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
return;
}
- err = pfd_connect(&c->chan, realpf->dhost, realpf->dport,
- &c->sc, ssh->conf, realpf->pfrec->addressfamily);
+ err = portfwdmgr_connect(
+ ssh->portfwdmgr, &c->chan, realpf->dhost, realpf->dport,
+ &c->sc, realpf->addressfamily);
logeventf(ssh, "Attempting to forward remote port to "
"%s:%d", realpf->dhost, realpf->dport);
if (err != NULL) {
@@ -9925,7 +9575,7 @@ static void do_ssh2_connection(void *vctx)
/*
* Enable port forwardings.
*/
- ssh_setup_portfwd(ssh, ssh->conf);
+ portfwdmgr_config(ssh->portfwdmgr, ssh->conf);
if (ssh->mainchan && !ssh->ncmode) {
/*
@@ -10703,7 +10353,7 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
ssh->channels = NULL;
ssh->rportfwds = NULL;
- ssh->portfwds = NULL;
+ ssh->portfwdmgr = portfwdmgr_new(ssh);
ssh->send_ok = 0;
ssh->editing = 0;
@@ -10798,6 +10448,7 @@ static void ssh_free(Backend *be)
freetree234(ssh->rportfwds);
ssh->rportfwds = NULL;
}
+ portfwdmgr_free(ssh->portfwdmgr);
if (ssh->x11disp)
x11_free_display(ssh->x11disp);
while ((auth = delpos234(ssh->x11authtree, 0)) != NULL)
@@ -10864,8 +10515,7 @@ static void ssh_reconfig(Backend *be, Conf *conf)
int i, rekey_time;
pinger_reconfig(ssh->pinger, ssh->conf, conf);
- if (ssh->portfwds)
- ssh_setup_portfwd(ssh, conf);
+ portfwdmgr_config(ssh->portfwdmgr, conf);
rekey_time = conf_get_int(conf, CONF_ssh_rekey_time);
if (ssh2_timer_update(ssh, rekey_mins(rekey_time, 60)))
diff --git a/ssh.h b/ssh.h
index a4aea86e..4f5fc204 100644
--- a/ssh.h
+++ b/ssh.h
@@ -149,10 +149,6 @@ void ssh_connshare_log(Ssh ssh, int event, const char *logtext,
const char *ds_err, const char *us_err);
unsigned ssh_alloc_sharing_channel(Ssh ssh, ssh_sharing_connstate *connstate);
void ssh_delete_sharing_channel(Ssh ssh, unsigned localid);
-int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
- ssh_sharing_connstate *connstate);
-void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
- ssh_sharing_connstate *connstate);
void ssh_sharing_queue_global_request(
Ssh ssh, ssh_sharing_connstate *connstate);
struct X11FakeAuth *ssh_sharing_add_x11_display(
@@ -175,6 +171,23 @@ void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
int protomajor, int protominor,
const void *initial_data, int initial_len);
+struct ssh_rportfwd;
+struct ssh_rportfwd *ssh_rportfwd_alloc(
+ Ssh ssh, const char *shost, int sport, const char *dhost, int dport,
+ int addressfamily, const char *log_description, PortFwdRecord *pfr,
+ ssh_sharing_connstate *share_ctx);
+void ssh_rportfwd_remove(Ssh ssh, struct ssh_rportfwd *rpf);
+
+/* Exports from portfwd.c */
+PortFwdManager *portfwdmgr_new(Ssh ssh);
+void portfwdmgr_free(PortFwdManager *mgr);
+void portfwdmgr_config(PortFwdManager *mgr, Conf *conf);
+void portfwdmgr_close(PortFwdManager *mgr, PortFwdRecord *pfr);
+void portfwdmgr_close_all(PortFwdManager *mgr);
+char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
+ char *hostname, int port, SshChannel *c,
+ int addressfamily);
+
Frontend *ssh_get_frontend(Ssh ssh);
#define SSH_CIPHER_IDEA 1
@@ -718,22 +731,10 @@ void random_add_heavynoise(void *noise, int length);
void logevent(Frontend *, const char *);
-struct PortForwarding;
-
/* Allocate and register a new channel for port forwarding */
SshChannel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
const char *org, Channel *chan);
-/* Exports from portfwd.c */
-extern char *pfd_connect(Channel **chan_ret, char *hostname, int port,
- SshChannel *c, Conf *conf, int addressfamily);
-struct PortListener;
-/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
-extern char *pfl_listen(char *desthost, int destport, char *srcaddr,
- int port, Ssh ssh, Conf *conf,
- struct PortListener **pl, int address_family);
-extern void pfl_terminate(struct PortListener *);
-
/* Exports from x11fwd.c */
enum {
X11_TRANS_IPV4 = 0, X11_TRANS_IPV6 = 6, X11_TRANS_UNIX = 256
diff --git a/sshshare.c b/sshshare.c
index 22611e7d..a43ab9c2 100644
--- a/sshshare.c
+++ b/sshshare.c
@@ -242,6 +242,7 @@ struct share_forwarding {
char *host;
int port;
int active; /* has the server sent REQUEST_SUCCESS? */
+ struct ssh_rportfwd *rpf;
};
struct share_xchannel_message {
@@ -856,8 +857,7 @@ static void share_try_cleanup(struct ssh_sharing_connstate *cs)
"cleanup after downstream went away");
strbuf_free(packet);
- ssh_remove_sharing_rportfwd(cs->parent->ssh,
- fwd->host, fwd->port, cs);
+ ssh_rportfwd_remove(cs->parent->ssh, fwd->rpf);
share_remove_forwarding(cs, fwd);
i--; /* don't accidentally skip one as a result */
}
@@ -1306,7 +1306,8 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
if (ptrlen_eq_string(request_name, "tcpip-forward")) {
ptrlen hostpl;
char *host;
- int port, ret;
+ int port;
+ struct ssh_rportfwd *rpf;
/*
* Pick the packet apart to find the want_reply field and
@@ -1328,8 +1329,9 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* ourselves to manufacture a failure packet and send it
* back to downstream.
*/
- ret = ssh_alloc_sharing_rportfwd(cs->parent->ssh, host, port, cs);
- if (!ret) {
+ rpf = ssh_rportfwd_alloc(
+ cs->parent->ssh, host, port, NULL, 0, 0, NULL, NULL, cs);
+ if (!rpf) {
if (orig_wantreply) {
send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
"", 0, NULL);
@@ -1359,6 +1361,8 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
globreq->fwd = fwd;
globreq->want_reply = orig_wantreply;
globreq->type = GLOBREQ_TCPIP_FORWARD;
+
+ fwd->rpf = rpf;
}
}
@@ -1395,7 +1399,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* Tell ssh.c to stop sending us channel-opens for
* this forwarding.
*/
- ssh_remove_sharing_rportfwd(cs->parent->ssh, host, port, cs);
+ ssh_rportfwd_remove(cs->parent->ssh, fwd->rpf);
/*
* Pass the cancel request on to the SSH server, but
From 8001dd4cbb9cdcd1b5431c0b289387a732cbc7ba Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Mon, 17 Sep 2018 12:14:00 +0100
Subject: [PATCH 410/607] New abstraction 'ConnectionLayer'.
This is a vtable that wraps up all the functionality required from the
SSH connection layer by associated modules like port forwarding and
connection sharing. This extra layer of indirection adds nothing
useful right now, but when I later separate the SSH-1 and SSH-2
connection layer implementations, it will be convenient for each one
to be able to implement this vtable in terms of its own internal data
structures.
To simplify this vtable, I've moved a lot of the logging duties
relating to connection sharing out of ssh.c into sshshare.c: now it
handles nearly all the logging itself relating to setting up
connection sharing in the first place and downstreams connecting and
disconnecting. The only exception is the 'Reusing a shared connection'
announcement in the console window, which is now done in ssh.c by
detecting downstream status immediately after setup.
---
defs.h | 1 +
portfwd.c | 61 +++++++++---------
ssh.c | 179 ++++++++++++++++++++++++++++-------------------------
ssh.h | 100 +++++++++++++++++++++---------
sshshare.c | 169 ++++++++++++++++++++++++++++++++++----------------
5 files changed, 313 insertions(+), 197 deletions(-)
diff --git a/defs.h b/defs.h
index 822ee71d..8bd9f13e 100644
--- a/defs.h
+++ b/defs.h
@@ -62,6 +62,7 @@ typedef struct share_channel share_channel;
typedef struct PortFwdManager PortFwdManager;
typedef struct PortFwdRecord PortFwdRecord;
+typedef struct ConnectionLayer ConnectionLayer;
typedef struct dlgparam dlgparam;
diff --git a/portfwd.c b/portfwd.c
index d200c78b..07210683 100644
--- a/portfwd.c
+++ b/portfwd.c
@@ -35,8 +35,8 @@ typedef enum {
} SocksState;
typedef struct PortForwarding {
- SshChannel *c; /* channel structure held by SSH backend */
- Ssh ssh; /* instance of SSH backend itself */
+ SshChannel *c; /* channel structure held by SSH connection layer */
+ ConnectionLayer *cl; /* the connection layer itself */
/* Note that ssh need not be filled in if c is non-NULL */
Socket s;
int input_wanted;
@@ -61,7 +61,7 @@ typedef struct PortForwarding {
} PortForwarding;
struct PortListener {
- Ssh ssh; /* instance of SSH backend itself */
+ ConnectionLayer *cl;
Socket s;
int is_dynamic;
/*
@@ -160,8 +160,9 @@ static void pfl_closing(Plug plug, const char *error_msg, int error_code,
pfl_terminate(pl);
}
-static SshChannel *wrap_send_port_open(
- Ssh ssh, const char *hostname, int port, Socket s, Channel *chan)
+static SshChannel *wrap_lportfwd_open(
+ ConnectionLayer *cl, const char *hostname, int port,
+ Socket s, Channel *chan)
{
char *peerinfo, *description;
SshChannel *toret;
@@ -174,7 +175,7 @@ static SshChannel *wrap_send_port_open(
description = dupstr("forwarding");
}
- toret = ssh_send_port_open(ssh, hostname, port, description, chan);
+ toret = ssh_lportfwd_open(cl, hostname, port, description, chan);
sfree(description);
return toret;
@@ -419,8 +420,8 @@ static void pfd_receive(Plug plug, int urgent, char *data, int len)
*/
sk_set_frozen(pf->s, 1);
- pf->c = wrap_send_port_open(pf->ssh, pf->hostname, pf->port, pf->s,
- &pf->chan);
+ pf->c = wrap_lportfwd_open(pf->cl, pf->hostname, pf->port, pf->s,
+ &pf->chan);
}
if (pf->ready)
sshfwd_write(pf->c, data, len);
@@ -480,7 +481,7 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
pf->input_wanted = TRUE;
pf->c = NULL;
- pf->ssh = pl->ssh;
+ pf->cl = pl->cl;
pf->s = s = constructor(ctx, &pf->plugvt);
if ((err = sk_socket_error(s)) != NULL) {
@@ -501,8 +502,8 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
pf->socks_state = SOCKS_NONE;
pf->hostname = dupstr(pl->hostname);
pf->port = pl->port;
- pf->c = wrap_send_port_open(pl->ssh, pf->hostname, pf->port,
- s, &pf->chan);
+ pf->c = wrap_lportfwd_open(pl->cl, pf->hostname, pf->port,
+ s, &pf->chan);
}
return 0;
@@ -525,7 +526,7 @@ static const Plug_vtable PortListener_plugvt = {
* dynamically allocated error message string.
*/
static char *pfl_listen(char *desthost, int destport, char *srcaddr,
- int port, Ssh ssh, Conf *conf,
+ int port, ConnectionLayer *cl, Conf *conf,
struct PortListener **pl_ret, int address_family)
{
const char *err;
@@ -542,7 +543,7 @@ static char *pfl_listen(char *desthost, int destport, char *srcaddr,
pl->is_dynamic = FALSE;
} else
pl->is_dynamic = TRUE;
- pl->ssh = ssh;
+ pl->cl = cl;
pl->s = new_listener(srcaddr, port, &pl->plugvt,
!conf_get_int(conf, CONF_lport_acceptall),
@@ -637,7 +638,7 @@ static void pfd_open_failure(Channel *chan, const char *errtext)
assert(chan->vt == &PortForwarding_channelvt);
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
- logeventf(ssh_get_frontend(pf->ssh),
+ logeventf(pf->cl->frontend,
"Forwarded connection refused by server%s%s",
errtext ? ": " : "", errtext ? errtext : "");
}
@@ -702,18 +703,16 @@ void pfr_free(PortFwdRecord *pfr)
}
struct PortFwdManager {
- Ssh ssh;
- Frontend *frontend;
+ ConnectionLayer *cl;
Conf *conf;
tree234 *forwardings;
};
-PortFwdManager *portfwdmgr_new(Ssh ssh)
+PortFwdManager *portfwdmgr_new(ConnectionLayer *cl)
{
PortFwdManager *mgr = snew(PortFwdManager);
- mgr->ssh = ssh;
- mgr->frontend = ssh_get_frontend(ssh);
+ mgr->cl = cl;
mgr->conf = NULL;
mgr->forwardings = newtree234(pfr_cmp);
@@ -801,7 +800,7 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
sserv = 1;
sport = net_service_lookup(sports);
if (!sport) {
- logeventf(mgr->frontend, "Service lookup failed for source"
+ logeventf(mgr->cl->frontend, "Service lookup failed for source"
" port \"%s\"", sports);
}
}
@@ -827,7 +826,7 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
dserv = 1;
dport = net_service_lookup(dports);
if (!dport) {
- logeventf(mgr->frontend,
+ logeventf(mgr->cl->frontend,
"Service lookup failed for destination"
" port \"%s\"", dports);
}
@@ -897,7 +896,7 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
message = msg2;
}
- logeventf(mgr->frontend, "Cancelling %s", message);
+ logeventf(mgr->cl->frontend, "Cancelling %s", message);
sfree(message);
/* pfr->remote or pfr->local may be NULL if setting up a
@@ -916,7 +915,7 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
* connections the server tries to make on it are
* rejected.
*/
- ssh_rportfwd_remove(mgr->ssh, pfr->remote);
+ ssh_rportfwd_remove(mgr->cl, pfr->remote);
} else if (pfr->local) {
pfl_terminate(pfr->local);
}
@@ -954,10 +953,10 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
if (pfr->type == 'L') {
char *err = pfl_listen(pfr->daddr, pfr->dport,
pfr->saddr, pfr->sport,
- mgr->ssh, conf, &pfr->local,
+ mgr->cl, conf, &pfr->local,
pfr->addressfamily);
- logeventf(mgr->frontend,
+ logeventf(mgr->cl->frontend,
"Local %sport %s forwarding to %s%s%s",
pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
@@ -967,10 +966,10 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
sfree(err);
} else if (pfr->type == 'D') {
char *err = pfl_listen(NULL, -1, pfr->saddr, pfr->sport,
- mgr->ssh, conf, &pfr->local,
+ mgr->cl, conf, &pfr->local,
pfr->addressfamily);
- logeventf(mgr->frontend,
+ logeventf(mgr->cl->frontend,
"Local %sport %s SOCKS dynamic forwarding%s%s",
pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
@@ -991,16 +990,16 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
}
pfr->remote = ssh_rportfwd_alloc(
- mgr->ssh, shost, pfr->sport, pfr->daddr, pfr->dport,
+ mgr->cl, shost, pfr->sport, pfr->daddr, pfr->dport,
pfr->addressfamily, sportdesc, pfr, NULL);
if (!pfr->remote) {
- logeventf(mgr->frontend,
+ logeventf(mgr->cl->frontend,
"Duplicate remote port forwarding to %s:%d",
pfr->daddr, pfr->dport);
pfr_free(pfr);
} else {
- logeventf(mgr->frontend, "Requesting remote port %s"
+ logeventf(mgr->cl->frontend, "Requesting remote port %s"
" forward to %s", sportdesc, dportdesc);
}
}
@@ -1049,7 +1048,7 @@ char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
pf->input_wanted = TRUE;
pf->ready = 1;
pf->c = c;
- pf->ssh = mgr->ssh;
+ pf->cl = mgr->cl;
pf->socks_state = SOCKS_NONE;
pf->s = new_connection(addr, dummy_realhost, port,
diff --git a/ssh.c b/ssh.c
index bedfe6af..9ec8553f 100644
--- a/ssh.c
+++ b/ssh.c
@@ -729,6 +729,8 @@ struct ssh_tag {
tree234 *rportfwds;
PortFwdManager *portfwdmgr;
+ ConnectionLayer cl;
+
enum {
SSH_STATE_PREPACKET,
SSH_STATE_BEFORE_SIZE,
@@ -2199,39 +2201,6 @@ static void ssh_socket_log(Plug plug, int type, SockAddr addr, int port,
ssh->session_started);
}
-void ssh_connshare_log(Ssh ssh, int event, const char *logtext,
- const char *ds_err, const char *us_err)
-{
- if (event == SHARE_NONE) {
- /* In this case, 'logtext' is an error message indicating a
- * reason why connection sharing couldn't be set up _at all_.
- * Failing that, ds_err and us_err indicate why we couldn't be
- * a downstream and an upstream respectively. */
- if (logtext) {
- logeventf(ssh, "Could not set up connection sharing: %s", logtext);
- } else {
- if (ds_err)
- logeventf(ssh, "Could not set up connection sharing"
- " as downstream: %s", ds_err);
- if (us_err)
- logeventf(ssh, "Could not set up connection sharing"
- " as upstream: %s", us_err);
- }
- } else if (event == SHARE_DOWNSTREAM) {
- /* In this case, 'logtext' is a local endpoint address */
- logeventf(ssh, "Using existing shared connection at %s", logtext);
- /* Also we should mention this in the console window to avoid
- * confusing users as to why this window doesn't behave the
- * usual way. */
- if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
- c_write_str(ssh,"Reusing a shared connection to this server.\r\n");
- }
- } else if (event == SHARE_UPSTREAM) {
- /* In this case, 'logtext' is a local endpoint address too */
- logeventf(ssh, "Sharing this connection at %s", logtext);
- }
-}
-
static void ssh_closing(Plug plug, const char *error_msg, int error_code,
int calling_back)
{
@@ -2363,7 +2332,7 @@ static const char *connect_to_host(Ssh ssh, const char *host, int port,
ssh->connshare = NULL;
ssh->attempting_connshare = TRUE; /* affects socket logging behaviour */
ssh->s = ssh_connection_sharing_init(
- ssh->savedhost, ssh->savedport, ssh->conf, ssh, &ssh->plugvt,
+ ssh->savedhost, ssh->savedport, ssh->conf, &ssh->cl, &ssh->plugvt,
&ssh->connshare);
ssh->attempting_connshare = FALSE;
if (ssh->s != NULL) {
@@ -2374,6 +2343,14 @@ static const char *connect_to_host(Ssh ssh, const char *host, int port,
ssh->current_incoming_data_fn = do_ssh_connection_init;
ssh->fullhostname = NULL;
*realhost = dupstr(host); /* best we can do */
+
+ if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
+ /* In an interactive session, or in verbose mode, announce
+ * in the console window that we're a sharing downstream,
+ * to avoid confusing users as to why this session doesn't
+ * behave in quite the usual way. */
+ c_write_str(ssh,"Reusing a shared connection to this server.\r\n");
+ }
} else {
/*
* We're not a downstream, so open a normal socket.
@@ -3758,11 +3735,56 @@ static void ssh_rportfwd_succfail(Ssh ssh, PktIn *pktin, void *ctx)
}
}
-struct ssh_rportfwd *ssh_rportfwd_alloc(
- Ssh ssh, const char *shost, int sport, const char *dhost, int dport,
+/* Many of these vtable methods have the same names as wrapper macros
+ * in ssh.h, so parenthesise the names to inhibit macro expansion */
+
+static struct ssh_rportfwd *(ssh_rportfwd_alloc)(
+ ConnectionLayer *cl,
+ const char *shost, int sport, const char *dhost, int dport,
+ int addressfamily, const char *log_description, PortFwdRecord *pfr,
+ ssh_sharing_connstate *share_ctx);
+static void (ssh_rportfwd_remove)(
+ ConnectionLayer *cl, struct ssh_rportfwd *rpf);
+static SshChannel *(ssh_lportfwd_open)(
+ ConnectionLayer *cl, const char *hostname, int port,
+ const char *org, Channel *chan);
+static struct X11FakeAuth *(ssh_add_sharing_x11_display)(
+ ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs,
+ share_channel *share_chan);
+static void (ssh_remove_sharing_x11_display)(ConnectionLayer *cl,
+ struct X11FakeAuth *auth);
+static void (ssh_send_packet_from_downstream)(
+ ConnectionLayer *cl, unsigned id, int type,
+ const void *pkt, int pktlen, const char *additional_log_text);
+static unsigned (ssh_alloc_sharing_channel)(
+ ConnectionLayer *cl, ssh_sharing_connstate *connstate);
+static void (ssh_delete_sharing_channel)(
+ ConnectionLayer *cl, unsigned localid);
+static void (ssh_sharing_queue_global_request)(
+ ConnectionLayer *cl, ssh_sharing_connstate *share_ctx);
+static int (ssh_agent_forwarding_permitted)(ConnectionLayer *cl);
+
+const struct ConnectionLayerVtable ssh_connlayer_vtable = {
+ ssh_rportfwd_alloc,
+ ssh_rportfwd_remove,
+ ssh_lportfwd_open,
+ ssh_add_sharing_x11_display,
+ ssh_remove_sharing_x11_display,
+ ssh_send_packet_from_downstream,
+ ssh_alloc_sharing_channel,
+ ssh_delete_sharing_channel,
+ ssh_sharing_queue_global_request,
+ ssh_agent_forwarding_permitted,
+};
+
+static struct ssh_rportfwd *(ssh_rportfwd_alloc)(
+ ConnectionLayer *cl,
+ const char *shost, int sport, const char *dhost, int dport,
int addressfamily, const char *log_description, PortFwdRecord *pfr,
ssh_sharing_connstate *share_ctx)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
+
/*
* Ensure the remote port forwardings tree exists.
*/
@@ -3819,8 +3841,10 @@ struct ssh_rportfwd *ssh_rportfwd_alloc(
return rpf;
}
-void ssh_rportfwd_remove(Ssh ssh, struct ssh_rportfwd *rpf)
+static void (ssh_rportfwd_remove)(
+ ConnectionLayer *cl, struct ssh_rportfwd *rpf)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
if (ssh->version == 1) {
/*
* We cannot cancel listening ports on the server side in
@@ -3855,9 +3879,10 @@ static void ssh_sharing_global_request_response(Ssh ssh, PktIn *pktin,
BinarySource_UPCAST(pktin)->len);
}
-void ssh_sharing_queue_global_request(Ssh ssh,
- ssh_sharing_connstate *share_ctx)
+static void (ssh_sharing_queue_global_request)(
+ ConnectionLayer *cl, ssh_sharing_connstate *share_ctx)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS, SSH2_MSG_REQUEST_FAILURE,
ssh_sharing_global_request_response, share_ctx);
}
@@ -4129,8 +4154,9 @@ static void ssh1_send_ttymode(BinarySink *bs,
}
}
-int ssh_agent_forwarding_permitted(Ssh ssh)
+static int (ssh_agent_forwarding_permitted)(ConnectionLayer *cl)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
return conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists();
}
@@ -4156,7 +4182,7 @@ static void do_ssh1_connection(void *vctx)
ssh->packet_dispatch[SSH1_MSG_CHANNEL_DATA] = ssh1_msg_channel_data;
ssh->packet_dispatch[SSH1_SMSG_EXIT_STATUS] = ssh1_smsg_exit_status;
- if (ssh_agent_forwarding_permitted(ssh)) {
+ if (ssh_agent_forwarding_permitted(&ssh->cl)) {
logevent("Requesting agent forwarding");
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AGENT_REQUEST_FORWARDING);
ssh_pkt_write(ssh, pkt);
@@ -6945,37 +6971,6 @@ static void ssh_check_termination(Ssh ssh)
}
}
-void ssh_sharing_downstream_connected(Ssh ssh, unsigned id,
- const char *peerinfo)
-{
- if (peerinfo)
- logeventf(ssh, "Connection sharing downstream #%u connected from %s",
- id, peerinfo);
- else
- logeventf(ssh, "Connection sharing downstream #%u connected", id);
-}
-
-void ssh_sharing_downstream_disconnected(Ssh ssh, unsigned id)
-{
- logeventf(ssh, "Connection sharing downstream #%u disconnected", id);
- ssh_check_termination(ssh);
-}
-
-void ssh_sharing_logf(Ssh ssh, unsigned id, const char *logfmt, ...)
-{
- va_list ap;
- char *buf;
-
- va_start(ap, logfmt);
- buf = dupvprintf(logfmt, ap);
- va_end(ap);
- if (id)
- logeventf(ssh, "Connection sharing downstream #%u: %s", id, buf);
- else
- logeventf(ssh, "Connection sharing: %s", buf);
- sfree(buf);
-}
-
/*
* Close any local socket and free any local resources associated with
* a channel. This converts the channel into a zombie.
@@ -7416,10 +7411,11 @@ static void ssh2_msg_global_request(Ssh ssh, PktIn *pktin)
}
}
-struct X11FakeAuth *ssh_sharing_add_x11_display(
- Ssh ssh, int authtype, ssh_sharing_connstate *share_cs,
+static struct X11FakeAuth *(ssh_add_sharing_x11_display)(
+ ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs,
share_channel *share_chan)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
struct X11FakeAuth *auth;
/*
@@ -7434,8 +7430,10 @@ struct X11FakeAuth *ssh_sharing_add_x11_display(
return auth;
}
-void ssh_sharing_remove_x11_display(Ssh ssh, struct X11FakeAuth *auth)
+static void (ssh_remove_sharing_x11_display)(
+ ConnectionLayer *cl, struct X11FakeAuth *auth)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
del234(ssh->x11authtree, auth);
x11_free_fake_auth(auth);
}
@@ -9477,8 +9475,8 @@ static void do_ssh2_connection(void *vctx)
* Just start a direct-tcpip channel and use it as the main
* channel.
*/
- mc->sc = ssh_send_port_open
- (ssh, conf_get_str(ssh->conf, CONF_ssh_nc_host),
+ mc->sc = ssh_lportfwd_open
+ (&ssh->cl, conf_get_str(ssh->conf, CONF_ssh_nc_host),
conf_get_int(ssh->conf, CONF_ssh_nc_port),
"main channel", &mc->chan);
ssh->mainchan = FROMFIELD(mc->sc, struct ssh_channel, sc);
@@ -9603,7 +9601,7 @@ static void do_ssh2_connection(void *vctx)
}
/* Potentially enable agent forwarding. */
- if (ssh_agent_forwarding_permitted(ssh))
+ if (ssh_agent_forwarding_permitted(&ssh->cl))
ssh2_setup_agent(ssh->mainchan, NULL, NULL);
/* Now allocate a pty for the session. */
@@ -10351,9 +10349,12 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
ssh->term_width = conf_get_int(ssh->conf, CONF_width);
ssh->term_height = conf_get_int(ssh->conf, CONF_height);
+ ssh->cl.vt = &ssh_connlayer_vtable;
+ ssh->cl.frontend = ssh->frontend;
+
ssh->channels = NULL;
ssh->rportfwds = NULL;
- ssh->portfwdmgr = portfwdmgr_new(ssh);
+ ssh->portfwdmgr = portfwdmgr_new(&ssh->cl);
ssh->send_ok = 0;
ssh->editing = 0;
@@ -10861,8 +10862,10 @@ static void ssh_special(Backend *be, Telnet_Special code)
}
}
-unsigned ssh_alloc_sharing_channel(Ssh ssh, ssh_sharing_connstate *connstate)
+static unsigned (ssh_alloc_sharing_channel)(
+ ConnectionLayer *cl, ssh_sharing_connstate *connstate)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
struct ssh_channel *c;
c = snew(struct ssh_channel);
@@ -10873,8 +10876,9 @@ unsigned ssh_alloc_sharing_channel(Ssh ssh, ssh_sharing_connstate *connstate)
return c->localid;
}
-void ssh_delete_sharing_channel(Ssh ssh, unsigned localid)
+static void (ssh_delete_sharing_channel)(ConnectionLayer *cl, unsigned localid)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
struct ssh_channel *c;
c = find234(ssh->channels, &localid, ssh_channelfind);
@@ -10882,10 +10886,11 @@ void ssh_delete_sharing_channel(Ssh ssh, unsigned localid)
ssh_channel_destroy(c);
}
-void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type,
- const void *data, int datalen,
- const char *additional_log_text)
+static void (ssh_send_packet_from_downstream)(
+ ConnectionLayer *cl, unsigned id, int type,
+ const void *data, int datalen, const char *additional_log_text)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
PktOut *pkt;
pkt = ssh_bpp_new_pktout(ssh->bpp, type);
@@ -10920,9 +10925,11 @@ static void ssh_unthrottle(Backend *be, int bufsize)
queue_idempotent_callback(&ssh->incoming_data_consumer);
}
-SshChannel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
- const char *org, Channel *chan)
+static SshChannel *(ssh_lportfwd_open)(
+ ConnectionLayer *cl, const char *hostname, int port,
+ const char *org, Channel *chan)
{
+ Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
struct ssh_channel *c = snew(struct ssh_channel);
PktOut *pktout;
diff --git a/ssh.h b/ssh.h
index 4f5fc204..a0085179 100644
--- a/ssh.h
+++ b/ssh.h
@@ -135,8 +135,8 @@ void ssh_unref_packet(PktIn *pkt);
void ssh_free_pktout(PktOut *pkt);
extern Socket ssh_connection_sharing_init(
- const char *host, int port, Conf *conf, Ssh ssh, Plug sshplug,
- ssh_sharing_state **state);
+ const char *host, int port, Conf *conf, ConnectionLayer *cl,
+ Plug sshplug, ssh_sharing_state **state);
int ssh_share_test_for_upstream(const char *host, int port, Conf *conf);
void share_got_pkt_from_server(ssh_sharing_connstate *ctx, int type,
const void *pkt, int pktlen);
@@ -147,22 +147,6 @@ int share_ndownstreams(ssh_sharing_state *state);
void ssh_connshare_log(Ssh ssh, int event, const char *logtext,
const char *ds_err, const char *us_err);
-unsigned ssh_alloc_sharing_channel(Ssh ssh, ssh_sharing_connstate *connstate);
-void ssh_delete_sharing_channel(Ssh ssh, unsigned localid);
-void ssh_sharing_queue_global_request(
- Ssh ssh, ssh_sharing_connstate *connstate);
-struct X11FakeAuth *ssh_sharing_add_x11_display(
- Ssh ssh, int authtype, ssh_sharing_connstate *share_cs,
- share_channel *share_chan);
-void ssh_sharing_remove_x11_display(Ssh ssh, struct X11FakeAuth *auth);
-void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type,
- const void *pkt, int pktlen,
- const char *additional_log_text);
-void ssh_sharing_downstream_connected(Ssh ssh, unsigned id,
- const char *peerinfo);
-void ssh_sharing_downstream_disconnected(Ssh ssh, unsigned id);
-void ssh_sharing_logf(Ssh ssh, unsigned id, const char *logfmt, ...);
-int ssh_agent_forwarding_permitted(Ssh ssh);
void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
unsigned upstream_id, unsigned server_id,
unsigned server_currwin, unsigned server_maxpkt,
@@ -172,14 +156,78 @@ void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
const void *initial_data, int initial_len);
struct ssh_rportfwd;
-struct ssh_rportfwd *ssh_rportfwd_alloc(
- Ssh ssh, const char *shost, int sport, const char *dhost, int dport,
- int addressfamily, const char *log_description, PortFwdRecord *pfr,
- ssh_sharing_connstate *share_ctx);
-void ssh_rportfwd_remove(Ssh ssh, struct ssh_rportfwd *rpf);
+
+struct ConnectionLayerVtable {
+ /* Allocate and free remote-to-local port forwardings, called by
+ * PortFwdManager or by connection sharing */
+ struct ssh_rportfwd *(*rportfwd_alloc)(
+ ConnectionLayer *cl,
+ const char *shost, int sport, const char *dhost, int dport,
+ int addressfamily, const char *log_description, PortFwdRecord *pfr,
+ ssh_sharing_connstate *share_ctx);
+ void (*rportfwd_remove)(ConnectionLayer *cl, struct ssh_rportfwd *rpf);
+
+ /* Open a local-to-remote port forwarding channel, called by
+ * PortFwdManager */
+ SshChannel *(*lportfwd_open)(
+ ConnectionLayer *cl, const char *hostname, int port,
+ const char *org, Channel *chan);
+
+ /* Add and remove X11 displays for connection sharing downstreams */
+ struct X11FakeAuth *(*add_sharing_x11_display)(
+ ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs,
+ share_channel *share_chan);
+ void (*remove_sharing_x11_display)(
+ ConnectionLayer *cl, struct X11FakeAuth *auth);
+
+ /* Pass through an outgoing SSH packet from a downstream */
+ void (*send_packet_from_downstream)(
+ ConnectionLayer *cl, unsigned id, int type,
+ const void *pkt, int pktlen, const char *additional_log_text);
+
+ /* Allocate/free an upstream channel number associated with a
+ * sharing downstream */
+ unsigned (*alloc_sharing_channel)(ConnectionLayer *cl,
+ ssh_sharing_connstate *connstate);
+ void (*delete_sharing_channel)(ConnectionLayer *cl, unsigned localid);
+
+ /* Indicate that a downstream has sent a global request with the
+ * want-reply flag, so that when a reply arrives it will be passed
+ * back to that downstrean */
+ void (*sharing_queue_global_request)(
+ ConnectionLayer *cl, ssh_sharing_connstate *connstate);
+
+ /* Query whether the connection layer is doing agent forwarding */
+ int (*agent_forwarding_permitted)(ConnectionLayer *cl);
+};
+
+struct ConnectionLayer {
+ Frontend *frontend;
+ const struct ConnectionLayerVtable *vt;
+};
+
+#define ssh_rportfwd_alloc(cl, sh, sp, dh, dp, af, ld, pfr, share) \
+ ((cl)->vt->rportfwd_alloc(cl, sh, sp, dh, dp, af, ld, pfr, share))
+#define ssh_rportfwd_remove(cl, rpf) ((cl)->vt->rportfwd_remove(cl, rpf))
+#define ssh_lportfwd_open(cl, h, p, org, chan) \
+ ((cl)->vt->lportfwd_open(cl, h, p, org, chan))
+#define ssh_add_sharing_x11_display(cl, auth, cs, ch) \
+ ((cl)->vt->add_sharing_x11_display(cl, auth, cs, ch))
+#define ssh_remove_sharing_x11_display(cl, fa) \
+ ((cl)->vt->remove_sharing_x11_display(cl, fa))
+#define ssh_send_packet_from_downstream(cl, id, type, pkt, len, log) \
+ ((cl)->vt->send_packet_from_downstream(cl, id, type, pkt, len, log))
+#define ssh_alloc_sharing_channel(cl, cs) \
+ ((cl)->vt->alloc_sharing_channel(cl, cs))
+#define ssh_delete_sharing_channel(cl, ch) \
+ ((cl)->vt->delete_sharing_channel(cl, ch))
+#define ssh_sharing_queue_global_request(cl, cs) \
+ ((cl)->vt->sharing_queue_global_request(cl, cs))
+#define ssh_agent_forwarding_permitted(cl) \
+ ((cl)->vt->agent_forwarding_permitted(cl))
/* Exports from portfwd.c */
-PortFwdManager *portfwdmgr_new(Ssh ssh);
+PortFwdManager *portfwdmgr_new(ConnectionLayer *cl);
void portfwdmgr_free(PortFwdManager *mgr);
void portfwdmgr_config(PortFwdManager *mgr, Conf *conf);
void portfwdmgr_close(PortFwdManager *mgr, PortFwdRecord *pfr);
@@ -731,10 +779,6 @@ void random_add_heavynoise(void *noise, int length);
void logevent(Frontend *, const char *);
-/* Allocate and register a new channel for port forwarding */
-SshChannel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
- const char *org, Channel *chan);
-
/* Exports from x11fwd.c */
enum {
X11_TRANS_IPV4 = 0, X11_TRANS_IPV6 = 6, X11_TRANS_UNIX = 256
diff --git a/sshshare.c b/sshshare.c
index a43ab9c2..7515d117 100644
--- a/sshshare.c
+++ b/sshshare.c
@@ -144,7 +144,7 @@ struct ssh_sharing_state {
Socket listensock; /* the master listening Socket */
tree234 *connections; /* holds ssh_sharing_connstates */
unsigned nextid; /* preferred id for next connstate */
- Ssh ssh; /* instance of the ssh backend */
+ ConnectionLayer *cl; /* instance of the ssh connection layer */
char *server_verstring; /* server version string after "SSH-" */
const Plug_vtable *plugvt;
@@ -618,7 +618,7 @@ static void share_remove_channel(struct ssh_sharing_connstate *cs,
del234(cs->channels_by_us, chan);
del234(cs->channels_by_server, chan);
if (chan->x11_auth_upstream)
- ssh_sharing_remove_x11_display(cs->parent->ssh,
+ ssh_remove_sharing_x11_display(cs->parent->cl,
chan->x11_auth_upstream);
sfree(chan->x11_auth_data);
sfree(chan);
@@ -703,6 +703,45 @@ static void share_remove_forwarding(struct ssh_sharing_connstate *cs,
sfree(fwd);
}
+static void logeventf(Frontend *frontend, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+
+ va_start(ap, fmt);
+ buf = dupvprintf(fmt, ap);
+ va_end(ap);
+ logevent(frontend, buf);
+ sfree(buf);
+}
+
+static void log_downstream(struct ssh_sharing_connstate *cs,
+ const char *logfmt, ...)
+{
+ va_list ap;
+ char *buf;
+
+ va_start(ap, logfmt);
+ buf = dupvprintf(logfmt, ap);
+ va_end(ap);
+ logeventf(cs->parent->cl->frontend,
+ "Connection sharing downstream #%u: %s", cs->id, buf);
+ sfree(buf);
+}
+
+static void log_general(struct ssh_sharing_state *sharestate,
+ const char *logfmt, ...)
+{
+ va_list ap;
+ char *buf;
+
+ va_start(ap, logfmt);
+ buf = dupvprintf(logfmt, ap);
+ va_end(ap);
+ logeventf(sharestate->cl->frontend, "Connection sharing: %s", buf);
+ sfree(buf);
+}
+
static void send_packet_to_downstream(struct ssh_sharing_connstate *cs,
int type, const void *pkt, int pktlen,
struct share_channel *chan)
@@ -790,7 +829,7 @@ static void share_try_cleanup(struct ssh_sharing_connstate *cs)
put_stringz(packet, reason);
put_stringz(packet, lang);
ssh_send_packet_from_downstream(
- cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_OPEN_FAILURE,
+ cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_OPEN_FAILURE,
packet->s, packet->len,
"cleanup after downstream went away");
strbuf_free(packet);
@@ -819,7 +858,7 @@ static void share_try_cleanup(struct ssh_sharing_connstate *cs)
packet = strbuf_new();
put_uint32(packet, chan->server_id);
ssh_send_packet_from_downstream(
- cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_CLOSE,
+ cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_CLOSE,
packet->s, packet->len,
"cleanup after downstream went away");
strbuf_free(packet);
@@ -828,7 +867,7 @@ static void share_try_cleanup(struct ssh_sharing_connstate *cs)
chan->state = SENT_CLOSE;
} else {
/* In this case, we _can_ clear up the channel now. */
- ssh_delete_sharing_channel(cs->parent->ssh, chan->upstream_id);
+ ssh_delete_sharing_channel(cs->parent->cl, chan->upstream_id);
share_remove_channel(cs, chan);
i--; /* don't accidentally skip one as a result */
}
@@ -852,12 +891,12 @@ static void share_try_cleanup(struct ssh_sharing_connstate *cs)
put_stringz(packet, fwd->host);
put_uint32(packet, fwd->port);
ssh_send_packet_from_downstream(
- cs->parent->ssh, cs->id, SSH2_MSG_GLOBAL_REQUEST,
+ cs->parent->cl, cs->id, SSH2_MSG_GLOBAL_REQUEST,
packet->s, packet->len,
"cleanup after downstream went away");
strbuf_free(packet);
- ssh_rportfwd_remove(cs->parent->ssh, fwd->rpf);
+ ssh_rportfwd_remove(cs->parent->cl, fwd->rpf);
share_remove_forwarding(cs, fwd);
i--; /* don't accidentally skip one as a result */
}
@@ -870,7 +909,7 @@ static void share_try_cleanup(struct ssh_sharing_connstate *cs)
* Now we're _really_ done, so we can get rid of cs completely.
*/
del234(cs->parent->connections, cs);
- ssh_sharing_downstream_disconnected(cs->parent->ssh, cs->id);
+ log_downstream(cs, "disconnected");
share_connstate_free(cs);
}
}
@@ -919,8 +958,7 @@ static void share_closing(Plug plug, const char *error_msg, int error_code,
/* do nothing */;
else
#endif
- ssh_sharing_logf(cs->parent->ssh, cs->id,
- "Socket error: %s", error_msg);
+ log_downstream(cs, "Socket error: %s", error_msg);
}
share_begin_cleanup(cs);
}
@@ -979,7 +1017,7 @@ void share_dead_xchannel_respond(struct ssh_sharing_connstate *cs,
strbuf *packet = strbuf_new();
put_uint32(packet, xc->server_id);
ssh_send_packet_from_downstream
- (cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_FAILURE,
+ (cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_FAILURE,
packet->s, packet->len,
"downstream refused X channel open");
strbuf_free(packet);
@@ -995,7 +1033,7 @@ void share_dead_xchannel_respond(struct ssh_sharing_connstate *cs,
}
xc->msgtail = NULL;
if (delete) {
- ssh_delete_sharing_channel(cs->parent->ssh, xc->upstream_id);
+ ssh_delete_sharing_channel(cs->parent->cl, xc->upstream_id);
share_remove_xchannel(cs, xc);
}
}
@@ -1031,7 +1069,7 @@ void share_xchannel_confirmation(struct ssh_sharing_connstate *cs,
put_uint32(packet, xc->server_id);
put_uint32(packet, downstream_window - xc->window);
ssh_send_packet_from_downstream(
- cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_WINDOW_ADJUST,
+ cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_WINDOW_ADJUST,
packet->s, packet->len,
"window adjustment after downstream accepted X channel");
strbuf_free(packet);
@@ -1047,7 +1085,7 @@ void share_xchannel_failure(struct ssh_sharing_connstate *cs,
strbuf *packet = strbuf_new();
put_uint32(packet, xc->server_id);
ssh_send_packet_from_downstream(
- cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_CLOSE,
+ cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_CLOSE,
packet->s, packet->len,
"downstream refused X channel open");
strbuf_free(packet);
@@ -1115,7 +1153,7 @@ void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
* If this was a once-only X forwarding, clean it up now.
*/
if (chan->x11_one_shot) {
- ssh_sharing_remove_x11_display(cs->parent->ssh,
+ ssh_remove_sharing_x11_display(cs->parent->cl,
chan->x11_auth_upstream);
chan->x11_auth_upstream = NULL;
sfree(chan->x11_auth_data);
@@ -1221,11 +1259,11 @@ void share_got_pkt_from_server(ssh_sharing_connstate *cs, int type,
}
}
} else if (type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
- ssh_delete_sharing_channel(cs->parent->ssh, chan->upstream_id);
+ ssh_delete_sharing_channel(cs->parent->cl, chan->upstream_id);
share_remove_channel(cs, chan);
} else if (type == SSH2_MSG_CHANNEL_CLOSE) {
if (chan->state == SENT_CLOSE) {
- ssh_delete_sharing_channel(cs->parent->ssh,
+ ssh_delete_sharing_channel(cs->parent->cl,
chan->upstream_id);
share_remove_channel(cs, chan);
if (!cs->sock) {
@@ -1330,7 +1368,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* back to downstream.
*/
rpf = ssh_rportfwd_alloc(
- cs->parent->ssh, host, port, NULL, 0, 0, NULL, NULL, cs);
+ cs->parent->cl, host, port, NULL, 0, 0, NULL, NULL, cs);
if (!rpf) {
if (orig_wantreply) {
send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
@@ -1346,10 +1384,10 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
*/
pkt[wantreplypos] = 1;
ssh_send_packet_from_downstream
- (cs->parent->ssh, cs->id, type, pkt, pktlen,
+ (cs->parent->cl, cs->id, type, pkt, pktlen,
orig_wantreply ? NULL : "upstream added want_reply flag");
fwd = share_add_forwarding(cs, host, port);
- ssh_sharing_queue_global_request(cs->parent->ssh, cs);
+ ssh_sharing_queue_global_request(cs->parent->cl, cs);
if (fwd) {
globreq = snew(struct share_globreq);
@@ -1399,7 +1437,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* Tell ssh.c to stop sending us channel-opens for
* this forwarding.
*/
- ssh_rportfwd_remove(cs->parent->ssh, fwd->rpf);
+ ssh_rportfwd_remove(cs->parent->cl, fwd->rpf);
/*
* Pass the cancel request on to the SSH server, but
@@ -1409,9 +1447,9 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
*/
pkt[wantreplypos] = 1;
ssh_send_packet_from_downstream
- (cs->parent->ssh, cs->id, type, pkt, pktlen,
+ (cs->parent->cl, cs->id, type, pkt, pktlen,
orig_wantreply ? NULL : "upstream added want_reply flag");
- ssh_sharing_queue_global_request(cs->parent->ssh, cs);
+ ssh_sharing_queue_global_request(cs->parent->cl, cs);
/*
* And queue a globreq so that when the reply comes
@@ -1445,7 +1483,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
get_string(src);
id_pos = src->pos;
old_id = get_uint32(src);
- new_id = ssh_alloc_sharing_channel(cs->parent->ssh, cs);
+ new_id = ssh_alloc_sharing_channel(cs->parent->cl, cs);
get_uint32(src); /* skip initial window size */
maxpkt = get_uint32(src);
if (get_err(src)) {
@@ -1454,7 +1492,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
}
share_add_channel(cs, old_id, new_id, 0, UNACKNOWLEDGED, maxpkt);
PUT_32BIT(pkt + id_pos, new_id);
- ssh_send_packet_from_downstream(cs->parent->ssh, cs->id,
+ ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
type, pkt, pktlen, NULL);
break;
@@ -1477,7 +1515,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
/* This server id may refer to either a halfchannel or an xchannel. */
hc = NULL, xc = NULL; /* placate optimiser */
if ((hc = share_find_halfchannel(cs, server_id)) != NULL) {
- new_id = ssh_alloc_sharing_channel(cs->parent->ssh, cs);
+ new_id = ssh_alloc_sharing_channel(cs->parent->cl, cs);
} else if ((xc = share_find_xchannel_by_server(cs, server_id))
!= NULL) {
new_id = xc->upstream_id;
@@ -1491,7 +1529,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
chan = share_add_channel(cs, old_id, new_id, server_id, OPEN, maxpkt);
if (hc) {
- ssh_send_packet_from_downstream(cs->parent->ssh, cs->id,
+ ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
type, pkt, pktlen, NULL);
share_remove_halfchannel(cs, hc);
} else if (xc) {
@@ -1515,7 +1553,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
/* This server id may refer to either a halfchannel or an xchannel. */
if ((hc = share_find_halfchannel(cs, server_id)) != NULL) {
- ssh_send_packet_from_downstream(cs->parent->ssh, cs->id,
+ ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
type, pkt, pktlen, NULL);
share_remove_halfchannel(cs, hc);
} else if ((xc = share_find_xchannel_by_server(cs, server_id))
@@ -1569,7 +1607,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
* a parent session channel.)
*/
if (ptrlen_eq_string(request_name, "auth-agent-req@openssh.com") &&
- !ssh_agent_forwarding_permitted(cs->parent->ssh)) {
+ !ssh_agent_forwarding_permitted(cs->parent->cl)) {
chan = share_find_channel_by_server(cs, server_id);
if (chan) {
@@ -1643,7 +1681,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
chan->x11_auth_data = x11_dehexify(auth_data,
&chan->x11_auth_datalen);
chan->x11_auth_upstream =
- ssh_sharing_add_x11_display(cs->parent->ssh, auth_proto,
+ ssh_add_sharing_x11_display(cs->parent->cl, auth_proto,
cs, chan);
chan->x11_one_shot = single_connection;
@@ -1661,7 +1699,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
put_stringz(packet, chan->x11_auth_upstream->datastring);
put_uint32(packet, screen);
ssh_send_packet_from_downstream(
- cs->parent->ssh, cs->id, SSH2_MSG_CHANNEL_REQUEST,
+ cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_REQUEST,
packet->s, packet->len, NULL);
strbuf_free(packet);
@@ -1669,13 +1707,13 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
}
}
- ssh_send_packet_from_downstream(cs->parent->ssh, cs->id,
+ ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
type, pkt, pktlen, NULL);
if (type == SSH2_MSG_CHANNEL_CLOSE && pktlen >= 4) {
chan = share_find_channel_by_server(cs, server_id);
if (chan) {
if (chan->state == RCVD_CLOSE) {
- ssh_delete_sharing_channel(cs->parent->ssh,
+ ssh_delete_sharing_channel(cs->parent->cl,
chan->upstream_id);
share_remove_channel(cs, chan);
} else {
@@ -1757,9 +1795,8 @@ static void share_receive(Plug plug, int urgent, char *data, int len)
}
if (cs->recvlen > 0 && cs->recvbuf[cs->recvlen-1] == '\015')
cs->recvlen--; /* trim off \r before \n */
- ssh_sharing_logf(cs->parent->ssh, cs->id,
- "Downstream version string: %.*s",
- cs->recvlen, cs->recvbuf);
+ log_downstream(cs, "Downstream version string: %.*s",
+ cs->recvlen, cs->recvbuf);
cs->got_verstring = TRUE;
/*
@@ -1813,8 +1850,7 @@ static void share_listen_closing(Plug plug, const char *error_msg,
{
ssh_sharing_state *sharestate = FROMFIELD(plug, ssh_sharing_state, plugvt);
if (error_msg)
- ssh_sharing_logf(sharestate->ssh, 0,
- "listening socket: %s", error_msg);
+ log_general(sharestate, "listening socket: %s", error_msg);
sk_close(sharestate->listensock);
sharestate->listensock = NULL;
}
@@ -1923,7 +1959,9 @@ static int share_listen_accepting(Plug plug,
cs->globreq_head = cs->globreq_tail = NULL;
peerinfo = sk_peer_info(cs->sock);
- ssh_sharing_downstream_connected(sharestate->ssh, cs->id, peerinfo);
+ log_downstream(cs, "connected%s%s",
+ peerinfo ? " from " : "", peerinfo ? peerinfo : "");
+
sfree(peerinfo);
return 0;
@@ -2024,14 +2062,14 @@ static const Plug_vtable ssh_sharing_listen_plugvt = {
* to the upstream; otherwise (whether or not we have established an
* upstream) we return NULL.
*/
-Socket ssh_connection_sharing_init(const char *host, int port,
- Conf *conf, Ssh ssh, Plug sshplug,
- ssh_sharing_state **state)
+Socket ssh_connection_sharing_init(
+ const char *host, int port, Conf *conf, ConnectionLayer *cl,
+ Plug sshplug, ssh_sharing_state **state)
{
int result, can_upstream, can_downstream;
char *logtext, *ds_err, *us_err;
char *sockname;
- Socket sock;
+ Socket sock, toret = NULL;
struct ssh_sharing_state *sharestate;
if (!conf_get_int(conf, CONF_ssh_connection_sharing))
@@ -2065,10 +2103,6 @@ Socket ssh_connection_sharing_init(const char *host, int port,
result = platform_ssh_share(
sockname, conf, sshplug, &sharestate->plugvt, &sock, &logtext,
&ds_err, &us_err, can_upstream, can_downstream);
- ssh_connshare_log(ssh, result, logtext, ds_err, us_err);
- sfree(logtext);
- sfree(ds_err);
- sfree(us_err);
switch (result) {
case SHARE_NONE:
/*
@@ -2076,11 +2110,29 @@ Socket ssh_connection_sharing_init(const char *host, int port,
* went wrong setting the socket up). Free the upstream
* structure and return NULL.
*/
+
+ if (logtext) {
+ /* For this result, if 'logtext' is not NULL then it is an
+ * error message indicating a reason why connection sharing
+ * couldn't be set up _at all_ */
+ logeventf(cl->frontend,
+ "Could not set up connection sharing: %s", logtext);
+ } else {
+ /* Failing that, ds_err and us_err indicate why we
+ * couldn't be a downstream and an upstream respectively */
+ if (ds_err)
+ logeventf(cl->frontend, "Could not set up connection sharing"
+ " as downstream: %s", ds_err);
+ if (us_err)
+ logeventf(cl->frontend, "Could not set up connection sharing"
+ " as upstream: %s", us_err);
+ }
+
assert(sock == NULL);
*state = NULL;
sfree(sharestate);
sfree(sockname);
- return NULL;
+ break;
case SHARE_DOWNSTREAM:
/*
@@ -2088,10 +2140,16 @@ Socket ssh_connection_sharing_init(const char *host, int port,
* don't need after all, and return the downstream socket as a
* replacement for an ordinary SSH connection.
*/
+
+ /* 'logtext' is a local endpoint address */
+ logeventf(cl->frontend,
+ "Using existing shared connection at %s", logtext);
+
*state = NULL;
sfree(sharestate);
sfree(sockname);
- return sock;
+ toret = sock;
+ break;
case SHARE_UPSTREAM:
/*
@@ -2099,15 +2157,22 @@ Socket ssh_connection_sharing_init(const char *host, int port,
* to the caller; return NULL, to tell ssh.c that it has to
* make an ordinary connection after all.
*/
+
+ /* 'logtext' is a local endpoint address */
+ logeventf(cl->frontend, "Sharing this connection at %s", logtext);
+
*state = sharestate;
sharestate->listensock = sock;
sharestate->connections = newtree234(share_connstate_cmp);
- sharestate->ssh = ssh;
+ sharestate->cl = cl;
sharestate->server_verstring = NULL;
sharestate->sockname = sockname;
sharestate->nextid = 1;
- return NULL;
+ break;
}
- return NULL;
+ sfree(logtext);
+ sfree(ds_err);
+ sfree(us_err);
+ return toret;
}
From ff7418af730b02d963ff15a660f60811b2c2577a Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 12:57:33 +0100
Subject: [PATCH 411/607] tree234.c: minor fixes to the test suite.
Added a missing #include of string.h; arranged that the test program
reports the number of errors detected at the end so I don't have to
search its entire output for "ERROR"; made the test program return a
nonzero exit status if any error was found.
---
tree234.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tree234.c b/tree234.c
index d3c5293d..6c826309 100644
--- a/tree234.c
+++ b/tree234.c
@@ -1014,6 +1014,9 @@ void *del234(tree234 * t, void *e)
*/
#include
+#include
+
+int n_errors = 0;
/*
* Error reporting function.
@@ -1026,6 +1029,7 @@ void error(char *fmt, ...)
vfprintf(stdout, fmt, ap);
va_end(ap);
printf("\n");
+ n_errors++;
}
/* The array representation of the data. */
@@ -1480,7 +1484,8 @@ int main(void)
delpostest(j);
}
- return 0;
+ printf("%d errors found\n", n_errors);
+ return (n_errors != 0);
}
#endif
From b2d0bd0da4ab023bb5f9b59bfccebd2e09b14f07 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 12:58:54 +0100
Subject: [PATCH 412/607] tree234.c: new search234() system.
This is a thing I've been meaning to set up for a while: it's a
pull-based search system (that is, the caller takes each step of the
search manually, rather than providing a callback), which lets the
caller inspect every step of the search, including the index of each
candidate element in the tree. This allows flexible kinds of query
that play the element and its index off against each other.
I've also rewritten the existing findrelpos234() search function using
the new one as a primitive, because that simplifies it a lot!
---
tree234.c | 298 ++++++++++++++++++++++++++++++++++++++----------------
tree234.h | 35 +++++++
2 files changed, 245 insertions(+), 88 deletions(-)
diff --git a/tree234.c b/tree234.c
index 6c826309..ea8ff9ee 100644
--- a/tree234.c
+++ b/tree234.c
@@ -107,6 +107,18 @@ static int countnode234(node234 * n)
return count;
}
+/*
+ * Internal function to return the number of elements in a node.
+ */
+static int elements234(node234 *n)
+{
+ int i;
+ for (i = 0; i < 3; i++)
+ if (!n->elems[i])
+ break;
+ return i;
+}
+
/*
* Count the elements in a tree.
*/
@@ -514,99 +526,66 @@ void *index234(tree234 * t, int index)
void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp,
int relation, int *index)
{
- node234 *n;
- void *ret;
- int c;
- int idx, ecount, kcount, cmpret;
+ search234_state ss;
+ int reldir = (relation == REL234_LT || relation == REL234_LE ? -1 :
+ relation == REL234_GT || relation == REL234_GE ? +1 : 0);
+ int equal_permitted = (relation != REL234_LT && relation != REL234_GT);
+ void *toret;
- if (t->root == NULL)
- return NULL;
+ /* Only LT / GT relations are permitted with a null query element. */
+ assert(!(equal_permitted && !e));
if (cmp == NULL)
cmp = t->cmp;
- n = t->root;
+ search234_start(&ss, t);
+ while (ss.element) {
+ int cmpret;
+
+ if (e) {
+ cmpret = cmp(e, ss.element);
+ } else {
+ cmpret = -reldir; /* invent a fixed compare result */
+ }
+
+ if (cmpret == 0) {
+ /*
+ * We've found an element that compares exactly equal to
+ * the query element.
+ */
+ if (equal_permitted) {
+ /* If our search relation permits equality, we've
+ * finished already. */
+ if (index)
+ *index = ss.index;
+ return ss.element;
+ } else {
+ /* Otherwise, pretend this element was slightly too
+ * big/small, according to the direction of search. */
+ cmpret = reldir;
+ }
+ }
+
+ search234_step(&ss, cmpret);
+ }
+
/*
- * Attempt to find the element itself.
+ * No element compares equal to the one we were after, but
+ * ss.index indicates the index that element would have if it were
+ * inserted.
+ *
+ * So if our search relation is EQ, we must simply return failure.
*/
- idx = 0;
- ecount = -1;
+ if (relation == REL234_EQ)
+ return NULL;
+
/*
- * Prepare a fake `cmp' result if e is NULL.
+ * Otherwise, we must do an index lookup for the previous index
+ * (if we're going left - LE or LT) or this index (if we're going
+ * right - GE or GT).
*/
- cmpret = 0;
- if (e == NULL) {
- assert(relation == REL234_LT || relation == REL234_GT);
- if (relation == REL234_LT)
- cmpret = +1; /* e is a max: always greater */
- else if (relation == REL234_GT)
- cmpret = -1; /* e is a min: always smaller */
- }
- while (1) {
- for (kcount = 0; kcount < 4; kcount++) {
- if (kcount >= 3 || n->elems[kcount] == NULL ||
- (c = cmpret ? cmpret : cmp(e, n->elems[kcount])) < 0) {
- break;
- }
- if (n->kids[kcount])
- idx += n->counts[kcount];
- if (c == 0) {
- ecount = kcount;
- break;
- }
- idx++;
- }
- if (ecount >= 0)
- break;
- if (n->kids[kcount])
- n = n->kids[kcount];
- else
- break;
- }
-
- if (ecount >= 0) {
- /*
- * We have found the element we're looking for. It's
- * n->elems[ecount], at tree index idx. If our search
- * relation is EQ, LE or GE we can now go home.
- */
- if (relation != REL234_LT && relation != REL234_GT) {
- if (index)
- *index = idx;
- return n->elems[ecount];
- }
-
- /*
- * Otherwise, we'll do an indexed lookup for the previous
- * or next element. (It would be perfectly possible to
- * implement these search types in a non-counted tree by
- * going back up from where we are, but far more fiddly.)
- */
- if (relation == REL234_LT)
- idx--;
- else
- idx++;
- } else {
- /*
- * We've found our way to the bottom of the tree and we
- * know where we would insert this node if we wanted to:
- * we'd put it in in place of the (empty) subtree
- * n->kids[kcount], and it would have index idx
- *
- * But the actual element isn't there. So if our search
- * relation is EQ, we're doomed.
- */
- if (relation == REL234_EQ)
- return NULL;
-
- /*
- * Otherwise, we must do an index lookup for index idx-1
- * (if we're going left - LE or LT) or index idx (if we're
- * going right - GE or GT).
- */
- if (relation == REL234_LT || relation == REL234_LE) {
- idx--;
- }
+ if (relation == REL234_LT || relation == REL234_LE) {
+ ss.index--;
}
/*
@@ -614,10 +593,10 @@ void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp,
* to do the rest. This will return NULL if the index is out of
* bounds, which is exactly what we want.
*/
- ret = index234(t, idx);
- if (ret && index)
- *index = idx;
- return ret;
+ toret = index234(t, ss.index);
+ if (toret && index)
+ *index = ss.index;
+ return toret;
}
void *find234(tree234 * t, void *e, cmpfn234 cmp)
{
@@ -632,6 +611,80 @@ void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index)
return findrelpos234(t, e, cmp, REL234_EQ, index);
}
+void search234_start(search234_state *state, tree234 *t)
+{
+ state->_node = t->root;
+ state->_base = 0; /* index of first element in this node's subtree */
+ state->_last = -1; /* indicate that this node is not previously visted */
+ search234_step(state, 0);
+}
+void search234_step(search234_state *state, int direction)
+{
+ node234 *node = state->_node;
+ int i;
+
+ if (!node) {
+ state->element = NULL;
+ state->index = 0;
+ return;
+ }
+
+ if (state->_last != -1) {
+ /*
+ * We're already pointing at some element of a node, so we
+ * should restrict to the elements left or right of it,
+ * depending on the requested search direction.
+ */
+ assert(direction);
+ assert(node);
+
+ if (direction > 0) {
+ state->_lo = state->_last + 1;
+ direction = +1;
+ } else {
+ state->_hi = state->_last - 1;
+ direction = -1;
+ }
+
+ if (state->_lo > state->_hi) {
+ /*
+ * We've run out of elements in this node, i.e. we've
+ * narrowed to nothing but a child pointer. Descend to
+ * that child, and update _base to the leftmost index of
+ * its subtree.
+ */
+ for (i = 0; i < state->_lo; i++)
+ state->_base += 1 + node->counts[i];
+ state->_node = node = node->kids[state->_lo];
+ state->_last = -1;
+ }
+ }
+
+ if (state->_last == -1) {
+ /*
+ * We've just entered a new node - either because of the above
+ * code, or because we were called from search234_start - and
+ * anything in that node is a viable answer.
+ */
+ state->_lo = 0;
+ state->_hi = node ? elements234(node)-1 : 0;
+ }
+
+ /*
+ * Now we've got something we can return.
+ */
+ if (!node) {
+ state->element = NULL;
+ state->index = state->_base;
+ } else {
+ state->_last = (state->_lo + state->_hi) / 2;
+ state->element = node->elems[state->_last];
+ state->index = state->_base + state->_last;
+ for (i = 0; i <= state->_last; i++)
+ state->index += node->counts[i];
+ }
+}
+
/*
* Delete an element e in a 2-3-4 tree. Does not free the element,
* merely removes all links to it from the tree nodes.
@@ -1418,6 +1471,73 @@ int findtest(void)
}
}
+void searchtest_recurse(search234_state ss, int lo, int hi,
+ char **expected, char *directionbuf,
+ char *directionptr)
+{
+ *directionptr = '\0';
+
+ if (!ss.element) {
+ if (lo != hi) {
+ error("search234(%s) gave NULL for non-empty interval [%d,%d)",
+ directionbuf, lo, hi);
+ } else if (ss.index != lo) {
+ error("search234(%s) gave index %d should be %d",
+ directionbuf, ss.index, lo);
+ } else {
+ printf("%*ssearch234(%s) gave NULL,%d\n",
+ (int)(directionptr-directionbuf) * 2, "", directionbuf,
+ ss.index);
+ }
+ } else if (lo == hi) {
+ error("search234(%s) gave %s for empty interval [%d,%d)",
+ directionbuf, (char *)ss.element, lo, hi);
+ } else if (ss.element != expected[ss.index]) {
+ error("search234(%s) gave element %s should be %s",
+ directionbuf, (char *)ss.element, expected[ss.index]);
+ } else if (ss.index < lo || ss.index >= hi) {
+ error("search234(%s) gave index %d should be in [%d,%d)",
+ directionbuf, ss.index, lo, hi);
+ return;
+ } else {
+ search234_state next;
+
+ printf("%*ssearch234(%s) gave %s,%d\n",
+ (int)(directionptr-directionbuf) * 2, "", directionbuf,
+ (char *)ss.element, ss.index);
+
+ next = ss;
+ search234_step(&next, -1);
+ *directionptr = '-';
+ searchtest_recurse(next, lo, ss.index,
+ expected, directionbuf, directionptr+1);
+
+ next = ss;
+ search234_step(&next, +1);
+ *directionptr = '+';
+ searchtest_recurse(next, ss.index+1, hi,
+ expected, directionbuf, directionptr+1);
+ }
+}
+
+void searchtest(void)
+{
+ char *expected[NSTR], *p;
+ char directionbuf[NSTR * 10];
+ int n;
+ search234_state ss;
+
+ printf("beginning searchtest:");
+ for (n = 0; (p = index234(tree, n)) != NULL; n++) {
+ expected[n] = p;
+ printf(" %d=%s", n, p);
+ }
+ printf(" count=%d\n", n);
+
+ search234_start(&ss, tree);
+ searchtest_recurse(ss, 0, n, expected, directionbuf, directionbuf);
+}
+
int main(void)
{
int in[NSTR];
@@ -1432,6 +1552,7 @@ int main(void)
cmp = mycmp;
verify();
+ searchtest();
for (i = 0; i < 10000; i++) {
j = randomnumber(&seed);
j %= NSTR;
@@ -1446,6 +1567,7 @@ int main(void)
in[j] = 1;
}
findtest();
+ searchtest();
}
while (arraylen > 0) {
diff --git a/tree234.h b/tree234.h
index ba743087..55cbe360 100644
--- a/tree234.h
+++ b/tree234.h
@@ -132,6 +132,41 @@ void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index);
void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp, int relation,
int *index);
+/*
+ * A more general search type still. Use search234_start() to
+ * initialise one of these state structures; it will fill in
+ * state->element with an element of the tree, and state->index with
+ * the index of that element. If you don't like that element, call
+ * search234_step, with direction == -1 if you want an element earlier
+ * in the tree, or +1 if you want a later one.
+ *
+ * If either function returns state->element == NULL, then you've
+ * narrowed the search to a point between two adjacent elements, so
+ * there are no further elements left to return consistent with the
+ * constraints you've imposed. In this case, state->index tells you
+ * how many elements come before the point you narrowed down to. After
+ * this, you mustn't call search234_step again (unless the state
+ * structure is first reinitialised).
+ *
+ * The use of this search system is that you get both the candidate
+ * element _and_ its index at every stage, so you can use both of them
+ * to make your decision. Also, you can remember element pointers from
+ * earlier in the search.
+ *
+ * The fields beginning with underscores are private to the
+ * implementation, and only exposed so that clients can know how much
+ * space to allocate for the structure as a whole. Don't modify them.
+ * (Except that it's safe to copy the whole structure.)
+ */
+typedef struct search234_state {
+ void *element;
+ int index;
+ int _lo, _hi, _last, _base;
+ void *_node;
+} search234_state;
+void search234_start(search234_state *state, tree234 *t);
+void search234_step(search234_state *state, int direction);
+
/*
* Delete an element e in a 2-3-4 tree. Does not free the element,
* merely removes all links to it from the tree nodes.
From 61f18ac451e4e056a69f8431e66a4a371652307c Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 13:20:03 +0100
Subject: [PATCH 413/607] Reimplement alloc_channel_id using search234.
This replaces the previous log(n)^2 algorithm for channel-number
allocation, which binary-searched the space of tree indices using a
log-time call to index234() at each step, with a single log-time pass
down the tree which only has to check the returned channel number
against the returned tree index at each step.
I'm under no illusions that this was a critical performance issue, but
it's been offending my sense of algorithmic elegance for a while.
---
ssh.c | 50 ++++++++++++++++++++++----------------------------
1 file changed, 22 insertions(+), 28 deletions(-)
diff --git a/ssh.c b/ssh.c
index 9ec8553f..6e55107d 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1046,42 +1046,36 @@ static int ssh_rportcmp_ssh2(void *av, void *bv)
return 0;
}
-static int alloc_channel_id(Ssh ssh)
+static unsigned alloc_channel_id(Ssh ssh)
{
const unsigned CHANNEL_NUMBER_OFFSET = 256;
- unsigned low, high, mid;
- int tsize;
- struct ssh_channel *c;
+ search234_state ss;
/*
- * First-fit allocation of channel numbers: always pick the
- * lowest unused one. To do this, binary-search using the
- * counted B-tree to find the largest channel ID which is in a
- * contiguous sequence from the beginning. (Precisely
- * everything in that sequence must have ID equal to its tree
- * index plus CHANNEL_NUMBER_OFFSET.)
+ * First-fit allocation of channel numbers: we always pick the
+ * lowest unused one.
+ *
+ * Every channel before that, and no channel after it, has an ID
+ * exactly equal to its tree index plus CHANNEL_NUMBER_OFFSET. So
+ * we can use the search234 system to identify the length of that
+ * initial sequence, in a single log-time pass down the channels
+ * tree.
*/
- tsize = count234(ssh->channels);
-
- low = -1;
- high = tsize;
- while (high - low > 1) {
- mid = (high + low) / 2;
- c = index234(ssh->channels, mid);
- if (c->localid == mid + CHANNEL_NUMBER_OFFSET)
- low = mid; /* this one is fine */
- else
- high = mid; /* this one is past it */
+ search234_start(&ss, ssh->channels);
+ while (ss.element) {
+ struct ssh_channel *c = (struct ssh_channel *)ss.element;
+ if (c->localid == ss.index + CHANNEL_NUMBER_OFFSET)
+ search234_step(&ss, +1);
+ else
+ search234_step(&ss, -1);
}
+
/*
- * Now low points to either -1, or the tree index of the
- * largest ID in the initial sequence.
+ * Now ss.index gives exactly the number of channels in that
+ * initial sequence. So adding CHANNEL_NUMBER_OFFSET to it must
+ * give precisely the lowest unused channel number.
*/
- {
- unsigned i = low + 1 + CHANNEL_NUMBER_OFFSET;
- assert(NULL == find234(ssh->channels, &i, ssh_channelfind));
- }
- return low + 1 + CHANNEL_NUMBER_OFFSET;
+ return ss.index + CHANNEL_NUMBER_OFFSET;
}
static void c_write_stderr(int trusted, const void *vbuf, int len)
From a3130487631dbb565355b2125b766c7e286a623e Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 17:37:00 +0100
Subject: [PATCH 414/607] New utility function logevent_and_free.
This should make it easier to do formatted-string based logging
outside ssh.c, because I can wrap up a local macro in any source file
I like that expands to logevent_and_free(wherever my Frontend is,
dupprintf(macro argument)).
It caused yet another stub function to be needed in testbn, but there
we go.
(Also, while I'm here, removed a redundant declaration of logevent
itself from ssh.h. The one in putty.h is all we need.)
---
misc.c | 6 ++++++
misc.h | 7 +++++++
ssh.h | 2 --
testbn.c | 3 +++
windows/winpgen.c | 3 +++
windows/winpgnt.c | 3 +++
6 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/misc.c b/misc.c
index f4334a04..1a528923 100644
--- a/misc.c
+++ b/misc.c
@@ -337,6 +337,12 @@ void burnstr(char *string) /* sfree(str), only clear it first */
}
}
+void logevent_and_free(Frontend *frontend, char *s)
+{
+ logevent(frontend, s);
+ sfree(s);
+}
+
int toint(unsigned u)
{
/*
diff --git a/misc.h b/misc.h
index 242d1a94..40191724 100644
--- a/misc.h
+++ b/misc.h
@@ -31,6 +31,13 @@ char *dupprintf(const char *fmt, ...)
char *dupvprintf(const char *fmt, va_list ap);
void burnstr(char *string);
+/*
+ * Pass a dynamically allocated string to logevent and immediately
+ * free it. Intended for use by wrapper macros which pass the return
+ * value of dupprintf straight to this.
+ */
+void logevent_and_free(Frontend *frontend, char *msg);
+
struct strbuf {
char *s;
unsigned char *u;
diff --git a/ssh.h b/ssh.h
index a0085179..8a411710 100644
--- a/ssh.h
+++ b/ssh.h
@@ -777,8 +777,6 @@ int random_byte(void);
void random_add_noise(void *noise, int length);
void random_add_heavynoise(void *noise, int length);
-void logevent(Frontend *, const char *);
-
/* Exports from x11fwd.c */
enum {
X11_TRANS_IPV4 = 0, X11_TRANS_IPV6 = 6, X11_TRANS_UNIX = 256
diff --git a/testbn.c b/testbn.c
index 05f62767..16529e27 100644
--- a/testbn.c
+++ b/testbn.c
@@ -7,6 +7,7 @@
* testdata/bignum.py.
*/
+#include
#include
#include
#include
@@ -31,6 +32,8 @@ int random_byte(void)
return 0;
}
+void logevent(Frontend *frontend, const char *msg) { assert(0); }
+
#define fromxdigit(c) ( (c)>'9' ? ((c)&0xDF) - 'A' + 10 : (c) - '0' )
/* For Unix in particular, but harmless if this main() is reused elsewhere */
diff --git a/windows/winpgen.c b/windows/winpgen.c
index d2840113..d951fa6b 100644
--- a/windows/winpgen.c
+++ b/windows/winpgen.c
@@ -60,6 +60,9 @@ void nonfatal(const char *fmt, ...)
sfree(stuff);
}
+/* Stub needed to link against misc.c */
+void logevent(Frontend *frontend, const char *msg) { assert(0); }
+
/* ----------------------------------------------------------------------
* Progress report code. This is really horrible :-)
*/
diff --git a/windows/winpgnt.c b/windows/winpgnt.c
index 3734ab17..0f89b810 100644
--- a/windows/winpgnt.c
+++ b/windows/winpgnt.c
@@ -113,6 +113,9 @@ static void unmungestr(char *in, char *out, int outlen)
return;
}
+/* Stub needed to link against misc.c */
+void logevent(Frontend *frontend, const char *msg) { assert(0); }
+
static int has_security;
struct PassphraseProcStruct {
From ce0b672e787ff2f542ff2863b0e8cd914a929292 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 17:58:25 +0100
Subject: [PATCH 415/607] Macro to make a ptrlen out of a string literal.
I'm quite surprised I haven't needed this for anything else yet. I
suppose if I had it, I could have written most of my ptrlen_eq_strings
in terms of it, and saved a lot of gratuitous runtime strlens.
---
misc.h | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/misc.h b/misc.h
index 40191724..4e0d8b2d 100644
--- a/misc.h
+++ b/misc.h
@@ -107,6 +107,12 @@ int string_length_for_printf(size_t);
/* Derive two printf arguments from a ptrlen, suitable for "%.*s" */
#define PTRLEN_PRINTF(pl) \
string_length_for_printf((pl).len), (const char *)(pl).ptr
+/* Make a ptrlen out of a compile-time string literal. We try to
+ * enforce that it _is_ a string literal by token-pasting "" on to it,
+ * which should provoke a compile error if it's any other kind of
+ * string. */
+#define PTRLEN_LITERAL(stringlit) \
+ TYPECHECK("" stringlit "", make_ptrlen(stringlit, sizeof(stringlit)-1))
/* Wipe sensitive data out of memory that's about to be freed. Simpler
* than memset because we don't need the fill char parameter; also
From 370ff150ab21db45ade9edf37967676122b0a526 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 17:59:38 +0100
Subject: [PATCH 416/607] Move bug flag definitions out into ssh.h.
With a new shiny list-macro system that will allocate power-of-2
values for them without me having to manually keep the numbers
straight.
---
ssh.c | 17 -----------------
ssh.h | 26 ++++++++++++++++++++++++++
2 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/ssh.c b/ssh.c
index 6e55107d..7031a554 100644
--- a/ssh.c
+++ b/ssh.c
@@ -47,23 +47,6 @@ static const char *const ssh2_disconnect_reasons[] = {
"illegal user name",
};
-/*
- * Various remote-bug flags.
- */
-#define BUG_CHOKES_ON_SSH1_IGNORE 1
-#define BUG_SSH2_HMAC 2
-#define BUG_NEEDS_SSH1_PLAIN_PASSWORD 4
-#define BUG_CHOKES_ON_RSA 8
-#define BUG_SSH2_RSA_PADDING 16
-#define BUG_SSH2_DERIVEKEY 32
-#define BUG_SSH2_REKEY 64
-#define BUG_SSH2_PK_SESSIONID 128
-#define BUG_SSH2_MAXPKT 256
-#define BUG_CHOKES_ON_SSH2_IGNORE 512
-#define BUG_CHOKES_ON_WINADJ 1024
-#define BUG_SENDS_LATE_REQUEST_REPLY 2048
-#define BUG_SSH2_OLDGEX 4096
-
#define DH_MIN_SIZE 1024
#define DH_MAX_SIZE 8192
diff --git a/ssh.h b/ssh.h
index 8a411710..7dbd31fe 100644
--- a/ssh.h
+++ b/ssh.h
@@ -1266,3 +1266,29 @@ const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type);
* format.
*/
void old_keyfile_warning(void);
+
+/*
+ * Flags indicating implementation bugs that we know how to mitigate
+ * if we think the other end has them.
+ */
+#define SSH_IMPL_BUG_LIST(X) \
+ X(BUG_CHOKES_ON_SSH1_IGNORE) \
+ X(BUG_SSH2_HMAC) \
+ X(BUG_NEEDS_SSH1_PLAIN_PASSWORD) \
+ X(BUG_CHOKES_ON_RSA) \
+ X(BUG_SSH2_RSA_PADDING) \
+ X(BUG_SSH2_DERIVEKEY) \
+ X(BUG_SSH2_REKEY) \
+ X(BUG_SSH2_PK_SESSIONID) \
+ X(BUG_SSH2_MAXPKT) \
+ X(BUG_CHOKES_ON_SSH2_IGNORE) \
+ X(BUG_CHOKES_ON_WINADJ) \
+ X(BUG_SENDS_LATE_REQUEST_REPLY) \
+ X(BUG_SSH2_OLDGEX) \
+ /* end of list */
+#define TMP_DECLARE_LOG2_ENUM(thing) log2_##thing,
+enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_LOG2_ENUM) };
+#undef TMP_DECLARE_LOG2_ENUM
+#define TMP_DECLARE_REAL_ENUM(thing) thing = 1 << log2_##thing,
+enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_REAL_ENUM) };
+#undef TMP_DECLARE_REAL_ENUM
From af8e526a7dfd7c7d5cdaa3e429a9b87cbf75073a Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 17:37:00 +0100
Subject: [PATCH 417/607] Move version string exchange out into a BPP.
Getting it out of the overgrown ssh.c is worthwhile in itself! But
there are other benefits of this reorganisation too.
One is that I get to remove ssh->current_incoming_data_fn, because now
_all_ incoming network data is handled by whatever the current BPP is.
So now we only indirect through the BPP, not through some other
preliminary function pointer _and_ the BPP.
Another is that all _outgoing_ network data is now handled centrally,
including our outgoing version string - which means that a hex dump of
that string now shows up in the raw-data log file, from which it was
previously conspicuous by its absence.
---
Recipe | 2 +-
ssh.c | 650 +++++--------------------------------------------
sshbpp.h | 17 ++
sshverstring.c | 602 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 686 insertions(+), 585 deletions(-)
create mode 100644 sshverstring.c
diff --git a/Recipe b/Recipe
index b735ac47..a2728c08 100644
--- a/Recipe
+++ b/Recipe
@@ -251,7 +251,7 @@ NONSSH = telnet raw rlogin ldisc pinger
# SSH back end (putty, plink, pscp, psftp).
SSH = ssh ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
- + sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ + sshverstring sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf
+ sshgssc pgssapi sshshare sshecc aqsync marshal nullplug agentf
diff --git a/ssh.c b/ssh.c
index 7031a554..9ac60d27 100644
--- a/ssh.c
+++ b/ssh.c
@@ -661,6 +661,7 @@ enum RekeyClass {
struct ssh_tag {
char *v_c, *v_s;
+ struct ssh_version_receiver version_receiver;
ssh_hash *exhash;
Socket s;
@@ -795,7 +796,6 @@ struct ssh_tag {
BinaryPacketProtocol *bpp;
void (*general_packet_processing)(Ssh ssh, PktIn *pkt);
- void (*current_incoming_data_fn) (Ssh ssh);
void (*current_user_input_fn) (Ssh ssh);
/*
@@ -1149,26 +1149,6 @@ static void ssh_pkt_write(Ssh ssh, PktOut *pkt)
queue_idempotent_callback(&ssh->outgoing_data_sender);
}
-static int ssh_versioncmp(const char *a, const char *b)
-{
- char *ae, *be;
- unsigned long av, bv;
-
- av = strtoul(a, &ae, 10);
- bv = strtoul(b, &be, 10);
- if (av != bv)
- return (av < bv ? -1 : +1);
- if (*ae == '.')
- ae++;
- if (*be == '.')
- be++;
- av = strtoul(ae, &ae, 10);
- bv = strtoul(be, &be, 10);
- if (av != bv)
- return (av < bv ? -1 : +1);
- return 0;
-}
-
/*
* Packet construction functions. Mostly shared between SSH-1 and SSH-2.
*/
@@ -1346,258 +1326,6 @@ static void ssh2_add_sigblob(Ssh ssh, PktOut *pkt,
put_string(pkt, sigblob, sigblob_len);
}
-/*
- * Examine the remote side's version string and compare it against
- * a list of known buggy implementations.
- */
-static void ssh_detect_bugs(Ssh ssh, char *vstring)
-{
- char *imp; /* pointer to implementation part */
- imp = vstring;
- imp += strcspn(imp, "-");
- if (*imp) imp++;
- imp += strcspn(imp, "-");
- if (*imp) imp++;
-
- ssh->remote_bugs = 0;
-
- /*
- * General notes on server version strings:
- * - Not all servers reporting "Cisco-1.25" have all the bugs listed
- * here -- in particular, we've heard of one that's perfectly happy
- * with SSH1_MSG_IGNOREs -- but this string never seems to change,
- * so we can't distinguish them.
- */
- if (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == AUTO &&
- (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
- !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
- !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") ||
- !strcmp(imp, "OSU_1.4alpha3") || !strcmp(imp, "OSU_1.5alpha4")))) {
- /*
- * These versions don't support SSH1_MSG_IGNORE, so we have
- * to use a different defence against password length
- * sniffing.
- */
- ssh->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
- logevent("We believe remote version has SSH-1 ignore bug");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == AUTO &&
- (!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) {
- /*
- * These versions need a plain password sent; they can't
- * handle having a null and a random length of data after
- * the password.
- */
- ssh->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
- logevent("We believe remote version needs a plain SSH-1 password");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == AUTO &&
- (!strcmp(imp, "Cisco-1.25")))) {
- /*
- * These versions apparently have no clue whatever about
- * RSA authentication and will panic and die if they see
- * an AUTH_RSA message.
- */
- ssh->remote_bugs |= BUG_CHOKES_ON_RSA;
- logevent("We believe remote version can't handle SSH-1 RSA authentication");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == AUTO &&
- !wc_match("* VShell", imp) &&
- (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||
- wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||
- wc_match("2.1 *", imp)))) {
- /*
- * These versions have the HMAC bug.
- */
- ssh->remote_bugs |= BUG_SSH2_HMAC;
- logevent("We believe remote version has SSH-2 HMAC bug");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == AUTO &&
- !wc_match("* VShell", imp) &&
- (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {
- /*
- * These versions have the key-derivation bug (failing to
- * include the literal shared secret in the hashes that
- * generate the keys).
- */
- ssh->remote_bugs |= BUG_SSH2_DERIVEKEY;
- logevent("We believe remote version has SSH-2 key-derivation bug");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == AUTO &&
- (wc_match("OpenSSH_2.[5-9]*", imp) ||
- wc_match("OpenSSH_3.[0-2]*", imp) ||
- wc_match("mod_sftp/0.[0-8]*", imp) ||
- wc_match("mod_sftp/0.9.[0-8]", imp)))) {
- /*
- * These versions have the SSH-2 RSA padding bug.
- */
- ssh->remote_bugs |= BUG_SSH2_RSA_PADDING;
- logevent("We believe remote version has SSH-2 RSA padding bug");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == AUTO &&
- wc_match("OpenSSH_2.[0-2]*", imp))) {
- /*
- * These versions have the SSH-2 session-ID bug in
- * public-key authentication.
- */
- ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID;
- logevent("We believe remote version has SSH-2 public-key-session-ID bug");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == AUTO &&
- (wc_match("DigiSSH_2.0", imp) ||
- wc_match("OpenSSH_2.[0-4]*", imp) ||
- wc_match("OpenSSH_2.5.[0-3]*", imp) ||
- wc_match("Sun_SSH_1.0", imp) ||
- wc_match("Sun_SSH_1.0.1", imp) ||
- /* All versions <= 1.2.6 (they changed their format in 1.2.7) */
- wc_match("WeOnlyDo-*", imp)))) {
- /*
- * These versions have the SSH-2 rekey bug.
- */
- ssh->remote_bugs |= BUG_SSH2_REKEY;
- logevent("We believe remote version has SSH-2 rekey bug");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == AUTO &&
- (wc_match("1.36_sshlib GlobalSCAPE", imp) ||
- wc_match("1.36 sshlib: GlobalScape", imp)))) {
- /*
- * This version ignores our makpkt and needs to be throttled.
- */
- ssh->remote_bugs |= BUG_SSH2_MAXPKT;
- logevent("We believe remote version ignores SSH-2 maximum packet size");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_ignore2) == FORCE_ON) {
- /*
- * Servers that don't support SSH2_MSG_IGNORE. Currently,
- * none detected automatically.
- */
- ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
- logevent("We believe remote version has SSH-2 ignore bug");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_oldgex2) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_oldgex2) == AUTO &&
- (wc_match("OpenSSH_2.[235]*", imp)))) {
- /*
- * These versions only support the original (pre-RFC4419)
- * SSH-2 GEX request, and disconnect with a protocol error if
- * we use the newer version.
- */
- ssh->remote_bugs |= BUG_SSH2_OLDGEX;
- logevent("We believe remote version has outdated SSH-2 GEX");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_winadj) == FORCE_ON) {
- /*
- * Servers that don't support our winadj request for one
- * reason or another. Currently, none detected automatically.
- */
- ssh->remote_bugs |= BUG_CHOKES_ON_WINADJ;
- logevent("We believe remote version has winadj bug");
- }
-
- if (conf_get_int(ssh->conf, CONF_sshbug_chanreq) == FORCE_ON ||
- (conf_get_int(ssh->conf, CONF_sshbug_chanreq) == AUTO &&
- (wc_match("OpenSSH_[2-5].*", imp) ||
- wc_match("OpenSSH_6.[0-6]*", imp) ||
- wc_match("dropbear_0.[2-4][0-9]*", imp) ||
- wc_match("dropbear_0.5[01]*", imp)))) {
- /*
- * These versions have the SSH-2 channel request bug.
- * OpenSSH 6.7 and above do not:
- * https://bugzilla.mindrot.org/show_bug.cgi?id=1818
- * dropbear_0.52 and above do not:
- * https://secure.ucc.asn.au/hg/dropbear/rev/cd02449b709c
- */
- ssh->remote_bugs |= BUG_SENDS_LATE_REQUEST_REPLY;
- logevent("We believe remote version has SSH-2 channel request bug");
- }
-}
-
-/*
- * The `software version' part of an SSH version string is required
- * to contain no spaces or minus signs.
- */
-static void ssh_fix_verstring(char *str)
-{
- /* Eat "-". */
- while (*str && *str != '-') str++;
- assert(*str == '-'); str++;
-
- /* Convert minus signs and spaces in the remaining string into
- * underscores. */
- while (*str) {
- if (*str == '-' || *str == ' ')
- *str = '_';
- str++;
- }
-}
-
-/*
- * Send an appropriate SSH version string.
- */
-static void ssh_send_verstring(Ssh ssh, const char *protoname, char *svers)
-{
- char *verstring;
-
- if (ssh->version == 2) {
- /*
- * Construct a v2 version string.
- */
- verstring = dupprintf("%s2.0-%s\015\012", protoname, sshver);
- } else {
- /*
- * Construct a v1 version string.
- */
- assert(!strcmp(protoname, "SSH-")); /* no v1 bare connection protocol */
- verstring = dupprintf("SSH-%s-%s\012",
- (ssh_versioncmp(svers, "1.5") <= 0 ?
- svers : "1.5"),
- sshver);
- }
-
- ssh_fix_verstring(verstring + strlen(protoname));
-#ifdef FUZZING
- /* FUZZING make PuTTY insecure, so make live use difficult. */
- verstring[0] = 'I';
-#endif
-
- if (ssh->version == 2) {
- size_t len;
- /*
- * Record our version string.
- */
- len = strcspn(verstring, "\015\012");
- ssh->v_c = snewn(len + 1, char);
- memcpy(ssh->v_c, verstring, len);
- ssh->v_c[len] = 0;
- }
-
- logeventf(ssh, "We claim version: %.*s",
- strcspn(verstring, "\015\012"), verstring);
- bufchain_add(&ssh->outgoing_data, verstring, strlen(verstring));
- queue_idempotent_callback(&ssh->outgoing_data_sender);
- sfree(verstring);
-}
-
static void ssh_feed_to_bpp(Ssh ssh)
{
PacketQueueNode *prev_tail = ssh->pq_full.end.prev;
@@ -1628,342 +1356,83 @@ static void ssh_feed_to_bpp(Ssh ssh)
queue_idempotent_callback(&ssh->pq_full_consumer);
}
-static void do_ssh_init(Ssh ssh)
+static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
+ int major_version)
{
- static const char protoname[] = "SSH-";
-
- struct do_ssh_init_state {
- int crLine;
- int vslen;
- char *vstring;
- char *version;
- int vstrsize;
- int i;
- int proto1, proto2;
- };
- crState(do_ssh_init_state);
-
- crBeginState;
+ Ssh ssh = FROMFIELD(rcv, struct ssh_tag, version_receiver);
+ BinaryPacketProtocol *old_bpp;
/*
- * Search for a line beginning with the protocol name prefix in
- * the input.
+ * Queue an outgoing-data run: if the version string has been sent
+ * late rather than early, it'll still be sitting on our output
+ * raw data queue.
*/
- s->i = 0;
- while (1) {
- char prefix[sizeof(protoname)-1];
-
- /*
- * Every time round this loop, we're at the start of a new
- * line, so look for the prefix.
- */
- crMaybeWaitUntilV(
- bufchain_size(&ssh->incoming_data) >= sizeof(prefix));
- bufchain_fetch(&ssh->incoming_data, prefix, sizeof(prefix));
- if (!memcmp(prefix, protoname, sizeof(prefix))) {
- bufchain_consume(&ssh->incoming_data, sizeof(prefix));
- break;
- }
-
- /*
- * If we didn't find it, consume data until we see a newline.
- */
- while (1) {
- int len;
- void *data;
- char *nl;
-
- crMaybeWaitUntilV(bufchain_size(&ssh->incoming_data) > 0);
- bufchain_prefix(&ssh->incoming_data, &data, &len);
- if ((nl = memchr(data, '\012', len)) != NULL) {
- bufchain_consume(&ssh->incoming_data, nl - (char *)data + 1);
- break;
- } else {
- bufchain_consume(&ssh->incoming_data, len);
- }
- }
- }
-
- ssh->session_started = TRUE;
- ssh->agentfwd_enabled = FALSE;
+ queue_idempotent_callback(&ssh->outgoing_data_sender);
/*
- * Now read the rest of the greeting line.
+ * We don't support choosing a major protocol version dynamically,
+ * so this should always be the same value we set up in
+ * connect_to_host().
*/
- s->vstrsize = sizeof(protoname) + 16;
- s->vstring = snewn(s->vstrsize, char);
- strcpy(s->vstring, protoname);
- s->vslen = strlen(protoname);
- s->i = 0;
- do {
- int len;
- void *data;
- char *nl;
-
- crMaybeWaitUntilV(bufchain_size(&ssh->incoming_data) > 0);
- bufchain_prefix(&ssh->incoming_data, &data, &len);
- if ((nl = memchr(data, '\012', len)) != NULL) {
- len = nl - (char *)data + 1;
- }
-
- if (s->vslen + len >= s->vstrsize - 1) {
- s->vstrsize = (s->vslen + len) * 5 / 4 + 32;
- s->vstring = sresize(s->vstring, s->vstrsize, char);
- }
+ assert(ssh->version == major_version);
- memcpy(s->vstring + s->vslen, data, len);
- s->vslen += len;
- bufchain_consume(&ssh->incoming_data, len);
+ old_bpp = ssh->bpp;
+ ssh->remote_bugs = ssh_verstring_get_bugs(old_bpp);
- } while (s->vstring[s->vslen-1] != '\012');
+ if (!ssh->bare_connection) {
+ if (ssh->version == 2) {
+ /*
+ * Retrieve both version strings from the old BPP before
+ * we free it.
+ */
+ ssh->v_s = dupstr(ssh_verstring_get_remote(old_bpp));
+ ssh->v_c = dupstr(ssh_verstring_get_local(old_bpp));
- s->vstring[s->vslen] = 0;
- s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */
+ /*
+ * Initialise SSH-2 protocol.
+ */
+ ssh2_protocol_setup(ssh);
+ ssh->general_packet_processing = ssh2_general_packet_processing;
+ ssh->current_user_input_fn = NULL;
+ } else {
+ /*
+ * Initialise SSH-1 protocol.
+ */
+ ssh1_protocol_setup(ssh);
+ ssh->current_user_input_fn = ssh1_login_input;
+ }
- logeventf(ssh, "Server version: %s", s->vstring);
- ssh_detect_bugs(ssh, s->vstring);
+ if (ssh->version == 2)
+ queue_idempotent_callback(&ssh->ssh2_transport_icb);
- /*
- * Decide which SSH protocol version to support.
- */
- s->version = dupprintf(
- "%.*s", (int)strcspn(s->vstring + strlen(protoname), "-"),
- s->vstring + strlen(protoname));
-
- /* Anything strictly below "2.0" means protocol 1 is supported. */
- s->proto1 = ssh_versioncmp(s->version, "2.0") < 0;
- /* Anything greater or equal to "1.99" means protocol 2 is supported. */
- s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0;
-
- if (conf_get_int(ssh->conf, CONF_sshprot) == 0) {
- if (!s->proto1) {
- bombout(("SSH protocol version 1 required by our configuration "
- "but not provided by server"));
- crStopV;
- }
- } else if (conf_get_int(ssh->conf, CONF_sshprot) == 3) {
- if (!s->proto2) {
- bombout(("SSH protocol version 2 required by our configuration "
- "but server only provides (old, insecure) SSH-1"));
- crStopV;
- }
} else {
- /* No longer support values 1 or 2 for CONF_sshprot */
- assert(!"Unexpected value for CONF_sshprot");
- }
-
- if (s->proto2 && (conf_get_int(ssh->conf, CONF_sshprot) >= 2 || !s->proto1))
- ssh->version = 2;
- else
- ssh->version = 1;
-
- logeventf(ssh, "Using SSH protocol version %d", ssh->version);
+ assert(ssh->version == 2); /* can't do SSH-1 bare connection! */
+ logeventf(ssh, "Using bare ssh-connection protocol");
- /* Send the version string, if we haven't already */
- if (conf_get_int(ssh->conf, CONF_sshprot) != 3)
- ssh_send_verstring(ssh, protoname, s->version);
+ ssh2_bare_connection_protocol_setup(ssh);
+ ssh->current_user_input_fn = ssh2_connection_input;
- sfree(s->version);
-
- if (ssh->version == 2) {
- size_t len;
- /*
- * Record their version string.
- */
- len = strcspn(s->vstring, "\015\012");
- ssh->v_s = snewn(len + 1, char);
- memcpy(ssh->v_s, s->vstring, len);
- ssh->v_s[len] = 0;
-
- /*
- * Initialise SSH-2 protocol.
- */
- ssh2_protocol_setup(ssh);
- ssh->general_packet_processing = ssh2_general_packet_processing;
- ssh->current_user_input_fn = NULL;
- } else {
- /*
- * Initialise SSH-1 protocol.
- */
- ssh1_protocol_setup(ssh);
- ssh->current_user_input_fn = ssh1_login_input;
}
+
ssh->bpp->out_raw = &ssh->outgoing_data;
ssh->bpp->in_raw = &ssh->incoming_data;
ssh->bpp->in_pq = &ssh->pq_full;
ssh->bpp->pls = &ssh->pls;
ssh->bpp->logctx = ssh->logctx;
- ssh->current_incoming_data_fn = ssh_feed_to_bpp;
queue_idempotent_callback(&ssh->incoming_data_consumer);
queue_idempotent_callback(&ssh->user_input_consumer);
- if (ssh->version == 2)
- queue_idempotent_callback(&ssh->ssh2_transport_icb);
update_specials_menu(ssh->frontend);
ssh->state = SSH_STATE_BEFORE_SIZE;
ssh->pinger = pinger_new(ssh->conf, &ssh->backend);
- sfree(s->vstring);
-
- crFinishV;
-}
-
-static void do_ssh_connection_init(Ssh ssh)
-{
- /*
- * Ordinary SSH begins with the banner "SSH-x.y-...". This is just
- * the ssh-connection part, extracted and given a trivial binary
- * packet protocol, so we replace 'SSH-' at the start with a new
- * name. In proper SSH style (though of course this part of the
- * proper SSH protocol _isn't_ subject to this kind of
- * DNS-domain-based extension), we define the new name in our
- * extension space.
- */
- static const char protoname[] =
- "SSHCONNECTION@putty.projects.tartarus.org-";
-
- struct do_ssh_connection_init_state {
- int crLine;
- int vslen;
- char *vstring;
- char *version;
- int vstrsize;
- int i;
- };
- crState(do_ssh_connection_init_state);
-
- crBeginState;
-
- /*
- * Search for a line beginning with the protocol name prefix in
- * the input.
- */
- s->i = 0;
- while (1) {
- char prefix[sizeof(protoname)-1];
-
+ if (ssh->bare_connection) {
/*
- * Every time round this loop, we're at the start of a new
- * line, so look for the prefix.
+ * Get connection protocol under way.
*/
- crMaybeWaitUntilV(
- bufchain_size(&ssh->incoming_data) >= sizeof(prefix));
- bufchain_fetch(&ssh->incoming_data, prefix, sizeof(prefix));
- if (!memcmp(prefix, protoname, sizeof(prefix))) {
- bufchain_consume(&ssh->incoming_data, sizeof(prefix));
- break;
- }
-
- /*
- * If we didn't find it, consume data until we see a newline.
- */
- while (1) {
- int len;
- void *data;
- char *nl;
-
- crMaybeWaitUntilV(bufchain_size(&ssh->incoming_data) > 0);
- bufchain_prefix(&ssh->incoming_data, &data, &len);
- if ((nl = memchr(data, '\012', len)) != NULL) {
- bufchain_consume(&ssh->incoming_data, nl - (char *)data + 1);
- break;
- } else {
- bufchain_consume(&ssh->incoming_data, len);
- }
- }
- }
-
- /*
- * Now read the rest of the greeting line.
- */
- s->vstrsize = sizeof(protoname) + 16;
- s->vstring = snewn(s->vstrsize, char);
- strcpy(s->vstring, protoname);
- s->vslen = strlen(protoname);
- s->i = 0;
- do {
- int len;
- void *data;
- char *nl;
-
- crMaybeWaitUntilV(bufchain_size(&ssh->incoming_data) > 0);
- bufchain_prefix(&ssh->incoming_data, &data, &len);
- if ((nl = memchr(data, '\012', len)) != NULL) {
- len = nl - (char *)data + 1;
- }
-
- if (s->vslen + len >= s->vstrsize - 1) {
- s->vstrsize = (s->vslen + len) * 5 / 4 + 32;
- s->vstring = sresize(s->vstring, s->vstrsize, char);
- }
-
- memcpy(s->vstring + s->vslen, data, len);
- s->vslen += len;
- bufchain_consume(&ssh->incoming_data, len);
-
- } while (s->vstring[s->vslen-1] != '\012');
-
- s->vstring[s->vslen] = 0;
- s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */
-
- ssh->agentfwd_enabled = FALSE;
-
- logeventf(ssh, "Server version: %s", s->vstring);
- ssh_detect_bugs(ssh, s->vstring);
-
- /*
- * Decide which SSH protocol version to support. This is easy in
- * bare ssh-connection mode: only 2.0 is legal.
- */
- s->version = dupprintf(
- "%.*s", (int)strcspn(s->vstring + strlen(protoname), "-"),
- s->vstring + strlen(protoname));
-
- if (ssh_versioncmp(s->version, "2.0") < 0) {
- bombout(("Server announces compatibility with SSH-1 in bare ssh-connection protocol"));
- crStopV;
+ do_ssh2_connection(ssh);
}
- if (conf_get_int(ssh->conf, CONF_sshprot) == 0) {
- bombout(("Bare ssh-connection protocol cannot be run in SSH-1-only mode"));
- crStopV;
- }
-
- ssh->version = 2;
-
- logeventf(ssh, "Using bare ssh-connection protocol");
-
- /* Send the version string, if we haven't already */
- ssh_send_verstring(ssh, protoname, s->version);
-
- sfree(s->version);
-
- /*
- * Initialise bare connection protocol.
- */
- ssh2_bare_connection_protocol_setup(ssh);
- ssh->bpp->out_raw = &ssh->outgoing_data;
- ssh->bpp->in_raw = &ssh->incoming_data;
- ssh->bpp->in_pq = &ssh->pq_full;
- ssh->bpp->pls = &ssh->pls;
- ssh->bpp->logctx = ssh->logctx;
- ssh->current_incoming_data_fn = ssh_feed_to_bpp;
- queue_idempotent_callback(&ssh->incoming_data_consumer);
- ssh->current_user_input_fn = ssh2_connection_input;
- queue_idempotent_callback(&ssh->user_input_consumer);
-
- update_specials_menu(ssh->frontend);
- ssh->state = SSH_STATE_BEFORE_SIZE;
- ssh->pinger = pinger_new(ssh->conf, &ssh->backend);
-
- /*
- * Get connection protocol under way.
- */
- do_ssh2_connection(ssh);
-
- sfree(s->vstring);
-
- crFinishV;
}
static void ssh_set_frozen(Ssh ssh, int frozen)
@@ -1981,7 +1450,7 @@ static void ssh_process_incoming_data(void *ctx)
return;
if (!ssh->frozen)
- ssh->current_incoming_data_fn(ssh);
+ ssh_feed_to_bpp(ssh);
if (ssh->state == SSH_STATE_CLOSED) /* yes, check _again_ */
return;
@@ -2317,7 +1786,6 @@ static const char *connect_to_host(Ssh ssh, const char *host, int port,
* We are a downstream.
*/
ssh->bare_connection = TRUE;
- ssh->current_incoming_data_fn = do_ssh_connection_init;
ssh->fullhostname = NULL;
*realhost = dupstr(host); /* best we can do */
@@ -2332,7 +1800,6 @@ static const char *connect_to_host(Ssh ssh, const char *host, int port,
/*
* We're not a downstream, so open a normal socket.
*/
- ssh->current_incoming_data_fn = do_ssh_init;
/*
* Try to find host.
@@ -2358,20 +1825,35 @@ static const char *connect_to_host(Ssh ssh, const char *host, int port,
/*
* The SSH version number is always fixed (since we no longer support
- * fallback between versions), so set it now, and if it's SSH-2,
- * send the version string now too.
+ * fallback between versions), so set it now.
*/
sshprot = conf_get_int(ssh->conf, CONF_sshprot);
assert(sshprot == 0 || sshprot == 3);
if (sshprot == 0)
/* SSH-1 only */
ssh->version = 1;
- if (sshprot == 3 && !ssh->bare_connection) {
+ if (sshprot == 3 || ssh->bare_connection) {
/* SSH-2 only */
ssh->version = 2;
- ssh_send_verstring(ssh, "SSH-", NULL);
}
+ /*
+ * Set up the initial BPP that will do the version string
+ * exchange.
+ */
+ ssh->version_receiver.got_ssh_version = ssh_got_ssh_version;
+ ssh->bpp = ssh_verstring_new(
+ ssh->conf, ssh->frontend, ssh->bare_connection,
+ ssh->version == 1 ? "1.5" : "2.0", &ssh->version_receiver);
+ ssh->bpp->out_raw = &ssh->outgoing_data;
+ ssh->bpp->in_raw = &ssh->incoming_data;
+ /*
+ * And call its handle_input method right now, in case it wants to
+ * send the outgoing version string immediately.
+ */
+ ssh_bpp_handle_input(ssh->bpp);
+ queue_idempotent_callback(&ssh->outgoing_data_sender);
+
/*
* loghost, if configured, overrides realhost.
*/
diff --git a/sshbpp.h b/sshbpp.h
index 6e45262e..a8820cf1 100644
--- a/sshbpp.h
+++ b/sshbpp.h
@@ -50,4 +50,21 @@ void ssh2_bpp_new_incoming_crypto(
BinaryPacketProtocol *ssh2_bare_bpp_new(void);
+/*
+ * The initial code to handle the SSH version exchange is also
+ * structured as an implementation of BinaryPacketProtocol, because
+ * that makes it easy to switch from that to the next BPP once it
+ * tells us which one we're using.
+ */
+struct ssh_version_receiver {
+ void (*got_ssh_version)(struct ssh_version_receiver *rcv,
+ int major_version);
+};
+BinaryPacketProtocol *ssh_verstring_new(
+ Conf *conf, Frontend *frontend, int bare_connection_mode,
+ const char *protoversion, struct ssh_version_receiver *rcv);
+const char *ssh_verstring_get_remote(BinaryPacketProtocol *);
+const char *ssh_verstring_get_local(BinaryPacketProtocol *);
+int ssh_verstring_get_bugs(BinaryPacketProtocol *);
+
#endif /* PUTTY_SSHBPP_H */
diff --git a/sshverstring.c b/sshverstring.c
new file mode 100644
index 00000000..a9b1e46c
--- /dev/null
+++ b/sshverstring.c
@@ -0,0 +1,602 @@
+/*
+ * Code to handle the initial SSH version string exchange.
+ */
+
+#include
+#include
+#include
+
+#include "putty.h"
+#include "ssh.h"
+#include "sshbpp.h"
+#include "sshcr.h"
+
+#define PREFIX_MAXLEN 64
+
+struct ssh_verstring_state {
+ int crState;
+
+ Conf *conf;
+ Frontend *frontend;
+ ptrlen prefix_wanted;
+ char *our_protoversion;
+ struct ssh_version_receiver *receiver;
+
+ int send_early;
+
+ int found_prefix;
+ int major_protoversion;
+ int remote_bugs;
+ char prefix[PREFIX_MAXLEN];
+ char *vstring;
+ int vslen, vstrsize;
+ char *protoversion;
+ const char *softwareversion;
+
+ char *our_vstring;
+ int i;
+
+ BinaryPacketProtocol bpp;
+};
+
+static void ssh_verstring_free(BinaryPacketProtocol *bpp);
+static void ssh_verstring_handle_input(BinaryPacketProtocol *bpp);
+static PktOut *ssh_verstring_new_pktout(int type);
+static void ssh_verstring_format_packet(BinaryPacketProtocol *bpp, PktOut *);
+
+const struct BinaryPacketProtocolVtable ssh_verstring_vtable = {
+ ssh_verstring_free,
+ ssh_verstring_handle_input,
+ ssh_verstring_new_pktout,
+ ssh_verstring_format_packet,
+};
+
+static void ssh_detect_bugs(struct ssh_verstring_state *s);
+static int ssh_version_includes_v1(const char *ver);
+static int ssh_version_includes_v2(const char *ver);
+
+BinaryPacketProtocol *ssh_verstring_new(
+ Conf *conf, Frontend *frontend, int bare_connection_mode,
+ const char *protoversion, struct ssh_version_receiver *rcv)
+{
+ struct ssh_verstring_state *s = snew(struct ssh_verstring_state);
+
+ memset(s, 0, sizeof(struct ssh_verstring_state));
+
+ if (!bare_connection_mode) {
+ s->prefix_wanted = PTRLEN_LITERAL("SSH-");
+ } else {
+ /*
+ * Ordinary SSH begins with the banner "SSH-x.y-...". Here,
+ * we're going to be speaking just the ssh-connection
+ * subprotocol, extracted and given a trivial binary packet
+ * protocol, so we need a new banner.
+ *
+ * The new banner is like the ordinary SSH banner, but
+ * replaces the prefix 'SSH-' at the start with a new name. In
+ * proper SSH style (though of course this part of the proper
+ * SSH protocol _isn't_ subject to this kind of
+ * DNS-domain-based extension), we define the new name in our
+ * extension space.
+ */
+ s->prefix_wanted = PTRLEN_LITERAL(
+ "SSHCONNECTION@putty.projects.tartarus.org-");
+ }
+ assert(s->prefix_wanted.len <= PREFIX_MAXLEN);
+
+ s->conf = conf_copy(conf);
+ s->frontend = frontend;
+ s->our_protoversion = dupstr(protoversion);
+ s->receiver = rcv;
+
+ /*
+ * We send our version string early if we can. But if it includes
+ * SSH-1, we can't, because we have to take the other end into
+ * account too (see below).
+ */
+ s->send_early = !ssh_version_includes_v1(protoversion);
+
+ s->bpp.vt = &ssh_verstring_vtable;
+ return &s->bpp;
+}
+
+void ssh_verstring_free(BinaryPacketProtocol *bpp)
+{
+ struct ssh_verstring_state *s =
+ FROMFIELD(bpp, struct ssh_verstring_state, bpp);
+ conf_free(s->conf);
+ sfree(s->vstring);
+ sfree(s->protoversion);
+ sfree(s->our_vstring);
+ sfree(s->our_protoversion);
+ sfree(s);
+}
+
+static int ssh_versioncmp(const char *a, const char *b)
+{
+ char *ae, *be;
+ unsigned long av, bv;
+
+ av = strtoul(a, &ae, 10);
+ bv = strtoul(b, &be, 10);
+ if (av != bv)
+ return (av < bv ? -1 : +1);
+ if (*ae == '.')
+ ae++;
+ if (*be == '.')
+ be++;
+ av = strtoul(ae, &ae, 10);
+ bv = strtoul(be, &be, 10);
+ if (av != bv)
+ return (av < bv ? -1 : +1);
+ return 0;
+}
+
+static int ssh_version_includes_v1(const char *ver)
+{
+ return ssh_versioncmp(ver, "2.0") < 0;
+}
+
+static int ssh_version_includes_v2(const char *ver)
+{
+ return ssh_versioncmp(ver, "1.99") >= 0;
+}
+
+#define vs_logevent(printf_args) \
+ logevent_and_free(s->frontend, dupprintf printf_args)
+
+static void ssh_verstring_send(struct ssh_verstring_state *s)
+{
+ char *p;
+ int sv_pos;
+
+ /*
+ * Construct our outgoing version string.
+ */
+ s->our_vstring = dupprintf(
+ "%.*s%s-%s",
+ (int)s->prefix_wanted.len, (const char *)s->prefix_wanted.ptr,
+ s->our_protoversion, sshver);
+ sv_pos = s->prefix_wanted.len + strlen(s->our_protoversion) + 1;
+
+ /* Convert minus signs and spaces in the software version string
+ * into underscores. */
+ for (p = s->our_vstring + sv_pos; *p; p++) {
+ if (*p == '-' || *p == ' ')
+ *p = '_';
+ }
+
+#ifdef FUZZING
+ /*
+ * Replace the first character of the string with an "I" if we're
+ * compiling this code for fuzzing - i.e. the protocol prefix
+ * becomes "ISH-" instead of "SSH-".
+ *
+ * This is irrelevant to any real client software (the only thing
+ * reading the output of PuTTY built for fuzzing is the fuzzer,
+ * which can adapt to whatever it sees anyway). But it's a safety
+ * precaution making it difficult to accidentally run such a
+ * version of PuTTY (which would be hugely insecure) against a
+ * live peer implementation.
+ *
+ * (So the replacement prefix "ISH" notionally stands for
+ * 'Insecure Shell', of course.)
+ */
+ s->our_vstring[0] = 'I';
+#endif
+
+ /*
+ * Now send that version string, plus trailing \r\n or just \n
+ * (the latter in SSH-1 mode).
+ */
+ bufchain_add(s->bpp.out_raw, s->our_vstring, strlen(s->our_vstring));
+ if (ssh_version_includes_v2(s->our_protoversion))
+ bufchain_add(s->bpp.out_raw, "\015", 1);
+ bufchain_add(s->bpp.out_raw, "\012", 1);
+
+ vs_logevent(("We claim version: %s", s->our_vstring));
+}
+
+void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
+{
+ struct ssh_verstring_state *s =
+ FROMFIELD(bpp, struct ssh_verstring_state, bpp);
+
+ crBegin(s->crState);
+
+ /*
+ * If we're sending our version string up front before seeing the
+ * other side's, then do it now.
+ */
+ if (s->send_early)
+ ssh_verstring_send(s);
+
+ /*
+ * Search for a line beginning with the protocol name prefix in
+ * the input.
+ */
+ s->i = 0;
+ while (1) {
+ /*
+ * Every time round this loop, we're at the start of a new
+ * line, so look for the prefix.
+ */
+ crMaybeWaitUntilV(bufchain_size(s->bpp.in_raw) >=
+ s->prefix_wanted.len);
+ bufchain_fetch(s->bpp.in_raw, s->prefix, s->prefix_wanted.len);
+ if (!memcmp(s->prefix, s->prefix_wanted.ptr, s->prefix_wanted.len)) {
+ bufchain_consume(s->bpp.in_raw, s->prefix_wanted.len);
+ break;
+ }
+
+ /*
+ * If we didn't find it, consume data until we see a newline.
+ */
+ while (1) {
+ int len;
+ void *data;
+ char *nl;
+
+ crMaybeWaitUntilV(bufchain_size(s->bpp.in_raw) > 0);
+ bufchain_prefix(s->bpp.in_raw, &data, &len);
+ if ((nl = memchr(data, '\012', len)) != NULL) {
+ bufchain_consume(s->bpp.in_raw, nl - (char *)data + 1);
+ break;
+ } else {
+ bufchain_consume(s->bpp.in_raw, len);
+ }
+ }
+ }
+
+ s->found_prefix = TRUE;
+
+ /*
+ * Start a buffer to store the full greeting line.
+ */
+ s->vstrsize = s->prefix_wanted.len + 16;
+ s->vstring = snewn(s->vstrsize, char);
+ memcpy(s->vstring, s->prefix_wanted.ptr, s->prefix_wanted.len);
+ s->vslen = s->prefix_wanted.len;
+
+ /*
+ * Now read the rest of the greeting line.
+ */
+ s->i = 0;
+ do {
+ int len;
+ void *data;
+ char *nl;
+
+ crMaybeWaitUntilV(bufchain_size(s->bpp.in_raw) > 0);
+ bufchain_prefix(s->bpp.in_raw, &data, &len);
+ if ((nl = memchr(data, '\012', len)) != NULL) {
+ len = nl - (char *)data + 1;
+ }
+
+ if (s->vslen + len >= s->vstrsize - 1) {
+ s->vstrsize = (s->vslen + len) * 5 / 4 + 32;
+ s->vstring = sresize(s->vstring, s->vstrsize, char);
+ }
+
+ memcpy(s->vstring + s->vslen, data, len);
+ s->vslen += len;
+ bufchain_consume(s->bpp.in_raw, len);
+
+ } while (s->vstring[s->vslen-1] != '\012');
+
+ /*
+ * Trim \r and \n from the version string, and replace them with
+ * a NUL terminator.
+ */
+ while (s->vslen > 0 &&
+ (s->vstring[s->vslen-1] == '\r' ||
+ s->vstring[s->vslen-1] == '\n'))
+ s->vslen--;
+ s->vstring[s->vslen] = '\0';
+
+ vs_logevent(("Remote version: %s", s->vstring));
+
+ /*
+ * Pick out the protocol version and software version. The former
+ * goes in a separately allocated string, so that s->vstring
+ * remains intact for later use in key exchange; the latter is the
+ * tail of s->vstring, so it doesn't need to be allocated.
+ */
+ {
+ const char *pv_start = s->vstring + s->prefix_wanted.len;
+ int pv_len = strcspn(pv_start, "-");
+ s->protoversion = dupprintf("%.*s", pv_len, pv_start);
+ s->softwareversion = pv_start + pv_len;
+ if (*s->softwareversion) {
+ assert(*s->softwareversion == '-');
+ s->softwareversion++;
+ }
+ }
+
+ ssh_detect_bugs(s);
+
+ /*
+ * Figure out what actual SSH protocol version we're speaking.
+ */
+ if (ssh_version_includes_v2(s->our_protoversion) &&
+ ssh_version_includes_v2(s->protoversion)) {
+ /*
+ * We're doing SSH-2.
+ */
+ s->major_protoversion = 2;
+ } else if (ssh_version_includes_v1(s->our_protoversion) &&
+ ssh_version_includes_v1(s->protoversion)) {
+ /*
+ * We're doing SSH-1.
+ */
+ s->major_protoversion = 1;
+
+ /*
+ * There are multiple minor versions of SSH-1, and the
+ * protocol does not specify that the minimum of client
+ * and server versions is used. So we must adjust our
+ * outgoing protocol version to be no higher than that of
+ * the other side.
+ */
+ if (!s->send_early &&
+ ssh_versioncmp(s->our_protoversion, s->protoversion) > 0) {
+ sfree(s->our_protoversion);
+ s->our_protoversion = dupstr(s->protoversion);
+ }
+ } else {
+ /*
+ * Unable to agree on a major protocol version at all.
+ */
+ if (!ssh_version_includes_v2(s->our_protoversion)) {
+ s->bpp.error = dupstr(
+ "SSH protocol version 1 required by our configuration "
+ "but not provided by remote");
+ } else {
+ s->bpp.error = dupstr(
+ "SSH protocol version 2 required by our configuration "
+ "but remote only provides (old, insecure) SSH-1");
+ }
+ crStopV;
+ }
+
+ vs_logevent(("Using SSH protocol version %d", s->major_protoversion));
+
+ if (!s->send_early) {
+ /*
+ * If we didn't send our version string early, construct and
+ * send it now, because now we know what it is.
+ */
+ ssh_verstring_send(s);
+ }
+
+ /*
+ * And we're done. Notify our receiver that we now know our
+ * protocol version. This will cause it to disconnect us from the
+ * input stream and ultimately free us, because our job is now
+ * done.
+ */
+ s->receiver->got_ssh_version(s->receiver, s->major_protoversion);
+
+ crFinishV;
+}
+
+static PktOut *ssh_verstring_new_pktout(int type)
+{
+ assert(0 && "Should never try to send packets during SSH version "
+ "string exchange");
+ return NULL;
+}
+
+static void ssh_verstring_format_packet(BinaryPacketProtocol *bpp, PktOut *pkg)
+{
+ assert(0 && "Should never try to send packets during SSH version "
+ "string exchange");
+}
+
+/*
+ * Examine the remote side's version string, and compare it against a
+ * list of known buggy implementations.
+ */
+static void ssh_detect_bugs(struct ssh_verstring_state *s)
+{
+ const char *imp = s->softwareversion;
+
+ s->remote_bugs = 0;
+
+ /*
+ * General notes on server version strings:
+ * - Not all servers reporting "Cisco-1.25" have all the bugs listed
+ * here -- in particular, we've heard of one that's perfectly happy
+ * with SSH1_MSG_IGNOREs -- but this string never seems to change,
+ * so we can't distinguish them.
+ */
+ if (conf_get_int(s->conf, CONF_sshbug_ignore1) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_ignore1) == AUTO &&
+ (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
+ !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
+ !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") ||
+ !strcmp(imp, "OSU_1.4alpha3") || !strcmp(imp, "OSU_1.5alpha4")))) {
+ /*
+ * These versions don't support SSH1_MSG_IGNORE, so we have
+ * to use a different defence against password length
+ * sniffing.
+ */
+ s->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
+ vs_logevent(("We believe remote version has SSH-1 ignore bug"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_plainpw1) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_plainpw1) == AUTO &&
+ (!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) {
+ /*
+ * These versions need a plain password sent; they can't
+ * handle having a null and a random length of data after
+ * the password.
+ */
+ s->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
+ vs_logevent(("We believe remote version needs a "
+ "plain SSH-1 password"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_rsa1) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_rsa1) == AUTO &&
+ (!strcmp(imp, "Cisco-1.25")))) {
+ /*
+ * These versions apparently have no clue whatever about
+ * RSA authentication and will panic and die if they see
+ * an AUTH_RSA message.
+ */
+ s->remote_bugs |= BUG_CHOKES_ON_RSA;
+ vs_logevent(("We believe remote version can't handle SSH-1 "
+ "RSA authentication"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_hmac2) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_hmac2) == AUTO &&
+ !wc_match("* VShell", imp) &&
+ (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||
+ wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||
+ wc_match("2.1 *", imp)))) {
+ /*
+ * These versions have the HMAC bug.
+ */
+ s->remote_bugs |= BUG_SSH2_HMAC;
+ vs_logevent(("We believe remote version has SSH-2 HMAC bug"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_derivekey2) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_derivekey2) == AUTO &&
+ !wc_match("* VShell", imp) &&
+ (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {
+ /*
+ * These versions have the key-derivation bug (failing to
+ * include the literal shared secret in the hashes that
+ * generate the keys).
+ */
+ s->remote_bugs |= BUG_SSH2_DERIVEKEY;
+ vs_logevent(("We believe remote version has SSH-2 "
+ "key-derivation bug"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_rsapad2) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_rsapad2) == AUTO &&
+ (wc_match("OpenSSH_2.[5-9]*", imp) ||
+ wc_match("OpenSSH_3.[0-2]*", imp) ||
+ wc_match("mod_sftp/0.[0-8]*", imp) ||
+ wc_match("mod_sftp/0.9.[0-8]", imp)))) {
+ /*
+ * These versions have the SSH-2 RSA padding bug.
+ */
+ s->remote_bugs |= BUG_SSH2_RSA_PADDING;
+ vs_logevent(("We believe remote version has SSH-2 RSA padding bug"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_pksessid2) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_pksessid2) == AUTO &&
+ wc_match("OpenSSH_2.[0-2]*", imp))) {
+ /*
+ * These versions have the SSH-2 session-ID bug in
+ * public-key authentication.
+ */
+ s->remote_bugs |= BUG_SSH2_PK_SESSIONID;
+ vs_logevent(("We believe remote version has SSH-2 "
+ "public-key-session-ID bug"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_rekey2) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_rekey2) == AUTO &&
+ (wc_match("DigiSSH_2.0", imp) ||
+ wc_match("OpenSSH_2.[0-4]*", imp) ||
+ wc_match("OpenSSH_2.5.[0-3]*", imp) ||
+ wc_match("Sun_SSH_1.0", imp) ||
+ wc_match("Sun_SSH_1.0.1", imp) ||
+ /* All versions <= 1.2.6 (they changed their format in 1.2.7) */
+ wc_match("WeOnlyDo-*", imp)))) {
+ /*
+ * These versions have the SSH-2 rekey bug.
+ */
+ s->remote_bugs |= BUG_SSH2_REKEY;
+ vs_logevent(("We believe remote version has SSH-2 rekey bug"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_maxpkt2) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_maxpkt2) == AUTO &&
+ (wc_match("1.36_sshlib GlobalSCAPE", imp) ||
+ wc_match("1.36 sshlib: GlobalScape", imp)))) {
+ /*
+ * This version ignores our makpkt and needs to be throttled.
+ */
+ s->remote_bugs |= BUG_SSH2_MAXPKT;
+ vs_logevent(("We believe remote version ignores SSH-2 "
+ "maximum packet size"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_ignore2) == FORCE_ON) {
+ /*
+ * Servers that don't support SSH2_MSG_IGNORE. Currently,
+ * none detected automatically.
+ */
+ s->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
+ vs_logevent(("We believe remote version has SSH-2 ignore bug"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_oldgex2) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_oldgex2) == AUTO &&
+ (wc_match("OpenSSH_2.[235]*", imp)))) {
+ /*
+ * These versions only support the original (pre-RFC4419)
+ * SSH-2 GEX request, and disconnect with a protocol error if
+ * we use the newer version.
+ */
+ s->remote_bugs |= BUG_SSH2_OLDGEX;
+ vs_logevent(("We believe remote version has outdated SSH-2 GEX"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_winadj) == FORCE_ON) {
+ /*
+ * Servers that don't support our winadj request for one
+ * reason or another. Currently, none detected automatically.
+ */
+ s->remote_bugs |= BUG_CHOKES_ON_WINADJ;
+ vs_logevent(("We believe remote version has winadj bug"));
+ }
+
+ if (conf_get_int(s->conf, CONF_sshbug_chanreq) == FORCE_ON ||
+ (conf_get_int(s->conf, CONF_sshbug_chanreq) == AUTO &&
+ (wc_match("OpenSSH_[2-5].*", imp) ||
+ wc_match("OpenSSH_6.[0-6]*", imp) ||
+ wc_match("dropbear_0.[2-4][0-9]*", imp) ||
+ wc_match("dropbear_0.5[01]*", imp)))) {
+ /*
+ * These versions have the SSH-2 channel request bug.
+ * OpenSSH 6.7 and above do not:
+ * https://bugzilla.mindrot.org/show_bug.cgi?id=1818
+ * dropbear_0.52 and above do not:
+ * https://secure.ucc.asn.au/hg/dropbear/rev/cd02449b709c
+ */
+ s->remote_bugs |= BUG_SENDS_LATE_REQUEST_REPLY;
+ vs_logevent(("We believe remote version has SSH-2 "
+ "channel request bug"));
+ }
+}
+
+const char *ssh_verstring_get_remote(BinaryPacketProtocol *bpp)
+{
+ struct ssh_verstring_state *s =
+ FROMFIELD(bpp, struct ssh_verstring_state, bpp);
+ return s->vstring;
+}
+
+const char *ssh_verstring_get_local(BinaryPacketProtocol *bpp)
+{
+ struct ssh_verstring_state *s =
+ FROMFIELD(bpp, struct ssh_verstring_state, bpp);
+ return s->our_vstring;
+}
+
+int ssh_verstring_get_bugs(BinaryPacketProtocol *bpp)
+{
+ struct ssh_verstring_state *s =
+ FROMFIELD(bpp, struct ssh_verstring_state, bpp);
+ return s->remote_bugs;
+}
From 63a14f26f78097c3ee5aa8ea8160efbe3c62b5cd Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 18:22:36 +0100
Subject: [PATCH 418/607] Rework handling of untrusted terminal data.
Now there's a centralised routine in misc.c to do the sanitisation,
which copies data on to an outgoing bufchain. This allows me to remove
from_backend_untrusted() completely from the frontend API, simplifying
code in several places.
Two use cases for untrusted-terminal-data sanitisation were in the
terminal.c prompts handler, and in the collection of SSH-2 userauth
banners. Both of those were writing output to a bufchain anyway, so
it was very convenient to just replace a bufchain_add with
sanitise_term_data and then not have to worry about it again.
There was also a simplistic sanitiser in uxcons.c, which I've now
replaced with a call to the good one - and in wincons.c there was a
FIXME saying I ought to get round to that, which now I have!
---
misc.c | 27 +++++++++++++++++++++++++++
misc.h | 2 ++
pscp.c | 9 ---------
psftp.c | 9 ---------
putty.h | 2 --
ssh.c | 16 ++++++----------
terminal.c | 29 ++++++++++-------------------
unix/gtkwin.c | 5 -----
unix/uxcons.c | 15 ++++++++++-----
unix/uxplink.c | 10 ----------
windows/wincons.c | 12 ++++++++++--
windows/window.c | 5 -----
windows/winplink.c | 10 ----------
13 files changed, 65 insertions(+), 86 deletions(-)
diff --git a/misc.c b/misc.c
index 1a528923..42a4a228 100644
--- a/misc.c
+++ b/misc.c
@@ -826,6 +826,33 @@ int bufchain_try_fetch_consume(bufchain *ch, void *data, int len)
}
}
+/* ----------------------------------------------------------------------
+ * Sanitise terminal output that we have reason not to trust, e.g.
+ * because it appears in the login banner or password prompt from a
+ * server, which we'd rather not permit to use arbitrary escape
+ * sequences.
+ */
+
+void sanitise_term_data(bufchain *out, const void *vdata, int len)
+{
+ const char *data = (const char *)vdata;
+ int i;
+
+ /*
+ * FIXME: this method of sanitisation is ASCII-centric. It would
+ * be nice to permit SSH banners and the like to contain printable
+ * Unicode, but that would need a lot more complicated code here
+ * (not to mention knowing what character set it should interpret
+ * the data as).
+ */
+ for (i = 0; i < len; i++) {
+ if (data[i] == '\n')
+ bufchain_add(out, "\r\n", 2);
+ else if (data[i] >= ' ' && data[i] < 0x7F)
+ bufchain_add(out, data + i, 1);
+ }
+}
+
/* ----------------------------------------------------------------------
* My own versions of malloc, realloc and free. Because I want
* malloc and realloc to bomb out and exit the program if they run
diff --git a/misc.h b/misc.h
index 4e0d8b2d..b63df049 100644
--- a/misc.h
+++ b/misc.h
@@ -90,6 +90,8 @@ void bufchain_fetch(bufchain *ch, void *data, int len);
void bufchain_fetch_consume(bufchain *ch, void *data, int len);
int bufchain_try_fetch_consume(bufchain *ch, void *data, int len);
+void sanitise_term_data(bufchain *out, const void *vdata, int len);
+
int validate_manual_hostkey(char *key);
struct tm ltime(void);
diff --git a/pscp.c b/pscp.c
index 594f119f..67029f94 100644
--- a/pscp.c
+++ b/pscp.c
@@ -196,15 +196,6 @@ int from_backend(Frontend *frontend, int is_stderr,
return 0;
}
-int from_backend_untrusted(Frontend *frontend, const void *data, int len)
-{
- /*
- * No "untrusted" output should get here (the way the code is
- * currently, it's all diverted by FLAG_STDERR).
- */
- assert(!"Unexpected call to from_backend_untrusted()");
- return 0; /* not reached */
-}
int from_backend_eof(Frontend *frontend)
{
/*
diff --git a/psftp.c b/psftp.c
index d57b118e..4eb4068d 100644
--- a/psftp.c
+++ b/psftp.c
@@ -2551,15 +2551,6 @@ int from_backend(Frontend *frontend, int is_stderr,
return 0;
}
-int from_backend_untrusted(Frontend *frontend, const void *data, int len)
-{
- /*
- * No "untrusted" output should get here (the way the code is
- * currently, it's all diverted by FLAG_STDERR).
- */
- assert(!"Unexpected call to from_backend_untrusted()");
- return 0; /* not reached */
-}
int from_backend_eof(Frontend *frontend)
{
/*
diff --git a/putty.h b/putty.h
index dd43e8c4..5bb7974f 100644
--- a/putty.h
+++ b/putty.h
@@ -712,7 +712,6 @@ void frontend_echoedit_update(Frontend *frontend, int echo, int edit);
* shutdown. */
void update_specials_menu(Frontend *frontend);
int from_backend(Frontend *frontend, int is_stderr, const void *data, int len);
-int from_backend_untrusted(Frontend *frontend, const void *data, int len);
/* Called when the back end wants to indicate that EOF has arrived on
* the server-to-client stream. Returns FALSE to indicate that we
* intend to keep the session open in the other direction, or TRUE to
@@ -1130,7 +1129,6 @@ void term_request_copy(Terminal *, const int *clipboards, int n_clipboards);
void term_request_paste(Terminal *, int clipboard);
void term_seen_key_event(Terminal *);
int term_data(Terminal *, int is_stderr, const void *data, int len);
-int term_data_untrusted(Terminal *, const void *data, int len);
void term_provide_backend(Terminal *term, Backend *backend);
void term_provide_logctx(Terminal *term, LogContext *logctx);
void term_set_focus(Terminal *term, int has_focus);
diff --git a/ssh.c b/ssh.c
index 9ac60d27..c8365cae 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1078,14 +1078,6 @@ static void c_write(Ssh ssh, const void *buf, int len)
from_backend(ssh->frontend, 1, buf, len);
}
-static void c_write_untrusted(Ssh ssh, const void *buf, int len)
-{
- if (flags & FLAG_STDERR)
- c_write_stderr(0, buf, len);
- else
- from_backend_untrusted(ssh->frontend, buf, len);
-}
-
static void c_write_str(Ssh ssh, const char *buf)
{
c_write(ssh, buf, strlen(buf));
@@ -7066,7 +7058,7 @@ static void ssh2_msg_userauth_banner(Ssh ssh, PktIn *pktin)
bufchain_size(&ssh->banner) <= 131072) {
ptrlen banner = get_string(pktin);
if (banner.len)
- bufchain_add(&ssh->banner, banner.ptr, banner.len);
+ sanitise_term_data(&ssh->banner, banner.ptr, banner.len);
}
}
@@ -7679,11 +7671,15 @@ static void do_ssh2_userauth(void *vctx)
* banner _anyway_, and moreover the printing of
* the banner will screw up processing on the
* output of (say) plink.)
+ *
+ * The banner data has been sanitised already by this
+ * point, so we can safely send it straight to the
+ * output channel.
*/
if (size && (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) {
char *banner = snewn(size, char);
bufchain_fetch(&ssh->banner, banner, size);
- c_write_untrusted(ssh, banner, size);
+ c_write(ssh, banner, size);
sfree(banner);
}
bufchain_clear(&ssh->banner);
diff --git a/terminal.c b/terminal.c
index a8bd6fc7..621d81de 100644
--- a/terminal.c
+++ b/terminal.c
@@ -6624,10 +6624,8 @@ int term_ldisc(Terminal *term, int option)
return FALSE;
}
-int term_data(Terminal *term, int is_stderr, const void *data, int len)
+static void term_added_data(Terminal *term)
{
- bufchain_add(&term->inbuf, data, len);
-
if (!term->in_term_out) {
term->in_term_out = TRUE;
term_reset_cblink(term);
@@ -6640,6 +6638,12 @@ int term_data(Terminal *term, int is_stderr, const void *data, int len)
term_out(term);
term->in_term_out = FALSE;
}
+}
+
+int term_data(Terminal *term, int is_stderr, const void *data, int len)
+{
+ bufchain_add(&term->inbuf, data, len);
+ term_added_data(term);
/*
* term_out() always completely empties inbuf. Therefore,
@@ -6663,23 +6667,10 @@ int term_data(Terminal *term, int is_stderr, const void *data, int len)
return 0;
}
-/*
- * Write untrusted data to the terminal.
- * The only control character that should be honoured is \n (which
- * will behave as a CRLF).
- */
-int term_data_untrusted(Terminal *term, const void *vdata, int len)
+static void term_data_untrusted(Terminal *term, const void *data, int len)
{
- const char *data = (const char *)vdata;
- int i;
- /* FIXME: more sophisticated checking? */
- for (i = 0; i < len; i++) {
- if (data[i] == '\n')
- term_data(term, 1, "\r\n", 2);
- else if (data[i] & 0x60)
- term_data(term, 1, data + i, 1);
- }
- return 0; /* assumes that term_data() always returns 0 */
+ sanitise_term_data(&term->inbuf, data, len);
+ term_added_data(term);
}
void term_provide_logctx(Terminal *term, LogContext *logctx)
diff --git a/unix/gtkwin.c b/unix/gtkwin.c
index 46b62782..eb3f201f 100644
--- a/unix/gtkwin.c
+++ b/unix/gtkwin.c
@@ -305,11 +305,6 @@ int from_backend(Frontend *inst, int is_stderr, const void *data, int len)
return term_data(inst->term, is_stderr, data, len);
}
-int from_backend_untrusted(Frontend *inst, const void *data, int len)
-{
- return term_data_untrusted(inst->term, data, len);
-}
-
int from_backend_eof(Frontend *inst)
{
return TRUE; /* do respond to incoming EOF with outgoing */
diff --git a/unix/uxcons.c b/unix/uxcons.c
index b4fecce1..cf3603a4 100644
--- a/unix/uxcons.c
+++ b/unix/uxcons.c
@@ -448,11 +448,16 @@ static void console_close(FILE *outfp, int infd)
static void console_prompt_text(FILE *outfp, const char *data, int len)
{
- int i;
-
- for (i = 0; i < len; i++)
- if ((data[i] & 0x60) || (data[i] == '\n'))
- fputc(data[i], outfp);
+ bufchain sanitised;
+ void *vdata;
+
+ bufchain_init(&sanitised);
+ sanitise_term_data(&sanitised, data, len);
+ while (bufchain_size(&sanitised) > 0) {
+ bufchain_prefix(&sanitised, &vdata, &len);
+ fwrite(vdata, 1, len, outfp);
+ bufchain_consume(&sanitised, len);
+ }
fflush(outfp);
}
diff --git a/unix/uxplink.c b/unix/uxplink.c
index 56bdf533..b058dcd0 100644
--- a/unix/uxplink.c
+++ b/unix/uxplink.c
@@ -413,16 +413,6 @@ int from_backend(Frontend *frontend, int is_stderr,
}
}
-int from_backend_untrusted(Frontend *frontend, const void *data, int len)
-{
- /*
- * No "untrusted" output should get here (the way the code is
- * currently, it's all diverted by FLAG_STDERR).
- */
- assert(!"Unexpected call to from_backend_untrusted()");
- return 0; /* not reached */
-}
-
int from_backend_eof(Frontend *frontend)
{
assert(outgoingeof == EOF_NO);
diff --git a/windows/wincons.c b/windows/wincons.c
index 10367be8..34ed6a30 100644
--- a/windows/wincons.c
+++ b/windows/wincons.c
@@ -348,8 +348,16 @@ void logevent(Frontend *frontend, const char *string)
static void console_data_untrusted(HANDLE hout, const char *data, int len)
{
DWORD dummy;
- /* FIXME: control-character filtering */
- WriteFile(hout, data, len, &dummy, NULL);
+ bufchain sanitised;
+ void *vdata;
+
+ bufchain_init(&sanitised);
+ sanitise_term_data(&sanitised, data, len);
+ while (bufchain_size(&sanitised) > 0) {
+ bufchain_prefix(&sanitised, &vdata, &len);
+ WriteFile(hout, vdata, len, &dummy, NULL);
+ bufchain_consume(&sanitised, len);
+ }
}
int console_get_userpass_input(prompts_t *p)
diff --git a/windows/window.c b/windows/window.c
index eb0f5e01..b73de782 100644
--- a/windows/window.c
+++ b/windows/window.c
@@ -5922,11 +5922,6 @@ int from_backend(Frontend *frontend, int is_stderr, const void *data, int len)
return term_data(term, is_stderr, data, len);
}
-int from_backend_untrusted(Frontend *frontend, const void *data, int len)
-{
- return term_data_untrusted(term, data, len);
-}
-
int from_backend_eof(Frontend *frontend)
{
return TRUE; /* do respond to incoming EOF with outgoing */
diff --git a/windows/winplink.c b/windows/winplink.c
index c156d740..c647a65a 100644
--- a/windows/winplink.c
+++ b/windows/winplink.c
@@ -114,16 +114,6 @@ int from_backend(Frontend *frontend, int is_stderr,
return handle_backlog(stdout_handle) + handle_backlog(stderr_handle);
}
-int from_backend_untrusted(Frontend *frontend, const void *data, int len)
-{
- /*
- * No "untrusted" output should get here (the way the code is
- * currently, it's all diverted by FLAG_STDERR).
- */
- assert(!"Unexpected call to from_backend_untrusted()");
- return 0; /* not reached */
-}
-
int from_backend_eof(Frontend *frontend)
{
handle_write_eof(stdout_handle);
From 6e24b7d5898b1f3d9b5a1e4f0eb71632d182bb9c Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 21:56:11 +0100
Subject: [PATCH 419/607] Extend PacketQueue to take PktOut as well.
Some upcoming restructuring I've got planned will need to pass output
packets back and forth on queues, as well as input ones. So here's a
change that arranges that we can have a PktInQueue and a PktOutQueue,
sharing most of their implementation via a PacketQueueBase structure
which links together the PacketQueueNode fields in the two packet
structures.
There's a tricksy bit of macro manoeuvring to get all of this
type-checked, so that I can't accidentally link a PktOut on to a
PktInQueue or vice versa. It works by having the main queue functions
wrapped by macros; when receiving a packet structure on input, they
type-check it against the queue structure and then automatically look
up its qnode field to pass to the underlying PacketQueueBase function;
on output, they translate a returned PacketQueueNode back to its
containing packet type by calling a 'get' function pointer.
---
ssh.c | 146 ++++++++++++++++++++++++++++---------------------------
ssh.h | 46 ++++++++++++++----
sshbpp.h | 2 +-
3 files changed, 111 insertions(+), 83 deletions(-)
diff --git a/ssh.c b/ssh.c
index c8365cae..b38a281b 100644
--- a/ssh.c
+++ b/ssh.c
@@ -547,61 +547,83 @@ static PktOut *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
#endif
static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin);
-void pq_init(struct PacketQueue *pq)
+void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node)
{
- pq->end.next = pq->end.prev = &pq->end;
-}
-
-void pq_push(struct PacketQueue *pq, PktIn *pkt)
-{
- PacketQueueNode *node = &pkt->qnode;
assert(!node->next);
assert(!node->prev);
- node->next = &pq->end;
- node->prev = pq->end.prev;
+ node->next = &pqb->end;
+ node->prev = pqb->end.prev;
node->next->prev = node;
node->prev->next = node;
}
-void pq_push_front(struct PacketQueue *pq, PktIn *pkt)
+void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node)
{
- PacketQueueNode *node = &pkt->qnode;
assert(!node->next);
assert(!node->prev);
- node->prev = &pq->end;
- node->next = pq->end.next;
+ node->prev = &pqb->end;
+ node->next = pqb->end.next;
node->next->prev = node;
node->prev->next = node;
}
-PktIn *pq_peek(struct PacketQueue *pq)
+static PktIn *pq_in_get(PacketQueueBase *pqb, int pop)
{
- if (pq->end.next == &pq->end)
+ PacketQueueNode *node = pqb->end.next;
+ if (node == &pqb->end)
return NULL;
- return FROMFIELD(pq->end.next, PktIn, qnode);
+
+ if (pop) {
+ node->next->prev = node->prev;
+ node->prev->next = node->next;
+ node->prev = node->next = NULL;
+ }
+
+ return FROMFIELD(node, PktIn, qnode);
}
-PktIn *pq_pop(struct PacketQueue *pq)
+static PktOut *pq_out_get(PacketQueueBase *pqb, int pop)
{
- PacketQueueNode *node = pq->end.next;
- if (node == &pq->end)
+ PacketQueueNode *node = pqb->end.next;
+ if (node == &pqb->end)
return NULL;
- node->next->prev = node->prev;
- node->prev->next = node->next;
- node->prev = node->next = NULL;
+ if (pop) {
+ node->next->prev = node->prev;
+ node->prev->next = node->next;
+ node->prev = node->next = NULL;
+ }
+
+ return FROMFIELD(node, PktOut, qnode);
+}
- return FROMFIELD(node, PktIn, qnode);
+void pq_in_init(PktInQueue *pq)
+{
+ pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
+ pq->get = pq_in_get;
}
-void pq_clear(struct PacketQueue *pq)
+void pq_out_init(PktOutQueue *pq)
+{
+ pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
+ pq->get = pq_out_get;
+}
+
+void pq_in_clear(PktInQueue *pq)
{
PktIn *pkt;
while ((pkt = pq_pop(pq)) != NULL)
ssh_unref_packet(pkt);
}
-int pq_empty_on_to_front_of(struct PacketQueue *src, struct PacketQueue *dest)
+void pq_out_clear(PktOutQueue *pq)
+{
+ PktOut *pkt;
+ while ((pkt = pq_pop(pq)) != NULL)
+ ssh_free_pktout(pkt);
+}
+
+int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest)
{
struct PacketQueueNode *srcfirst, *srclast;
@@ -727,8 +749,7 @@ struct ssh_tag {
int sent_console_eof;
int got_pty; /* affects EOF behaviour on main channel */
- PktOut **queue;
- int queuelen, queuesize;
+ PktOutQueue outq;
int queueing;
/*
@@ -765,22 +786,22 @@ struct ssh_tag {
int incoming_data_seen_eof;
char *incoming_data_eof_message;
- struct PacketQueue pq_full;
+ PktInQueue pq_full;
struct IdempotentCallback pq_full_consumer;
- struct PacketQueue pq_ssh1_login;
+ PktInQueue pq_ssh1_login;
struct IdempotentCallback ssh1_login_icb;
- struct PacketQueue pq_ssh1_connection;
+ PktInQueue pq_ssh1_connection;
struct IdempotentCallback ssh1_connection_icb;
- struct PacketQueue pq_ssh2_transport;
+ PktInQueue pq_ssh2_transport;
struct IdempotentCallback ssh2_transport_icb;
- struct PacketQueue pq_ssh2_userauth;
+ PktInQueue pq_ssh2_userauth;
struct IdempotentCallback ssh2_userauth_icb;
- struct PacketQueue pq_ssh2_connection;
+ PktInQueue pq_ssh2_connection;
struct IdempotentCallback ssh2_connection_icb;
bufchain user_input;
@@ -1165,21 +1186,6 @@ static void ssh_pkt_BinarySink_write(BinarySink *bs,
ssh_pkt_adddata(pkt, data, len);
}
-/*
- * Queue an SSH-2 packet.
- */
-static void ssh2_pkt_queue(Ssh ssh, PktOut *pkt)
-{
- assert(ssh->queueing);
-
- if (ssh->queuelen >= ssh->queuesize) {
- ssh->queuesize = ssh->queuelen + 32;
- ssh->queue = sresize(ssh->queue, ssh->queuesize, PktOut *);
- }
-
- ssh->queue[ssh->queuelen++] = pkt;
-}
-
/*
* Either queue or send a packet, depending on whether queueing is
* set.
@@ -1187,7 +1193,7 @@ static void ssh2_pkt_queue(Ssh ssh, PktOut *pkt)
static void ssh2_pkt_send(Ssh ssh, PktOut *pkt)
{
if (ssh->queueing) {
- ssh2_pkt_queue(ssh, pkt);
+ pq_push(&ssh->outq, pkt);
} else {
ssh_pkt_write(ssh, pkt);
}
@@ -1226,13 +1232,12 @@ static void ssh_send_outgoing_data(void *ctx)
*/
static void ssh2_pkt_queuesend(Ssh ssh)
{
- int i;
+ PktOut *pkt;
assert(!ssh->queueing);
- for (i = 0; i < ssh->queuelen; i++)
- ssh_pkt_write(ssh, ssh->queue[i]);
- ssh->queuelen = 0;
+ while ((pkt = pq_pop(&ssh->outq)) != NULL)
+ ssh_pkt_write(ssh, pkt);
}
#if 0
@@ -1320,7 +1325,7 @@ static void ssh2_add_sigblob(Ssh ssh, PktOut *pkt,
static void ssh_feed_to_bpp(Ssh ssh)
{
- PacketQueueNode *prev_tail = ssh->pq_full.end.prev;
+ PacketQueueNode *prev_tail = ssh->pq_full.pqb.end.prev;
assert(ssh->bpp);
ssh_bpp_handle_input(ssh->bpp);
@@ -1344,7 +1349,7 @@ static void ssh_feed_to_bpp(Ssh ssh)
ssh->disconnect_message_seen = TRUE;
}
- if (ssh->pq_full.end.prev != prev_tail)
+ if (ssh->pq_full.pqb.end.prev != prev_tail)
queue_idempotent_callback(&ssh->pq_full_consumer);
}
@@ -9731,27 +9736,27 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
ssh->incoming_data_consumer.fn = ssh_process_incoming_data;
ssh->incoming_data_consumer.ctx = ssh;
ssh->incoming_data_consumer.queued = FALSE;
- pq_init(&ssh->pq_full);
+ pq_in_init(&ssh->pq_full);
ssh->pq_full_consumer.fn = ssh_process_pq_full;
ssh->pq_full_consumer.ctx = ssh;
ssh->pq_full_consumer.queued = FALSE;
- pq_init(&ssh->pq_ssh1_login);
+ pq_in_init(&ssh->pq_ssh1_login);
ssh->ssh1_login_icb.fn = do_ssh1_login;
ssh->ssh1_login_icb.ctx = ssh;
ssh->ssh1_login_icb.queued = FALSE;
- pq_init(&ssh->pq_ssh1_connection);
+ pq_in_init(&ssh->pq_ssh1_connection);
ssh->ssh1_connection_icb.fn = do_ssh1_connection;
ssh->ssh1_connection_icb.ctx = ssh;
ssh->ssh1_connection_icb.queued = FALSE;
- pq_init(&ssh->pq_ssh2_transport);
+ pq_in_init(&ssh->pq_ssh2_transport);
ssh->ssh2_transport_icb.fn = do_ssh2_transport;
ssh->ssh2_transport_icb.ctx = ssh;
ssh->ssh2_transport_icb.queued = FALSE;
- pq_init(&ssh->pq_ssh2_userauth);
+ pq_in_init(&ssh->pq_ssh2_userauth);
ssh->ssh2_userauth_icb.fn = do_ssh2_userauth;
ssh->ssh2_userauth_icb.ctx = ssh;
ssh->ssh2_userauth_icb.queued = FALSE;
- pq_init(&ssh->pq_ssh2_connection);
+ pq_in_init(&ssh->pq_ssh2_connection);
ssh->ssh2_connection_icb.fn = do_ssh2_connection;
ssh->ssh2_connection_icb.ctx = ssh;
ssh->ssh2_connection_icb.queued = FALSE;
@@ -9771,8 +9776,7 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
ssh->mainchan = NULL;
ssh->throttled_all = 0;
ssh->v1_stdout_throttling = 0;
- ssh->queue = NULL;
- ssh->queuelen = ssh->queuesize = 0;
+ pq_out_init(&ssh->outq);
ssh->queueing = FALSE;
ssh->qhead = ssh->qtail = NULL;
ssh->deferred_rekey_reason = NULL;
@@ -9862,9 +9866,7 @@ static void ssh_free(Backend *be)
dh_cleanup(ssh->dh_ctx);
sfree(ssh->savedhost);
- while (ssh->queuelen-- > 0)
- ssh_free_pktout(ssh->queue[ssh->queuelen]);
- sfree(ssh->queue);
+ pq_out_clear(&ssh->outq);
while (ssh->qhead) {
struct queued_handler *qh = ssh->qhead;
@@ -9920,12 +9922,12 @@ static void ssh_free(Backend *be)
bufchain_clear(&ssh->incoming_data);
bufchain_clear(&ssh->outgoing_data);
sfree(ssh->incoming_data_eof_message);
- pq_clear(&ssh->pq_full);
- pq_clear(&ssh->pq_ssh1_login);
- pq_clear(&ssh->pq_ssh1_connection);
- pq_clear(&ssh->pq_ssh2_transport);
- pq_clear(&ssh->pq_ssh2_userauth);
- pq_clear(&ssh->pq_ssh2_connection);
+ pq_in_clear(&ssh->pq_full);
+ pq_in_clear(&ssh->pq_ssh1_login);
+ pq_in_clear(&ssh->pq_ssh1_connection);
+ pq_in_clear(&ssh->pq_ssh2_transport);
+ pq_in_clear(&ssh->pq_ssh2_userauth);
+ pq_in_clear(&ssh->pq_ssh2_connection);
bufchain_clear(&ssh->user_input);
sfree(ssh->v_c);
sfree(ssh->v_s);
diff --git a/ssh.h b/ssh.h
index 7dbd31fe..7ed45101 100644
--- a/ssh.h
+++ b/ssh.h
@@ -81,20 +81,46 @@ typedef struct PktOut {
unsigned downstream_id;
const char *additional_log_text;
+ PacketQueueNode qnode; /* for linking this packet on to a queue */
BinarySink_IMPLEMENTATION;
} PktOut;
-typedef struct PacketQueue {
+typedef struct PacketQueueBase {
PacketQueueNode end;
-} PacketQueue;
-
-void pq_init(struct PacketQueue *pq);
-void pq_push(struct PacketQueue *pq, PktIn *pkt);
-void pq_push_front(struct PacketQueue *pq, PktIn *pkt);
-PktIn *pq_peek(struct PacketQueue *pq);
-PktIn *pq_pop(struct PacketQueue *pq);
-void pq_clear(struct PacketQueue *pq);
-int pq_empty_on_to_front_of(struct PacketQueue *src, struct PacketQueue *dest);
+} PacketQueueBase;
+
+typedef struct PktInQueue {
+ PacketQueueBase pqb;
+ PktIn *(*get)(PacketQueueBase *, int pop);
+} PktInQueue;
+
+typedef struct PktOutQueue {
+ PacketQueueBase pqb;
+ PktOut *(*get)(PacketQueueBase *, int pop);
+} PktOutQueue;
+
+void pq_base_init(PacketQueueBase *pqb);
+void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node);
+void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node);
+int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest);
+
+void pq_in_init(PktInQueue *pq);
+void pq_out_init(PktOutQueue *pq);
+void pq_in_clear(PktInQueue *pq);
+void pq_out_clear(PktOutQueue *pq);
+
+#define pq_push(pq, pkt) \
+ TYPECHECK((pq)->get(&(pq)->pqb, FALSE) == pkt, \
+ pq_base_push(&(pq)->pqb, &(pkt)->qnode))
+#define pq_push_front(pq, pkt) \
+ TYPECHECK((pq)->get(&(pq)->pqb, FALSE) == pkt, \
+ pq_base_push_front(&(pq)->pqb, &(pkt)->qnode))
+#define pq_peek(pq) ((pq)->get(&(pq)->pqb, FALSE))
+#define pq_pop(pq) ((pq)->get(&(pq)->pqb, TRUE))
+#define pq_empty_on_to_front_of(src, dst) \
+ TYPECHECK((src)->get(&(src)->pqb, FALSE) == \
+ (dst)->get(&(dst)->pqb, FALSE), \
+ pq_base_empty_on_to_front_of(&(src)->pqb, &(dst)->pqb))
/*
* Packet type contexts, so that ssh2_pkt_type can correctly decode
diff --git a/sshbpp.h b/sshbpp.h
index a8820cf1..953add3a 100644
--- a/sshbpp.h
+++ b/sshbpp.h
@@ -17,7 +17,7 @@ struct BinaryPacketProtocolVtable {
struct BinaryPacketProtocol {
const struct BinaryPacketProtocolVtable *vt;
bufchain *in_raw, *out_raw;
- PacketQueue *in_pq;
+ PktInQueue *in_pq;
PacketLogSettings *pls;
LogContext *logctx;
From 242c074646b250682a5d41540ab91e67b02757a5 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 21:56:44 +0100
Subject: [PATCH 420/607] Move low-level functions out into sshcommon.c.
These are essentially data-structure maintenance, and it seems silly
to have them be part of the same file that manages the topmost
structure of the SSH connection.
---
Recipe | 2 +-
ssh.c | 148 -----------------------------------------------
sshcommon.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 162 insertions(+), 149 deletions(-)
create mode 100644 sshcommon.c
diff --git a/Recipe b/Recipe
index a2728c08..87fc0b67 100644
--- a/Recipe
+++ b/Recipe
@@ -250,7 +250,7 @@ GTKMAIN = gtkmain cmdline
NONSSH = telnet raw rlogin ldisc pinger
# SSH back end (putty, plink, pscp, psftp).
-SSH = ssh ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
+SSH = ssh sshcommon ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
+ sshverstring sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf
diff --git a/ssh.c b/ssh.c
index b38a281b..c73218eb 100644
--- a/ssh.c
+++ b/ssh.c
@@ -278,8 +278,6 @@ enum {
PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM,
};
-static void ssh_pkt_ensure(struct PktOut *, int length);
-static void ssh_pkt_adddata(struct PktOut *, const void *data, int len);
static void ssh2_pkt_send(Ssh, struct PktOut *);
static void do_ssh1_login(void *vctx);
static void do_ssh2_userauth(void *vctx);
@@ -547,100 +545,6 @@ static PktOut *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
#endif
static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin);
-void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node)
-{
- assert(!node->next);
- assert(!node->prev);
- node->next = &pqb->end;
- node->prev = pqb->end.prev;
- node->next->prev = node;
- node->prev->next = node;
-}
-
-void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node)
-{
- assert(!node->next);
- assert(!node->prev);
- node->prev = &pqb->end;
- node->next = pqb->end.next;
- node->next->prev = node;
- node->prev->next = node;
-}
-
-static PktIn *pq_in_get(PacketQueueBase *pqb, int pop)
-{
- PacketQueueNode *node = pqb->end.next;
- if (node == &pqb->end)
- return NULL;
-
- if (pop) {
- node->next->prev = node->prev;
- node->prev->next = node->next;
- node->prev = node->next = NULL;
- }
-
- return FROMFIELD(node, PktIn, qnode);
-}
-
-static PktOut *pq_out_get(PacketQueueBase *pqb, int pop)
-{
- PacketQueueNode *node = pqb->end.next;
- if (node == &pqb->end)
- return NULL;
-
- if (pop) {
- node->next->prev = node->prev;
- node->prev->next = node->next;
- node->prev = node->next = NULL;
- }
-
- return FROMFIELD(node, PktOut, qnode);
-}
-
-void pq_in_init(PktInQueue *pq)
-{
- pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
- pq->get = pq_in_get;
-}
-
-void pq_out_init(PktOutQueue *pq)
-{
- pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
- pq->get = pq_out_get;
-}
-
-void pq_in_clear(PktInQueue *pq)
-{
- PktIn *pkt;
- while ((pkt = pq_pop(pq)) != NULL)
- ssh_unref_packet(pkt);
-}
-
-void pq_out_clear(PktOutQueue *pq)
-{
- PktOut *pkt;
- while ((pkt = pq_pop(pq)) != NULL)
- ssh_free_pktout(pkt);
-}
-
-int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest)
-{
- struct PacketQueueNode *srcfirst, *srclast;
-
- if (src->end.next == &src->end)
- return FALSE;
-
- srcfirst = src->end.next;
- srclast = src->end.prev;
- srcfirst->prev = &dest->end;
- srclast->next = dest->end.next;
- srcfirst->prev->next = srcfirst;
- srclast->next->prev = srclast;
- src->end.next = src->end.prev = &src->end;
-
- return TRUE;
-}
-
struct queued_handler;
struct queued_handler {
int msg1, msg2;
@@ -1104,34 +1008,6 @@ static void c_write_str(Ssh ssh, const char *buf)
c_write(ssh, buf, strlen(buf));
}
-void ssh_unref_packet(PktIn *pkt)
-{
- if (--pkt->refcount <= 0)
- sfree(pkt);
-}
-
-void ssh_free_pktout(PktOut *pkt)
-{
- sfree(pkt->data);
- sfree(pkt);
-}
-
-static void ssh_pkt_BinarySink_write(BinarySink *bs,
- const void *data, size_t len);
-PktOut *ssh_new_packet(void)
-{
- PktOut *pkt = snew(PktOut);
-
- BinarySink_INIT(pkt, ssh_pkt_BinarySink_write);
- pkt->data = NULL;
- pkt->length = 0;
- pkt->maxlen = 0;
- pkt->downstream_id = 0;
- pkt->additional_log_text = NULL;
-
- return pkt;
-}
-
static int s_write(Ssh ssh, const void *data, int len)
{
if (len && ssh->logctx)
@@ -1162,30 +1038,6 @@ static void ssh_pkt_write(Ssh ssh, PktOut *pkt)
queue_idempotent_callback(&ssh->outgoing_data_sender);
}
-/*
- * Packet construction functions. Mostly shared between SSH-1 and SSH-2.
- */
-static void ssh_pkt_ensure(PktOut *pkt, int length)
-{
- if (pkt->maxlen < length) {
- pkt->maxlen = length + 256;
- pkt->data = sresize(pkt->data, pkt->maxlen, unsigned char);
- }
-}
-static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len)
-{
- pkt->length += len;
- ssh_pkt_ensure(pkt, pkt->length);
- memcpy(pkt->data + pkt->length - len, data, len);
-}
-
-static void ssh_pkt_BinarySink_write(BinarySink *bs,
- const void *data, size_t len)
-{
- PktOut *pkt = BinarySink_DOWNCAST(bs, PktOut);
- ssh_pkt_adddata(pkt, data, len);
-}
-
/*
* Either queue or send a packet, depending on whether queueing is
* set.
diff --git a/sshcommon.c b/sshcommon.c
new file mode 100644
index 00000000..7af086fe
--- /dev/null
+++ b/sshcommon.c
@@ -0,0 +1,161 @@
+/*
+ * Supporting routines used in common by all the various components of
+ * the SSH system.
+ */
+
+#include
+
+#include "ssh.h"
+#include "sshchan.h"
+
+/* ----------------------------------------------------------------------
+ * Implementation of PacketQueue.
+ */
+
+void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node)
+{
+ assert(!node->next);
+ assert(!node->prev);
+ node->next = &pqb->end;
+ node->prev = pqb->end.prev;
+ node->next->prev = node;
+ node->prev->next = node;
+}
+
+void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node)
+{
+ assert(!node->next);
+ assert(!node->prev);
+ node->prev = &pqb->end;
+ node->next = pqb->end.next;
+ node->next->prev = node;
+ node->prev->next = node;
+}
+
+static PktIn *pq_in_get(PacketQueueBase *pqb, int pop)
+{
+ PacketQueueNode *node = pqb->end.next;
+ if (node == &pqb->end)
+ return NULL;
+
+ if (pop) {
+ node->next->prev = node->prev;
+ node->prev->next = node->next;
+ node->prev = node->next = NULL;
+ }
+
+ return FROMFIELD(node, PktIn, qnode);
+}
+
+static PktOut *pq_out_get(PacketQueueBase *pqb, int pop)
+{
+ PacketQueueNode *node = pqb->end.next;
+ if (node == &pqb->end)
+ return NULL;
+
+ if (pop) {
+ node->next->prev = node->prev;
+ node->prev->next = node->next;
+ node->prev = node->next = NULL;
+ }
+
+ return FROMFIELD(node, PktOut, qnode);
+}
+
+void pq_in_init(PktInQueue *pq)
+{
+ pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
+ pq->get = pq_in_get;
+}
+
+void pq_out_init(PktOutQueue *pq)
+{
+ pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
+ pq->get = pq_out_get;
+}
+
+void pq_in_clear(PktInQueue *pq)
+{
+ PktIn *pkt;
+ while ((pkt = pq_pop(pq)) != NULL)
+ ssh_unref_packet(pkt);
+}
+
+void pq_out_clear(PktOutQueue *pq)
+{
+ PktOut *pkt;
+ while ((pkt = pq_pop(pq)) != NULL)
+ ssh_free_pktout(pkt);
+}
+
+int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest)
+{
+ struct PacketQueueNode *srcfirst, *srclast;
+
+ if (src->end.next == &src->end)
+ return FALSE;
+
+ srcfirst = src->end.next;
+ srclast = src->end.prev;
+ srcfirst->prev = &dest->end;
+ srclast->next = dest->end.next;
+ srcfirst->prev->next = srcfirst;
+ srclast->next->prev = srclast;
+ src->end.next = src->end.prev = &src->end;
+
+ return TRUE;
+}
+
+/* ----------------------------------------------------------------------
+ * Low-level functions for the packet structures themselves.
+ */
+
+static void ssh_pkt_BinarySink_write(BinarySink *bs,
+ const void *data, size_t len);
+PktOut *ssh_new_packet(void)
+{
+ PktOut *pkt = snew(PktOut);
+
+ BinarySink_INIT(pkt, ssh_pkt_BinarySink_write);
+ pkt->data = NULL;
+ pkt->length = 0;
+ pkt->maxlen = 0;
+ pkt->downstream_id = 0;
+ pkt->additional_log_text = NULL;
+ pkt->qnode.next = pkt->qnode.prev = NULL;
+
+ return pkt;
+}
+
+static void ssh_pkt_ensure(PktOut *pkt, int length)
+{
+ if (pkt->maxlen < length) {
+ pkt->maxlen = length + 256;
+ pkt->data = sresize(pkt->data, pkt->maxlen, unsigned char);
+ }
+}
+static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len)
+{
+ pkt->length += len;
+ ssh_pkt_ensure(pkt, pkt->length);
+ memcpy(pkt->data + pkt->length - len, data, len);
+}
+
+static void ssh_pkt_BinarySink_write(BinarySink *bs,
+ const void *data, size_t len)
+{
+ PktOut *pkt = BinarySink_DOWNCAST(bs, PktOut);
+ ssh_pkt_adddata(pkt, data, len);
+}
+
+void ssh_unref_packet(PktIn *pkt)
+{
+ if (--pkt->refcount <= 0)
+ sfree(pkt);
+}
+
+void ssh_free_pktout(PktOut *pkt)
+{
+ sfree(pkt->data);
+ sfree(pkt);
+}
From 04226693e3cbaf412cacf879073627158d807a1d Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 20:52:12 +0100
Subject: [PATCH 421/607] Get rid of ssh_set_frozen.
We used it to suppress reading from the network at every point during
protocol setup where PuTTY was waiting for a user response to a dialog
box (e.g. a host key warning). The purpose of this was to avoid
dropping an important packet while the coroutine was listening to one
of its other input parameters (as it were). But now everything is
queue-based, packets will stay queued until we're ready to look at
them anyway; so it's better _not_ to freeze the connection, so that
messages we _can_ handle in between (e.g. SSH_MSG_DEBUG or
SSH_MSG_IGNORE) can still be processed.
That dispenses with all uses of ssh_set_frozen except for its use by
ssh_throttle_conn to exert back-pressure on the server in SSH1 which
doesn't have per-channel windows. So I've moved that last use _into_
ssh_throttle_conn, and now the function is completely gone.
---
ssh.c | 36 ++++++++++++------------------------
1 file changed, 12 insertions(+), 24 deletions(-)
diff --git a/ssh.c b/ssh.c
index c73218eb..f5cf7142 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1284,13 +1284,6 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
}
}
-static void ssh_set_frozen(Ssh ssh, int frozen)
-{
- if (ssh->s)
- sk_set_frozen(ssh->s, frozen);
- ssh->frozen = frozen;
-}
-
static void ssh_process_incoming_data(void *ctx)
{
Ssh ssh = (Ssh)ctx;
@@ -1720,13 +1713,23 @@ static const char *connect_to_host(Ssh ssh, const char *host, int port,
static void ssh_throttle_conn(Ssh ssh, int adjust)
{
int old_count = ssh->conn_throttle_count;
+ int frozen;
+
ssh->conn_throttle_count += adjust;
assert(ssh->conn_throttle_count >= 0);
+
if (ssh->conn_throttle_count && !old_count) {
- ssh_set_frozen(ssh, 1);
+ frozen = TRUE;
} else if (!ssh->conn_throttle_count && old_count) {
- ssh_set_frozen(ssh, 0);
+ frozen = FALSE;
+ } else {
+ return; /* don't change current frozen state */
}
+
+ ssh->frozen = frozen;
+
+ if (ssh->s)
+ sk_set_frozen(ssh->s, frozen);
}
static void ssh_channel_check_throttle(struct ssh_channel *c)
@@ -2025,7 +2028,6 @@ static void do_ssh1_login(void *vctx)
sfree(keystr);
crStopV;
} else if (s->dlgret < 0) { /* none configured; use standard handling */
- ssh_set_frozen(ssh, 1);
s->dlgret = verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport,
"rsa", keystr, fingerprint,
@@ -2039,7 +2041,6 @@ static void do_ssh1_login(void *vctx)
crWaitUntilV(ssh->user_response >= 0);
s->dlgret = ssh->user_response;
}
- ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh_disconnect(ssh, "User aborted at host key verification",
@@ -2111,7 +2112,6 @@ static void do_ssh1_login(void *vctx)
/* Warn about chosen cipher if necessary. */
if (warn) {
- ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend, "cipher", cipher_string,
ssh_dialog_callback, ssh);
if (s->dlgret < 0) {
@@ -2119,7 +2119,6 @@ static void do_ssh1_login(void *vctx)
crWaitUntilV(ssh->user_response >= 0);
s->dlgret = ssh->user_response;
}
- ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
0, TRUE);
@@ -4625,7 +4624,6 @@ static void do_ssh2_transport(void *vctx)
BinarySource_UPCAST(pktin)->len + 1);
if (s->warn_kex) {
- ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",
ssh->kex->name,
ssh_dialog_callback, ssh);
@@ -4634,7 +4632,6 @@ static void do_ssh2_transport(void *vctx)
crWaitUntilV(ssh->user_response >= 0);
s->dlgret = ssh->user_response;
}
- ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh_disconnect(ssh, "User aborted at kex warning", NULL,
0, TRUE);
@@ -4646,8 +4643,6 @@ static void do_ssh2_transport(void *vctx)
int j, k;
char *betteralgs;
- ssh_set_frozen(ssh, 1);
-
/*
* Change warning box wording depending on why we chose a
* warning-level host key algorithm. If it's because
@@ -4695,7 +4690,6 @@ static void do_ssh2_transport(void *vctx)
crWaitUntilV(ssh->user_response >= 0);
s->dlgret = ssh->user_response;
}
- ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh_disconnect(ssh, "User aborted at host key warning", NULL,
0, TRUE);
@@ -4704,7 +4698,6 @@ static void do_ssh2_transport(void *vctx)
}
if (s->warn_cscipher) {
- ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend,
"client-to-server cipher",
s->out.cipher->name,
@@ -4714,7 +4707,6 @@ static void do_ssh2_transport(void *vctx)
crWaitUntilV(ssh->user_response >= 0);
s->dlgret = ssh->user_response;
}
- ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
0, TRUE);
@@ -4723,7 +4715,6 @@ static void do_ssh2_transport(void *vctx)
}
if (s->warn_sccipher) {
- ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend,
"server-to-client cipher",
s->in.cipher->name,
@@ -4733,7 +4724,6 @@ static void do_ssh2_transport(void *vctx)
crWaitUntilV(ssh->user_response >= 0);
s->dlgret = ssh->user_response;
}
- ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
0, TRUE);
@@ -5498,7 +5488,6 @@ static void do_ssh2_transport(void *vctx)
bombout(("Host key did not appear in manually configured list"));
crStopV;
} else if (s->dlgret < 0) { /* none configured; use standard handling */
- ssh_set_frozen(ssh, 1);
s->dlgret = verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport,
ssh_key_cache_id(s->hkey),
@@ -5512,7 +5501,6 @@ static void do_ssh2_transport(void *vctx)
crWaitUntilV(ssh->user_response >= 0);
s->dlgret = ssh->user_response;
}
- ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh_disconnect(ssh, "Aborted at host key verification", NULL,
0, TRUE);
From 6a5d4d083abc6245b6d36e3d223a4404d30c1dff Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 20:30:40 +0100
Subject: [PATCH 422/607] Make pq_empty_on_to_front_of more general.
It's really just a concatenator for a pair of linked lists, but
unhelpfully restricted in which of the lists it replaces with the
output. Better to have a three-argument function that puts the output
wherever you like, whether it overlaps either or neither one of the
inputs.
---
ssh.c | 4 ++-
ssh.h | 11 +++++---
sshcommon.c | 74 ++++++++++++++++++++++++++++++++++++++++++-----------
3 files changed, 69 insertions(+), 20 deletions(-)
diff --git a/ssh.c b/ssh.c
index f5cf7142..2749f23f 100644
--- a/ssh.c
+++ b/ssh.c
@@ -8846,8 +8846,10 @@ static void do_ssh2_connection(void *vctx)
* matches any of the table entries we've just modified will go to
* the right handler function, and won't come here to confuse us.
*/
- if (pq_empty_on_to_front_of(&ssh->pq_ssh2_connection, &ssh->pq_full))
+ if (pq_peek(&ssh->pq_ssh2_connection)) {
+ pq_concatenate(&ssh->pq_full, &ssh->pq_ssh2_connection, &ssh->pq_full);
queue_idempotent_callback(&ssh->pq_full_consumer);
+ }
/*
* Now the connection protocol is properly up and running, with
diff --git a/ssh.h b/ssh.h
index 7ed45101..f4ef94ef 100644
--- a/ssh.h
+++ b/ssh.h
@@ -102,7 +102,8 @@ typedef struct PktOutQueue {
void pq_base_init(PacketQueueBase *pqb);
void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node);
void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node);
-int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest);
+void pq_base_concatenate(PacketQueueBase *dest,
+ PacketQueueBase *q1, PacketQueueBase *q2);
void pq_in_init(PktInQueue *pq);
void pq_out_init(PktOutQueue *pq);
@@ -117,10 +118,12 @@ void pq_out_clear(PktOutQueue *pq);
pq_base_push_front(&(pq)->pqb, &(pkt)->qnode))
#define pq_peek(pq) ((pq)->get(&(pq)->pqb, FALSE))
#define pq_pop(pq) ((pq)->get(&(pq)->pqb, TRUE))
-#define pq_empty_on_to_front_of(src, dst) \
- TYPECHECK((src)->get(&(src)->pqb, FALSE) == \
+#define pq_concatenate(dst, q1, q2) \
+ TYPECHECK((q1)->get(&(q1)->pqb, FALSE) == \
+ (dst)->get(&(dst)->pqb, FALSE) && \
+ (q2)->get(&(q2)->pqb, FALSE) == \
(dst)->get(&(dst)->pqb, FALSE), \
- pq_base_empty_on_to_front_of(&(src)->pqb, &(dst)->pqb))
+ pq_base_concatenate(&(dst)->pqb, &(q1)->pqb, &(q2)->pqb))
/*
* Packet type contexts, so that ssh2_pkt_type can correctly decode
diff --git a/sshcommon.c b/sshcommon.c
index 7af086fe..856a8358 100644
--- a/sshcommon.c
+++ b/sshcommon.c
@@ -88,22 +88,66 @@ void pq_out_clear(PktOutQueue *pq)
ssh_free_pktout(pkt);
}
-int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest)
+/*
+ * Concatenate the contents of the two queues q1 and q2, and leave the
+ * result in qdest. qdest must be either empty, or one of the input
+ * queues.
+ */
+void pq_base_concatenate(PacketQueueBase *qdest,
+ PacketQueueBase *q1, PacketQueueBase *q2)
{
- struct PacketQueueNode *srcfirst, *srclast;
-
- if (src->end.next == &src->end)
- return FALSE;
-
- srcfirst = src->end.next;
- srclast = src->end.prev;
- srcfirst->prev = &dest->end;
- srclast->next = dest->end.next;
- srcfirst->prev->next = srcfirst;
- srclast->next->prev = srclast;
- src->end.next = src->end.prev = &src->end;
-
- return TRUE;
+ struct PacketQueueNode *head1, *tail1, *head2, *tail2;
+
+ /*
+ * Extract the contents from both input queues, and empty them.
+ */
+
+ head1 = (q1->end.next == &q1->end ? NULL : q1->end.next);
+ tail1 = (q1->end.prev == &q1->end ? NULL : q1->end.prev);
+ head2 = (q2->end.next == &q2->end ? NULL : q2->end.next);
+ tail2 = (q2->end.prev == &q2->end ? NULL : q2->end.prev);
+
+ q1->end.next = q1->end.prev = &q1->end;
+ q2->end.next = q2->end.prev = &q2->end;
+
+ /*
+ * Link the two lists together, handling the case where one or
+ * both is empty.
+ */
+
+ if (tail1)
+ tail1->next = head2;
+ else
+ head1 = head2;
+
+ if (head2)
+ head2->prev = tail1;
+ else
+ tail2 = head1;
+
+ /*
+ * Check the destination queue is currently empty. (If it was one
+ * of the input queues, then it will be, because we emptied both
+ * of those just a moment ago.)
+ */
+
+ assert(qdest->end.next == &qdest->end);
+ assert(qdest->end.prev == &qdest->end);
+
+ /*
+ * If our concatenated list has anything in it, then put it in
+ * dest.
+ */
+
+ if (!head1) {
+ assert(!tail2);
+ } else {
+ assert(tail2);
+ qdest->end.next = head1;
+ qdest->end.prev = tail2;
+ head1->prev = &qdest->end;
+ tail2->next = &qdest->end;
+ }
}
/* ----------------------------------------------------------------------
From 64f95e633469844b86f4d62c03ae991c4b64ffee Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 20:31:19 +0100
Subject: [PATCH 423/607] Move the zombiechan implementation into sshcommon.c.
It doesn't really have to be in ssh.c sharing that file's internal
data structures; it's as much an independent object implementation as
any of the less trivial Channel instances. So it's another thing we
can get out of that too-large source file.
---
ssh.c | 67 -----------------------------------------------------
sshchan.h | 12 ++++++++++
sshcommon.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 76 insertions(+), 67 deletions(-)
diff --git a/ssh.c b/ssh.c
index 2749f23f..17e9075d 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1357,73 +1357,6 @@ int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof)
return FALSE; /* default: never proactively ask for a close */
}
-/*
- * Trivial channel vtable for handling 'zombie channels' - those whose
- * local source of data has already been shut down or otherwise
- * stopped existing - so that we don't have to give them a null
- * 'Channel *' and special-case that all over the place.
- */
-
-static void zombiechan_free(Channel *chan);
-static int zombiechan_send(Channel *chan, int is_stderr, const void *, int);
-static void zombiechan_set_input_wanted(Channel *chan, int wanted);
-static void zombiechan_do_nothing(Channel *chan);
-static void zombiechan_open_failure(Channel *chan, const char *);
-static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof);
-static char *zombiechan_log_close_msg(Channel *chan) { return NULL; }
-
-static const struct ChannelVtable zombiechan_channelvt = {
- zombiechan_free,
- zombiechan_do_nothing, /* open_confirmation */
- zombiechan_open_failure,
- zombiechan_send,
- zombiechan_do_nothing, /* send_eof */
- zombiechan_set_input_wanted,
- zombiechan_log_close_msg,
- zombiechan_want_close,
-};
-
-Channel *zombiechan_new(void)
-{
- Channel *chan = snew(Channel);
- chan->vt = &zombiechan_channelvt;
- chan->initial_fixed_window_size = 0;
- return chan;
-}
-
-static void zombiechan_free(Channel *chan)
-{
- assert(chan->vt == &zombiechan_channelvt);
- sfree(chan);
-}
-
-static void zombiechan_do_nothing(Channel *chan)
-{
- assert(chan->vt == &zombiechan_channelvt);
-}
-
-static void zombiechan_open_failure(Channel *chan, const char *errtext)
-{
- assert(chan->vt == &zombiechan_channelvt);
-}
-
-static int zombiechan_send(Channel *chan, int is_stderr,
- const void *data, int length)
-{
- assert(chan->vt == &zombiechan_channelvt);
- return 0;
-}
-
-static void zombiechan_set_input_wanted(Channel *chan, int enable)
-{
- assert(chan->vt == &zombiechan_channelvt);
-}
-
-static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof)
-{
- return TRUE;
-}
-
static int ssh_do_close(Ssh ssh, int notify_exit)
{
int ret = 0;
diff --git a/sshchan.h b/sshchan.h
index 271cdb09..e2bc28b0 100644
--- a/sshchan.h
+++ b/sshchan.h
@@ -56,6 +56,18 @@ void chan_remotely_opened_failure(Channel *chan, const char *errtext);
* closing until both directions have had an EOF */
int chan_no_eager_close(Channel *, int, int);
+/*
+ * Constructor for a trivial do-nothing implementation of
+ * ChannelVtable. Used for 'zombie' channels, i.e. channels whose
+ * proper local source of data has been shut down or otherwise stopped
+ * existing, but the SSH side is still there and needs some kind of a
+ * Channel implementation to talk to. In particular, the want_close
+ * method for this channel always returns 'yes, please close this
+ * channel asap', regardless of whether local and/or remote EOF have
+ * been sent - indeed, even if _neither_ has.
+ */
+Channel *zombiechan_new(void);
+
/* ----------------------------------------------------------------------
* This structure is owned by an SSH connection layer, and identifies
* the connection layer's end of the channel, for the Channel
diff --git a/sshcommon.c b/sshcommon.c
index 856a8358..8bab9beb 100644
--- a/sshcommon.c
+++ b/sshcommon.c
@@ -203,3 +203,67 @@ void ssh_free_pktout(PktOut *pkt)
sfree(pkt->data);
sfree(pkt);
}
+/* ----------------------------------------------------------------------
+ * Implement zombiechan_new() and its trivial vtable.
+ */
+
+static void zombiechan_free(Channel *chan);
+static int zombiechan_send(Channel *chan, int is_stderr, const void *, int);
+static void zombiechan_set_input_wanted(Channel *chan, int wanted);
+static void zombiechan_do_nothing(Channel *chan);
+static void zombiechan_open_failure(Channel *chan, const char *);
+static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof);
+static char *zombiechan_log_close_msg(Channel *chan) { return NULL; }
+
+static const struct ChannelVtable zombiechan_channelvt = {
+ zombiechan_free,
+ zombiechan_do_nothing, /* open_confirmation */
+ zombiechan_open_failure,
+ zombiechan_send,
+ zombiechan_do_nothing, /* send_eof */
+ zombiechan_set_input_wanted,
+ zombiechan_log_close_msg,
+ zombiechan_want_close,
+};
+
+Channel *zombiechan_new(void)
+{
+ Channel *chan = snew(Channel);
+ chan->vt = &zombiechan_channelvt;
+ chan->initial_fixed_window_size = 0;
+ return chan;
+}
+
+static void zombiechan_free(Channel *chan)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+ sfree(chan);
+}
+
+static void zombiechan_do_nothing(Channel *chan)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+}
+
+static void zombiechan_open_failure(Channel *chan, const char *errtext)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+}
+
+static int zombiechan_send(Channel *chan, int is_stderr,
+ const void *data, int length)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+ return 0;
+}
+
+static void zombiechan_set_input_wanted(Channel *chan, int enable)
+{
+ assert(chan->vt == &zombiechan_channelvt);
+}
+
+static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof)
+{
+ return TRUE;
+}
+
From 783f03d5ede052ad03c5311974e532609a0a7fa3 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 20:32:27 +0100
Subject: [PATCH 424/607] Move the default Channel methods into sshcommon.c.
Those don't need any of ssh.c's internal facilities either.
---
ssh.c | 15 ---------------
sshcommon.c | 19 +++++++++++++++++++
2 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/ssh.c b/ssh.c
index 17e9075d..7a2b26fa 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1342,21 +1342,6 @@ static void ssh_process_user_input(void *ctx)
ssh->current_user_input_fn(ssh);
}
-void chan_remotely_opened_confirmation(Channel *chan)
-{
- assert(0 && "this channel type should never receive OPEN_CONFIRMATION");
-}
-
-void chan_remotely_opened_failure(Channel *chan, const char *errtext)
-{
- assert(0 && "this channel type should never receive OPEN_FAILURE");
-}
-
-int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof)
-{
- return FALSE; /* default: never proactively ask for a close */
-}
-
static int ssh_do_close(Ssh ssh, int notify_exit)
{
int ret = 0;
diff --git a/sshcommon.c b/sshcommon.c
index 8bab9beb..ec471544 100644
--- a/sshcommon.c
+++ b/sshcommon.c
@@ -267,3 +267,22 @@ static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof)
return TRUE;
}
+/* ----------------------------------------------------------------------
+ * Centralised standard methods for other channel implementations to
+ * borrow.
+ */
+
+void chan_remotely_opened_confirmation(Channel *chan)
+{
+ assert(0 && "this channel type should never receive OPEN_CONFIRMATION");
+}
+
+void chan_remotely_opened_failure(Channel *chan, const char *errtext)
+{
+ assert(0 && "this channel type should never receive OPEN_FAILURE");
+}
+
+int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof)
+{
+ return FALSE; /* default: never proactively ask for a close */
+}
From 12abb953945f19b4aab6ca56dde0d2acd1c692c7 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 20:36:40 +0100
Subject: [PATCH 425/607] Move the ttymode formatter into sshcommon.c.
While I'm at it, I've brought it all into a single function: the
parsing of data from Conf, the list of modes, and even the old
callback system for writing to the destination buffer is now a simple
if statement that formats mode parameters as byte or uint32 depending
on SSH version. Also, the terminal speeds and the end byte are part of
the same setup, so it's all together in one place instead of scattered
all over ssh.c.
---
ssh.c | 190 ++--------------------------------------------------
ssh.h | 5 ++
sshcommon.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 191 insertions(+), 184 deletions(-)
diff --git a/ssh.c b/ssh.c
index 7a2b26fa..3d91a672 100644
--- a/ssh.c
+++ b/ssh.c
@@ -50,111 +50,6 @@ static const char *const ssh2_disconnect_reasons[] = {
#define DH_MIN_SIZE 1024
#define DH_MAX_SIZE 8192
-/*
- * Codes for terminal modes.
- * Most of these are the same in SSH-1 and SSH-2.
- * This list is derived from RFC 4254 and
- * SSH-1 RFC-1.2.31.
- */
-static const struct ssh_ttymode {
- const char* const mode;
- int opcode;
- enum { TTY_OP_CHAR, TTY_OP_BOOL } type;
-} ssh_ttymodes[] = {
- /* "V" prefix discarded for special characters relative to SSH specs */
- { "INTR", 1, TTY_OP_CHAR },
- { "QUIT", 2, TTY_OP_CHAR },
- { "ERASE", 3, TTY_OP_CHAR },
- { "KILL", 4, TTY_OP_CHAR },
- { "EOF", 5, TTY_OP_CHAR },
- { "EOL", 6, TTY_OP_CHAR },
- { "EOL2", 7, TTY_OP_CHAR },
- { "START", 8, TTY_OP_CHAR },
- { "STOP", 9, TTY_OP_CHAR },
- { "SUSP", 10, TTY_OP_CHAR },
- { "DSUSP", 11, TTY_OP_CHAR },
- { "REPRINT", 12, TTY_OP_CHAR },
- { "WERASE", 13, TTY_OP_CHAR },
- { "LNEXT", 14, TTY_OP_CHAR },
- { "FLUSH", 15, TTY_OP_CHAR },
- { "SWTCH", 16, TTY_OP_CHAR },
- { "STATUS", 17, TTY_OP_CHAR },
- { "DISCARD", 18, TTY_OP_CHAR },
- { "IGNPAR", 30, TTY_OP_BOOL },
- { "PARMRK", 31, TTY_OP_BOOL },
- { "INPCK", 32, TTY_OP_BOOL },
- { "ISTRIP", 33, TTY_OP_BOOL },
- { "INLCR", 34, TTY_OP_BOOL },
- { "IGNCR", 35, TTY_OP_BOOL },
- { "ICRNL", 36, TTY_OP_BOOL },
- { "IUCLC", 37, TTY_OP_BOOL },
- { "IXON", 38, TTY_OP_BOOL },
- { "IXANY", 39, TTY_OP_BOOL },
- { "IXOFF", 40, TTY_OP_BOOL },
- { "IMAXBEL", 41, TTY_OP_BOOL },
- { "IUTF8", 42, TTY_OP_BOOL },
- { "ISIG", 50, TTY_OP_BOOL },
- { "ICANON", 51, TTY_OP_BOOL },
- { "XCASE", 52, TTY_OP_BOOL },
- { "ECHO", 53, TTY_OP_BOOL },
- { "ECHOE", 54, TTY_OP_BOOL },
- { "ECHOK", 55, TTY_OP_BOOL },
- { "ECHONL", 56, TTY_OP_BOOL },
- { "NOFLSH", 57, TTY_OP_BOOL },
- { "TOSTOP", 58, TTY_OP_BOOL },
- { "IEXTEN", 59, TTY_OP_BOOL },
- { "ECHOCTL", 60, TTY_OP_BOOL },
- { "ECHOKE", 61, TTY_OP_BOOL },
- { "PENDIN", 62, TTY_OP_BOOL }, /* XXX is this a real mode? */
- { "OPOST", 70, TTY_OP_BOOL },
- { "OLCUC", 71, TTY_OP_BOOL },
- { "ONLCR", 72, TTY_OP_BOOL },
- { "OCRNL", 73, TTY_OP_BOOL },
- { "ONOCR", 74, TTY_OP_BOOL },
- { "ONLRET", 75, TTY_OP_BOOL },
- { "CS7", 90, TTY_OP_BOOL },
- { "CS8", 91, TTY_OP_BOOL },
- { "PARENB", 92, TTY_OP_BOOL },
- { "PARODD", 93, TTY_OP_BOOL }
-};
-
-/* Miscellaneous other tty-related constants. */
-#define SSH_TTY_OP_END 0
-/* The opcodes for ISPEED/OSPEED differ between SSH-1 and SSH-2. */
-#define SSH1_TTY_OP_ISPEED 192
-#define SSH1_TTY_OP_OSPEED 193
-#define SSH2_TTY_OP_ISPEED 128
-#define SSH2_TTY_OP_OSPEED 129
-
-/* Helper functions for parsing tty-related config. */
-static unsigned int ssh_tty_parse_specchar(char *s)
-{
- unsigned int ret;
- if (*s) {
- char *next = NULL;
- ret = ctrlparse(s, &next);
- if (!next) ret = s[0];
- } else {
- ret = 255; /* special value meaning "don't set" */
- }
- return ret;
-}
-static unsigned int ssh_tty_parse_boolean(char *s)
-{
- if (stricmp(s, "yes") == 0 ||
- stricmp(s, "on") == 0 ||
- stricmp(s, "true") == 0 ||
- stricmp(s, "+") == 0)
- return 1; /* true */
- else if (stricmp(s, "no") == 0 ||
- stricmp(s, "off") == 0 ||
- stricmp(s, "false") == 0 ||
- stricmp(s, "-") == 0)
- return 0; /* false */
- else
- return (atoi(s) != 0);
-}
-
/* Safely convert rekey_time to unsigned long minutes */
static unsigned long rekey_mins(int rekey_time, unsigned long def)
{
@@ -870,41 +765,6 @@ static void bomb_out(Ssh ssh, char *text)
#define bombout(msg) bomb_out(ssh, dupprintf msg)
-/* Helper function for common bits of parsing ttymodes. */
-static void parse_ttymodes(
- BinarySink *bs, Ssh ssh,
- void (*do_mode)(BinarySink *, const struct ssh_ttymode *, char *))
-{
- int i;
- const struct ssh_ttymode *mode;
- char *val;
-
- for (i = 0; i < lenof(ssh_ttymodes); i++) {
- mode = ssh_ttymodes + i;
- /* Every mode known to the current version of the code should be
- * mentioned; this was ensured when settings were loaded. */
- val = conf_get_str_str(ssh->conf, CONF_ttymodes, mode->mode);
-
- /*
- * val[0] can be
- * - 'V', indicating that an explicit value follows it;
- * - 'A', indicating that we should pass the value through from
- * the local environment via get_ttymode; or
- * - 'N', indicating that we should explicitly not send this
- * mode.
- */
- if (val[0] == 'A') {
- val = get_ttymode(ssh->frontend, mode->mode);
- if (val) {
- do_mode(bs, mode, val);
- sfree(val);
- }
- } else if (val[0] == 'V') {
- do_mode(bs, mode, val + 1); /* skip the 'V' */
- } /* else 'N', or something from the future we don't understand */
- }
-}
-
static int ssh_channelcmp(void *av, void *bv)
{
struct ssh_channel *a = (struct ssh_channel *) av;
@@ -3363,22 +3223,6 @@ static void ssh1_smsg_exit_status(Ssh ssh, PktIn *pktin)
ssh_disconnect(ssh, NULL, NULL, 0, TRUE);
}
-/* Helper function to deal with sending tty modes for REQUEST_PTY */
-static void ssh1_send_ttymode(BinarySink *bs,
- const struct ssh_ttymode *mode, char *val)
-{
- put_byte(bs, mode->opcode);
-
- switch (mode->type) {
- case TTY_OP_CHAR:
- put_byte(bs, ssh_tty_parse_specchar(val));
- break;
- case TTY_OP_BOOL:
- put_byte(bs, ssh_tty_parse_boolean(val));
- break;
- }
-}
-
static int (ssh_agent_forwarding_permitted)(ConnectionLayer *cl)
{
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
@@ -3478,12 +3322,9 @@ static void do_ssh1_connection(void *vctx)
put_uint32(pkt, ssh->term_width);
put_uint32(pkt, 0); /* width in pixels */
put_uint32(pkt, 0); /* height in pixels */
- parse_ttymodes(BinarySink_UPCAST(pkt), ssh, ssh1_send_ttymode);
- put_byte(pkt, SSH1_TTY_OP_ISPEED);
- put_uint32(pkt, ssh->ispeed);
- put_byte(pkt, SSH1_TTY_OP_OSPEED);
- put_uint32(pkt, ssh->ospeed);
- put_byte(pkt, SSH_TTY_OP_END);
+ write_ttymodes_to_packet_from_conf(
+ BinarySink_UPCAST(pkt), ssh->frontend, ssh->conf,
+ 1, ssh->ospeed, ssh->ispeed);
ssh_pkt_write(ssh, pkt);
ssh->state = SSH_STATE_INTERMED;
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection)) != NULL);
@@ -6825,22 +6666,6 @@ static void ssh2_msg_userauth_banner(Ssh ssh, PktIn *pktin)
}
}
-/* Helper function to deal with sending tty modes for "pty-req" */
-static void ssh2_send_ttymode(BinarySink *bs,
- const struct ssh_ttymode *mode, char *val)
-{
- put_byte(bs, mode->opcode);
-
- switch (mode->type) {
- case TTY_OP_CHAR:
- put_uint32(bs, ssh_tty_parse_specchar(val));
- break;
- case TTY_OP_BOOL:
- put_uint32(bs, ssh_tty_parse_boolean(val));
- break;
- }
-}
-
static void ssh2_setup_x11(struct ssh_channel *c, PktIn *pktin,
void *ctx)
{
@@ -6935,12 +6760,9 @@ static void ssh2_setup_pty(struct ssh_channel *c, PktIn *pktin,
put_uint32(pktout, 0); /* pixel height */
{
strbuf *modebuf = strbuf_new();
- parse_ttymodes(BinarySink_UPCAST(modebuf), ssh, ssh2_send_ttymode);
- put_byte(modebuf, SSH2_TTY_OP_ISPEED);
- put_uint32(modebuf, ssh->ispeed);
- put_byte(modebuf, SSH2_TTY_OP_OSPEED);
- put_uint32(modebuf, ssh->ospeed);
- put_byte(modebuf, SSH_TTY_OP_END);
+ write_ttymodes_to_packet_from_conf(
+ BinarySink_UPCAST(modebuf), ssh->frontend, ssh->conf,
+ 2, ssh->ospeed, ssh->ispeed);
put_stringsb(pktout, modebuf);
}
ssh2_pkt_send(ssh, pktout);
diff --git a/ssh.h b/ssh.h
index f4ef94ef..bfeb20df 100644
--- a/ssh.h
+++ b/ssh.h
@@ -1321,3 +1321,8 @@ enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_LOG2_ENUM) };
#define TMP_DECLARE_REAL_ENUM(thing) thing = 1 << log2_##thing,
enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_REAL_ENUM) };
#undef TMP_DECLARE_REAL_ENUM
+
+/* Shared function that writes tty modes into a pty request */
+void write_ttymodes_to_packet_from_conf(
+ BinarySink *bs, Frontend *frontend, Conf *conf,
+ int ssh_version, int ospeed, int ispeed);
diff --git a/sshcommon.c b/sshcommon.c
index ec471544..024b64c7 100644
--- a/sshcommon.c
+++ b/sshcommon.c
@@ -4,7 +4,9 @@
*/
#include
+#include
+#include "putty.h"
#include "ssh.h"
#include "sshchan.h"
@@ -286,3 +288,181 @@ int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof)
{
return FALSE; /* default: never proactively ask for a close */
}
+
+/* ----------------------------------------------------------------------
+ * Common routine to marshal tty modes into an SSH packet.
+ */
+
+void write_ttymodes_to_packet_from_conf(
+ BinarySink *bs, Frontend *frontend, Conf *conf,
+ int ssh_version, int ospeed, int ispeed)
+{
+ int i;
+
+ /*
+ * Codes for terminal modes.
+ * Most of these are the same in SSH-1 and SSH-2.
+ * This list is derived from RFC 4254 and
+ * SSH-1 RFC-1.2.31.
+ */
+ static const struct ssh_ttymode {
+ const char *mode;
+ int opcode;
+ enum { TTY_OP_CHAR, TTY_OP_BOOL } type;
+ } ssh_ttymodes[] = {
+ /* "V" prefix discarded for special characters relative to SSH specs */
+ { "INTR", 1, TTY_OP_CHAR },
+ { "QUIT", 2, TTY_OP_CHAR },
+ { "ERASE", 3, TTY_OP_CHAR },
+ { "KILL", 4, TTY_OP_CHAR },
+ { "EOF", 5, TTY_OP_CHAR },
+ { "EOL", 6, TTY_OP_CHAR },
+ { "EOL2", 7, TTY_OP_CHAR },
+ { "START", 8, TTY_OP_CHAR },
+ { "STOP", 9, TTY_OP_CHAR },
+ { "SUSP", 10, TTY_OP_CHAR },
+ { "DSUSP", 11, TTY_OP_CHAR },
+ { "REPRINT", 12, TTY_OP_CHAR },
+ { "WERASE", 13, TTY_OP_CHAR },
+ { "LNEXT", 14, TTY_OP_CHAR },
+ { "FLUSH", 15, TTY_OP_CHAR },
+ { "SWTCH", 16, TTY_OP_CHAR },
+ { "STATUS", 17, TTY_OP_CHAR },
+ { "DISCARD", 18, TTY_OP_CHAR },
+ { "IGNPAR", 30, TTY_OP_BOOL },
+ { "PARMRK", 31, TTY_OP_BOOL },
+ { "INPCK", 32, TTY_OP_BOOL },
+ { "ISTRIP", 33, TTY_OP_BOOL },
+ { "INLCR", 34, TTY_OP_BOOL },
+ { "IGNCR", 35, TTY_OP_BOOL },
+ { "ICRNL", 36, TTY_OP_BOOL },
+ { "IUCLC", 37, TTY_OP_BOOL },
+ { "IXON", 38, TTY_OP_BOOL },
+ { "IXANY", 39, TTY_OP_BOOL },
+ { "IXOFF", 40, TTY_OP_BOOL },
+ { "IMAXBEL", 41, TTY_OP_BOOL },
+ { "IUTF8", 42, TTY_OP_BOOL },
+ { "ISIG", 50, TTY_OP_BOOL },
+ { "ICANON", 51, TTY_OP_BOOL },
+ { "XCASE", 52, TTY_OP_BOOL },
+ { "ECHO", 53, TTY_OP_BOOL },
+ { "ECHOE", 54, TTY_OP_BOOL },
+ { "ECHOK", 55, TTY_OP_BOOL },
+ { "ECHONL", 56, TTY_OP_BOOL },
+ { "NOFLSH", 57, TTY_OP_BOOL },
+ { "TOSTOP", 58, TTY_OP_BOOL },
+ { "IEXTEN", 59, TTY_OP_BOOL },
+ { "ECHOCTL", 60, TTY_OP_BOOL },
+ { "ECHOKE", 61, TTY_OP_BOOL },
+ { "PENDIN", 62, TTY_OP_BOOL }, /* XXX is this a real mode? */
+ { "OPOST", 70, TTY_OP_BOOL },
+ { "OLCUC", 71, TTY_OP_BOOL },
+ { "ONLCR", 72, TTY_OP_BOOL },
+ { "OCRNL", 73, TTY_OP_BOOL },
+ { "ONOCR", 74, TTY_OP_BOOL },
+ { "ONLRET", 75, TTY_OP_BOOL },
+ { "CS7", 90, TTY_OP_BOOL },
+ { "CS8", 91, TTY_OP_BOOL },
+ { "PARENB", 92, TTY_OP_BOOL },
+ { "PARODD", 93, TTY_OP_BOOL }
+ };
+
+ /* Miscellaneous other tty-related constants. */
+ enum {
+ /* The opcodes for ISPEED/OSPEED differ between SSH-1 and SSH-2. */
+ SSH1_TTY_OP_ISPEED = 192,
+ SSH1_TTY_OP_OSPEED = 193,
+ SSH2_TTY_OP_ISPEED = 128,
+ SSH2_TTY_OP_OSPEED = 129,
+
+ SSH_TTY_OP_END = 0
+ };
+
+ for (i = 0; i < lenof(ssh_ttymodes); i++) {
+ const struct ssh_ttymode *mode = ssh_ttymodes + i;
+ const char *sval = conf_get_str_str(conf, CONF_ttymodes, mode->mode);
+ char *to_free = NULL;
+
+ /* Every mode known to the current version of the code should be
+ * mentioned; this was ensured when settings were loaded. */
+
+ /*
+ * sval[0] can be
+ * - 'V', indicating that an explicit value follows it;
+ * - 'A', indicating that we should pass the value through from
+ * the local environment via get_ttymode; or
+ * - 'N', indicating that we should explicitly not send this
+ * mode.
+ */
+ if (sval[0] == 'A') {
+ sval = to_free = get_ttymode(frontend, mode->mode);
+ } else if (sval[0] == 'V') {
+ sval++; /* skip the 'V' */
+ } else {
+ /* else 'N', or something from the future we don't understand */
+ continue;
+ }
+
+ if (sval) {
+ /*
+ * Parse the string representation of the tty mode
+ * into the integer value it will take on the wire.
+ */
+ unsigned ival = 0;
+
+ switch (mode->type) {
+ case TTY_OP_CHAR:
+ if (*sval) {
+ char *next = NULL;
+ /* We know ctrlparse won't write to the string, so
+ * casting away const is ugly but allowable. */
+ ival = ctrlparse((char *)sval, &next);
+ if (!next)
+ ival = sval[0];
+ } else {
+ ival = 255; /* special value meaning "don't set" */
+ }
+ break;
+ case TTY_OP_BOOL:
+ if (stricmp(sval, "yes") == 0 ||
+ stricmp(sval, "on") == 0 ||
+ stricmp(sval, "true") == 0 ||
+ stricmp(sval, "+") == 0)
+ ival = 1; /* true */
+ else if (stricmp(sval, "no") == 0 ||
+ stricmp(sval, "off") == 0 ||
+ stricmp(sval, "false") == 0 ||
+ stricmp(sval, "-") == 0)
+ ival = 0; /* false */
+ else
+ ival = (atoi(sval) != 0);
+ break;
+ default:
+ assert(0 && "Bad mode->type");
+ }
+
+ /*
+ * And write it into the output packet. The parameter
+ * value is formatted as a byte in SSH-1, but a uint32
+ * in SSH-2.
+ */
+ put_byte(bs, mode->opcode);
+ if (ssh_version == 1)
+ put_byte(bs, ival);
+ else
+ put_uint32(bs, ival);
+ }
+
+ sfree(to_free);
+ }
+
+ /*
+ * Finish off with the terminal speeds (which are formatted as
+ * uint32 in both protocol versions) and the end marker.
+ */
+ put_byte(bs, ssh_version == 1 ? SSH1_TTY_OP_ISPEED : SSH2_TTY_OP_ISPEED);
+ put_uint32(bs, ispeed);
+ put_byte(bs, ssh_version == 1 ? SSH1_TTY_OP_OSPEED : SSH2_TTY_OP_OSPEED);
+ put_uint32(bs, ospeed);
+ put_byte(bs, SSH_TTY_OP_END);
+}
From 968252bbdcca15fc73060a5bc709c3dbbc96c5ca Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 20:37:12 +0100
Subject: [PATCH 426/607] Move alloc_channel_id into sshcommon.c.
That function _did_ depend on ssh.c's internal facilities, namely the
layout of 'struct ssh_channel'. In place of that, it now takes an
extra integer argument telling it where to find the channel id in
whatever data structure you give it a tree of - so now I can split up
the SSH-1 and SSH-2 channel handling without losing the services of
that nice channel-number allocator.
---
ssh.c | 34 +---------------------------------
ssh.h | 8 ++++++++
sshcommon.c | 37 +++++++++++++++++++++++++++++++++++++
3 files changed, 46 insertions(+), 33 deletions(-)
diff --git a/ssh.c b/ssh.c
index 3d91a672..d5f203b0 100644
--- a/ssh.c
+++ b/ssh.c
@@ -814,38 +814,6 @@ static int ssh_rportcmp_ssh2(void *av, void *bv)
return 0;
}
-static unsigned alloc_channel_id(Ssh ssh)
-{
- const unsigned CHANNEL_NUMBER_OFFSET = 256;
- search234_state ss;
-
- /*
- * First-fit allocation of channel numbers: we always pick the
- * lowest unused one.
- *
- * Every channel before that, and no channel after it, has an ID
- * exactly equal to its tree index plus CHANNEL_NUMBER_OFFSET. So
- * we can use the search234 system to identify the length of that
- * initial sequence, in a single log-time pass down the channels
- * tree.
- */
- search234_start(&ss, ssh->channels);
- while (ss.element) {
- struct ssh_channel *c = (struct ssh_channel *)ss.element;
- if (c->localid == ss.index + CHANNEL_NUMBER_OFFSET)
- search234_step(&ss, +1);
- else
- search234_step(&ss, -1);
- }
-
- /*
- * Now ss.index gives exactly the number of channels in that
- * initial sequence. So adding CHANNEL_NUMBER_OFFSET to it must
- * give precisely the lowest unused channel number.
- */
- return ss.index + CHANNEL_NUMBER_OFFSET;
-}
-
static void c_write_stderr(int trusted, const void *vbuf, int len)
{
const char *buf = (const char *)vbuf;
@@ -5676,7 +5644,7 @@ static int ssh_is_simple(Ssh ssh)
static void ssh_channel_init(struct ssh_channel *c)
{
Ssh ssh = c->ssh;
- c->localid = alloc_channel_id(ssh);
+ c->localid = alloc_channel_id(ssh->channels, struct ssh_channel);
c->closes = 0;
c->pending_eof = FALSE;
c->throttling_conn = FALSE;
diff --git a/ssh.h b/ssh.h
index bfeb20df..fc61aa31 100644
--- a/ssh.h
+++ b/ssh.h
@@ -1326,3 +1326,11 @@ enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_REAL_ENUM) };
void write_ttymodes_to_packet_from_conf(
BinarySink *bs, Frontend *frontend, Conf *conf,
int ssh_version, int ospeed, int ispeed);
+
+/* Shared system for allocating local SSH channel ids. Expects to be
+ * passed a tree full of structs that have a field called 'localid' of
+ * type unsigned, and will check that! */
+unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset);
+#define alloc_channel_id(tree, type) \
+ TYPECHECK(&((type *)0)->localid == (unsigned *)0, \
+ alloc_channel_id_general(tree, offsetof(type, localid)))
diff --git a/sshcommon.c b/sshcommon.c
index 024b64c7..28f28cf9 100644
--- a/sshcommon.c
+++ b/sshcommon.c
@@ -466,3 +466,40 @@ void write_ttymodes_to_packet_from_conf(
put_uint32(bs, ospeed);
put_byte(bs, SSH_TTY_OP_END);
}
+
+/* ----------------------------------------------------------------------
+ * Routine for allocating a new channel ID, given a means of finding
+ * the index field in a given channel structure.
+ */
+
+unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset)
+{
+ const unsigned CHANNEL_NUMBER_OFFSET = 256;
+ search234_state ss;
+
+ /*
+ * First-fit allocation of channel numbers: we always pick the
+ * lowest unused one.
+ *
+ * Every channel before that, and no channel after it, has an ID
+ * exactly equal to its tree index plus CHANNEL_NUMBER_OFFSET. So
+ * we can use the search234 system to identify the length of that
+ * initial sequence, in a single log-time pass down the channels
+ * tree.
+ */
+ search234_start(&ss, channels);
+ while (ss.element) {
+ unsigned localid = *(unsigned *)((char *)ss.element + localid_offset);
+ if (localid == ss.index + CHANNEL_NUMBER_OFFSET)
+ search234_step(&ss, +1);
+ else
+ search234_step(&ss, -1);
+ }
+
+ /*
+ * Now ss.index gives exactly the number of channels in that
+ * initial sequence. So adding CHANNEL_NUMBER_OFFSET to it must
+ * give precisely the lowest unused channel number.
+ */
+ return ss.index + CHANNEL_NUMBER_OFFSET;
+}
From 26364bb6a125ff9631ca350d8feae3fecb25c85e Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 20:39:25 +0100
Subject: [PATCH 427/607] Move comma-separated string functions into
sshcommon.c.
These are just string handling, after all. They could even move into
misc.c if any non-SSH-related code ever had a need for them.
---
ssh.c | 53 -----------------------------------------------------
ssh.h | 5 +++++
sshcommon.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 56 insertions(+), 53 deletions(-)
diff --git a/ssh.c b/ssh.c
index d5f203b0..a8d93ad1 100644
--- a/ssh.c
+++ b/ssh.c
@@ -3464,59 +3464,6 @@ static void ssh1_protocol_setup(Ssh ssh)
ssh->packet_dispatch[SSH1_MSG_DEBUG] = ssh1_msg_debug;
}
-/*
- * Utility routines for decoding comma-separated strings in KEXINIT.
- */
-static int first_in_commasep_string(char const *needle, char const *haystack,
- int haylen)
-{
- int needlen;
- if (!needle || !haystack) /* protect against null pointers */
- return 0;
- needlen = strlen(needle);
-
- if (haylen >= needlen && /* haystack is long enough */
- !memcmp(needle, haystack, needlen) && /* initial match */
- (haylen == needlen || haystack[needlen] == ',')
- /* either , or EOS follows */
- )
- return 1;
- return 0;
-}
-
-static int in_commasep_string(char const *needle, char const *haystack,
- int haylen)
-{
- char *p;
-
- if (!needle || !haystack) /* protect against null pointers */
- return 0;
- /*
- * Is it at the start of the string?
- */
- if (first_in_commasep_string(needle, haystack, haylen))
- return 1;
- /*
- * If not, search for the next comma and resume after that.
- * If no comma found, terminate.
- */
- p = memchr(haystack, ',', haylen);
- if (!p) return 0;
- /* + 1 to skip over comma */
- return in_commasep_string(needle, p + 1, haylen - (p + 1 - haystack));
-}
-
-/*
- * Add a value to a strbuf containing a comma-separated list.
- */
-static void add_to_commasep(strbuf *buf, const char *data)
-{
- if (buf->len > 0)
- put_byte(buf, ',');
- put_data(buf, data, strlen(data));
-}
-
-
/*
* SSH-2 key derivation (RFC 4253 section 7.2).
*/
diff --git a/ssh.h b/ssh.h
index fc61aa31..16224330 100644
--- a/ssh.h
+++ b/ssh.h
@@ -1334,3 +1334,8 @@ unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset);
#define alloc_channel_id(tree, type) \
TYPECHECK(&((type *)0)->localid == (unsigned *)0, \
alloc_channel_id_general(tree, offsetof(type, localid)))
+
+int first_in_commasep_string(char const *needle, char const *haystack,
+ int haylen);
+int in_commasep_string(char const *needle, char const *haystack, int haylen);
+void add_to_commasep(strbuf *buf, const char *data);
diff --git a/sshcommon.c b/sshcommon.c
index 28f28cf9..371d6c30 100644
--- a/sshcommon.c
+++ b/sshcommon.c
@@ -503,3 +503,54 @@ unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset)
*/
return ss.index + CHANNEL_NUMBER_OFFSET;
}
+
+/* ----------------------------------------------------------------------
+ * Functions for handling the comma-separated strings used to store
+ * lists of protocol identifiers in SSH-2.
+ */
+
+int first_in_commasep_string(char const *needle, char const *haystack,
+ int haylen)
+{
+ int needlen;
+ if (!needle || !haystack) /* protect against null pointers */
+ return 0;
+ needlen = strlen(needle);
+
+ if (haylen >= needlen && /* haystack is long enough */
+ !memcmp(needle, haystack, needlen) && /* initial match */
+ (haylen == needlen || haystack[needlen] == ',')
+ /* either , or EOS follows */
+ )
+ return 1;
+ return 0;
+}
+
+int in_commasep_string(char const *needle, char const *haystack, int haylen)
+{
+ char *p;
+
+ if (!needle || !haystack) /* protect against null pointers */
+ return FALSE;
+ /*
+ * Is it at the start of the string?
+ */
+ if (first_in_commasep_string(needle, haystack, haylen))
+ return TRUE;
+ /*
+ * If not, search for the next comma and resume after that.
+ * If no comma found, terminate.
+ */
+ p = memchr(haystack, ',', haylen);
+ if (!p)
+ return FALSE;
+ /* + 1 to skip over comma */
+ return in_commasep_string(needle, p + 1, haylen - (p + 1 - haystack));
+}
+
+void add_to_commasep(strbuf *buf, const char *data)
+{
+ if (buf->len > 0)
+ put_byte(buf, ',');
+ put_data(buf, data, strlen(data));
+}
From 3ad919f9e921472ab5b298cd456c82cfcc07eb4b Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 20:40:21 +0100
Subject: [PATCH 428/607] Move ssh{1,2}_pkt_type into sshcommon.c.
These are already called from multiple places to translate packet type
codes into text, so let's put them somewhere nicely central.
---
ssh.c | 110 -------------------------------------------------
sshcommon.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 115 insertions(+), 110 deletions(-)
diff --git a/ssh.c b/ssh.c
index a8d93ad1..5cc1373c 100644
--- a/ssh.c
+++ b/ssh.c
@@ -58,116 +58,6 @@ static unsigned long rekey_mins(int rekey_time, unsigned long def)
return (unsigned long)rekey_time;
}
-#define translate(x) if (type == x) return #x
-#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
-#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
-const char *ssh1_pkt_type(int type)
-{
- translate(SSH1_MSG_DISCONNECT);
- translate(SSH1_SMSG_PUBLIC_KEY);
- translate(SSH1_CMSG_SESSION_KEY);
- translate(SSH1_CMSG_USER);
- translate(SSH1_CMSG_AUTH_RSA);
- translate(SSH1_SMSG_AUTH_RSA_CHALLENGE);
- translate(SSH1_CMSG_AUTH_RSA_RESPONSE);
- translate(SSH1_CMSG_AUTH_PASSWORD);
- translate(SSH1_CMSG_REQUEST_PTY);
- translate(SSH1_CMSG_WINDOW_SIZE);
- translate(SSH1_CMSG_EXEC_SHELL);
- translate(SSH1_CMSG_EXEC_CMD);
- translate(SSH1_SMSG_SUCCESS);
- translate(SSH1_SMSG_FAILURE);
- translate(SSH1_CMSG_STDIN_DATA);
- translate(SSH1_SMSG_STDOUT_DATA);
- translate(SSH1_SMSG_STDERR_DATA);
- translate(SSH1_CMSG_EOF);
- translate(SSH1_SMSG_EXIT_STATUS);
- translate(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
- translate(SSH1_MSG_CHANNEL_OPEN_FAILURE);
- translate(SSH1_MSG_CHANNEL_DATA);
- translate(SSH1_MSG_CHANNEL_CLOSE);
- translate(SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION);
- translate(SSH1_SMSG_X11_OPEN);
- translate(SSH1_CMSG_PORT_FORWARD_REQUEST);
- translate(SSH1_MSG_PORT_OPEN);
- translate(SSH1_CMSG_AGENT_REQUEST_FORWARDING);
- translate(SSH1_SMSG_AGENT_OPEN);
- translate(SSH1_MSG_IGNORE);
- translate(SSH1_CMSG_EXIT_CONFIRMATION);
- translate(SSH1_CMSG_X11_REQUEST_FORWARDING);
- translate(SSH1_CMSG_AUTH_RHOSTS_RSA);
- translate(SSH1_MSG_DEBUG);
- translate(SSH1_CMSG_REQUEST_COMPRESSION);
- translate(SSH1_CMSG_AUTH_TIS);
- translate(SSH1_SMSG_AUTH_TIS_CHALLENGE);
- translate(SSH1_CMSG_AUTH_TIS_RESPONSE);
- translate(SSH1_CMSG_AUTH_CCARD);
- translate(SSH1_SMSG_AUTH_CCARD_CHALLENGE);
- translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
- return "unknown";
-}
-const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
-{
- translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
- translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
- translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI);
- translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI);
- translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI);
- translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI);
- translate(SSH2_MSG_DISCONNECT);
- translate(SSH2_MSG_IGNORE);
- translate(SSH2_MSG_UNIMPLEMENTED);
- translate(SSH2_MSG_DEBUG);
- translate(SSH2_MSG_SERVICE_REQUEST);
- translate(SSH2_MSG_SERVICE_ACCEPT);
- translate(SSH2_MSG_KEXINIT);
- translate(SSH2_MSG_NEWKEYS);
- translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);
- translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);
- translatek(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, SSH2_PKTCTX_DHGEX);
- translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
- translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
- translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
- translatek(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
- translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
- translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
- translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
- translatek(SSH2_MSG_KEX_ECDH_INIT, SSH2_PKTCTX_ECDHKEX);
- translatek(SSH2_MSG_KEX_ECDH_REPLY, SSH2_PKTCTX_ECDHKEX);
- translatek(SSH2_MSG_KEXGSS_INIT, SSH2_PKTCTX_GSSKEX);
- translatek(SSH2_MSG_KEXGSS_CONTINUE, SSH2_PKTCTX_GSSKEX);
- translatek(SSH2_MSG_KEXGSS_COMPLETE, SSH2_PKTCTX_GSSKEX);
- translatek(SSH2_MSG_KEXGSS_HOSTKEY, SSH2_PKTCTX_GSSKEX);
- translatek(SSH2_MSG_KEXGSS_ERROR, SSH2_PKTCTX_GSSKEX);
- translatek(SSH2_MSG_KEXGSS_GROUPREQ, SSH2_PKTCTX_GSSKEX);
- translatek(SSH2_MSG_KEXGSS_GROUP, SSH2_PKTCTX_GSSKEX);
- translate(SSH2_MSG_USERAUTH_REQUEST);
- translate(SSH2_MSG_USERAUTH_FAILURE);
- translate(SSH2_MSG_USERAUTH_SUCCESS);
- translate(SSH2_MSG_USERAUTH_BANNER);
- translatea(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);
- translatea(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);
- translatea(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);
- translatea(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);
- translate(SSH2_MSG_GLOBAL_REQUEST);
- translate(SSH2_MSG_REQUEST_SUCCESS);
- translate(SSH2_MSG_REQUEST_FAILURE);
- translate(SSH2_MSG_CHANNEL_OPEN);
- translate(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
- translate(SSH2_MSG_CHANNEL_OPEN_FAILURE);
- translate(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
- translate(SSH2_MSG_CHANNEL_DATA);
- translate(SSH2_MSG_CHANNEL_EXTENDED_DATA);
- translate(SSH2_MSG_CHANNEL_EOF);
- translate(SSH2_MSG_CHANNEL_CLOSE);
- translate(SSH2_MSG_CHANNEL_REQUEST);
- translate(SSH2_MSG_CHANNEL_SUCCESS);
- translate(SSH2_MSG_CHANNEL_FAILURE);
- return "unknown";
-}
-#undef translate
-#undef translatec
-
/* Enumeration values for fields in SSH-1 packets */
enum {
PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM,
diff --git a/sshcommon.c b/sshcommon.c
index 371d6c30..b98c9a62 100644
--- a/sshcommon.c
+++ b/sshcommon.c
@@ -554,3 +554,118 @@ void add_to_commasep(strbuf *buf, const char *data)
put_byte(buf, ',');
put_data(buf, data, strlen(data));
}
+
+/* ----------------------------------------------------------------------
+ * Functions for translating SSH packet type codes into their symbolic
+ * string names.
+ */
+
+#define translate(x) if (type == x) return #x
+#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
+#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
+const char *ssh1_pkt_type(int type)
+{
+ translate(SSH1_MSG_DISCONNECT);
+ translate(SSH1_SMSG_PUBLIC_KEY);
+ translate(SSH1_CMSG_SESSION_KEY);
+ translate(SSH1_CMSG_USER);
+ translate(SSH1_CMSG_AUTH_RSA);
+ translate(SSH1_SMSG_AUTH_RSA_CHALLENGE);
+ translate(SSH1_CMSG_AUTH_RSA_RESPONSE);
+ translate(SSH1_CMSG_AUTH_PASSWORD);
+ translate(SSH1_CMSG_REQUEST_PTY);
+ translate(SSH1_CMSG_WINDOW_SIZE);
+ translate(SSH1_CMSG_EXEC_SHELL);
+ translate(SSH1_CMSG_EXEC_CMD);
+ translate(SSH1_SMSG_SUCCESS);
+ translate(SSH1_SMSG_FAILURE);
+ translate(SSH1_CMSG_STDIN_DATA);
+ translate(SSH1_SMSG_STDOUT_DATA);
+ translate(SSH1_SMSG_STDERR_DATA);
+ translate(SSH1_CMSG_EOF);
+ translate(SSH1_SMSG_EXIT_STATUS);
+ translate(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
+ translate(SSH1_MSG_CHANNEL_OPEN_FAILURE);
+ translate(SSH1_MSG_CHANNEL_DATA);
+ translate(SSH1_MSG_CHANNEL_CLOSE);
+ translate(SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION);
+ translate(SSH1_SMSG_X11_OPEN);
+ translate(SSH1_CMSG_PORT_FORWARD_REQUEST);
+ translate(SSH1_MSG_PORT_OPEN);
+ translate(SSH1_CMSG_AGENT_REQUEST_FORWARDING);
+ translate(SSH1_SMSG_AGENT_OPEN);
+ translate(SSH1_MSG_IGNORE);
+ translate(SSH1_CMSG_EXIT_CONFIRMATION);
+ translate(SSH1_CMSG_X11_REQUEST_FORWARDING);
+ translate(SSH1_CMSG_AUTH_RHOSTS_RSA);
+ translate(SSH1_MSG_DEBUG);
+ translate(SSH1_CMSG_REQUEST_COMPRESSION);
+ translate(SSH1_CMSG_AUTH_TIS);
+ translate(SSH1_SMSG_AUTH_TIS_CHALLENGE);
+ translate(SSH1_CMSG_AUTH_TIS_RESPONSE);
+ translate(SSH1_CMSG_AUTH_CCARD);
+ translate(SSH1_SMSG_AUTH_CCARD_CHALLENGE);
+ translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
+ return "unknown";
+}
+const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
+{
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI);
+ translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI);
+ translate(SSH2_MSG_DISCONNECT);
+ translate(SSH2_MSG_IGNORE);
+ translate(SSH2_MSG_UNIMPLEMENTED);
+ translate(SSH2_MSG_DEBUG);
+ translate(SSH2_MSG_SERVICE_REQUEST);
+ translate(SSH2_MSG_SERVICE_ACCEPT);
+ translate(SSH2_MSG_KEXINIT);
+ translate(SSH2_MSG_NEWKEYS);
+ translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);
+ translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);
+ translatek(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, SSH2_PKTCTX_DHGEX);
+ translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
+ translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
+ translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
+ translatek(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
+ translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
+ translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
+ translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
+ translatek(SSH2_MSG_KEX_ECDH_INIT, SSH2_PKTCTX_ECDHKEX);
+ translatek(SSH2_MSG_KEX_ECDH_REPLY, SSH2_PKTCTX_ECDHKEX);
+ translatek(SSH2_MSG_KEXGSS_INIT, SSH2_PKTCTX_GSSKEX);
+ translatek(SSH2_MSG_KEXGSS_CONTINUE, SSH2_PKTCTX_GSSKEX);
+ translatek(SSH2_MSG_KEXGSS_COMPLETE, SSH2_PKTCTX_GSSKEX);
+ translatek(SSH2_MSG_KEXGSS_HOSTKEY, SSH2_PKTCTX_GSSKEX);
+ translatek(SSH2_MSG_KEXGSS_ERROR, SSH2_PKTCTX_GSSKEX);
+ translatek(SSH2_MSG_KEXGSS_GROUPREQ, SSH2_PKTCTX_GSSKEX);
+ translatek(SSH2_MSG_KEXGSS_GROUP, SSH2_PKTCTX_GSSKEX);
+ translate(SSH2_MSG_USERAUTH_REQUEST);
+ translate(SSH2_MSG_USERAUTH_FAILURE);
+ translate(SSH2_MSG_USERAUTH_SUCCESS);
+ translate(SSH2_MSG_USERAUTH_BANNER);
+ translatea(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);
+ translatea(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);
+ translatea(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);
+ translatea(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);
+ translate(SSH2_MSG_GLOBAL_REQUEST);
+ translate(SSH2_MSG_REQUEST_SUCCESS);
+ translate(SSH2_MSG_REQUEST_FAILURE);
+ translate(SSH2_MSG_CHANNEL_OPEN);
+ translate(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+ translate(SSH2_MSG_CHANNEL_OPEN_FAILURE);
+ translate(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ translate(SSH2_MSG_CHANNEL_DATA);
+ translate(SSH2_MSG_CHANNEL_EXTENDED_DATA);
+ translate(SSH2_MSG_CHANNEL_EOF);
+ translate(SSH2_MSG_CHANNEL_CLOSE);
+ translate(SSH2_MSG_CHANNEL_REQUEST);
+ translate(SSH2_MSG_CHANNEL_SUCCESS);
+ translate(SSH2_MSG_CHANNEL_FAILURE);
+ return "unknown";
+}
+#undef translate
+#undef translatec
From 93f2df9b837194e678449947d8cf73517e87165c Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Wed, 19 Sep 2018 21:28:21 +0100
Subject: [PATCH 429/607] New system for tracking data-limit-based rekeys.
I've removed the encrypted_len fields from PktIn and PktOut, which
were used to communicate from the BPP to ssh.c how much each packet
contributed to the amount of data encrypted with a given set of cipher
keys. It seems more sensible to have the BPP itself keep that counter
- especially since only one of the three BPPs even needs to count it
at all. So now there's a new DataTransferStats structure which the BPP
updates, and ssh.c only needs to check it for overflow and reset the
limits.
---
ssh.c | 49 ++++++++++++++++++++++++++++++++-----------------
ssh.h | 2 --
ssh2bpp-bare.c | 2 --
ssh2bpp.c | 10 +++++++---
sshbpp.h | 27 ++++++++++++++++++++++++++-
5 files changed, 65 insertions(+), 25 deletions(-)
diff --git a/ssh.c b/ssh.c
index 5cc1373c..e3dba149 100644
--- a/ssh.c
+++ b/ssh.c
@@ -559,8 +559,8 @@ struct ssh_tag {
* Track incoming and outgoing data sizes and time, for
* size-based rekeys.
*/
- unsigned long incoming_data_size, outgoing_data_size;
unsigned long max_data_size;
+ struct DataTransferStats stats;
int kex_in_progress;
unsigned long next_rekey, last_rekey;
const char *deferred_rekey_reason;
@@ -781,10 +781,9 @@ static void ssh_send_outgoing_data(void *ctx)
backlog = s_write(ssh, data, len);
bufchain_consume(&ssh->outgoing_data, len);
- ssh->outgoing_data_size += len;
if (ssh->version == 2 && !ssh->kex_in_progress &&
- !ssh->bare_connection && ssh->max_data_size != 0 &&
- ssh->outgoing_data_size > ssh->max_data_size) {
+ ssh->state != SSH_STATE_PREPACKET &&
+ !ssh->bare_connection && !ssh->stats.out.running) {
ssh->rekey_reason = "too much data sent";
ssh->rekey_class = RK_NORMAL;
queue_idempotent_callback(&ssh->ssh2_transport_icb);
@@ -5131,7 +5130,9 @@ static void do_ssh2_transport(void *vctx)
*/
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_NEWKEYS);
ssh_pkt_write(ssh, s->pktout);
- ssh->outgoing_data_size = 0; /* start counting from here */
+ /* Start counting down the outgoing-data limit for these cipher keys. */
+ ssh->stats.out.running = TRUE;
+ ssh->stats.out.remaining = ssh->max_data_size;
/*
* We've sent client NEWKEYS, so create and initialise
@@ -5204,7 +5205,9 @@ static void do_ssh2_transport(void *vctx)
bombout(("expected new-keys packet from server"));
crStopV;
}
- ssh->incoming_data_size = 0; /* start counting from here */
+ /* Start counting down the incoming-data limit for these cipher keys. */
+ ssh->stats.in.running = TRUE;
+ ssh->stats.in.remaining = ssh->max_data_size;
/*
* We've seen server NEWKEYS, so create and initialise
@@ -5363,8 +5366,9 @@ static void do_ssh2_transport(void *vctx)
ssh->rekey_reason);
/* Reset the counters, so that at least this message doesn't
* hit the event log _too_ often. */
- ssh->outgoing_data_size = 0;
- ssh->incoming_data_size = 0;
+ ssh->stats.in.running = ssh->stats.out.running = TRUE;
+ ssh->stats.in.remaining = ssh->stats.out.remaining =
+ ssh->max_data_size;
(void) ssh2_timer_update(ssh, 0);
goto wait_for_rekey; /* this is still utterly horrid */
} else {
@@ -8649,7 +8653,7 @@ static void ssh2_protocol_setup(Ssh ssh)
{
int i;
- ssh->bpp = ssh2_bpp_new();
+ ssh->bpp = ssh2_bpp_new(&ssh->stats);
#ifndef NO_GSSAPI
/* Load and pick the highest GSS library on the preference list. */
@@ -9055,10 +9059,8 @@ static void ssh2_timer(void *ctx, unsigned long now)
static void ssh2_general_packet_processing(Ssh ssh, PktIn *pktin)
{
- ssh->incoming_data_size += pktin->encrypted_len;
- if (!ssh->kex_in_progress &&
- ssh->max_data_size != 0 &&
- ssh->incoming_data_size > ssh->max_data_size) {
+ if (!ssh->kex_in_progress && ssh->max_data_size != 0 &&
+ ssh->state != SSH_STATE_PREPACKET && !ssh->stats.in.running) {
ssh->rekey_reason = "too much data received";
ssh->rekey_class = RK_NORMAL;
queue_idempotent_callback(&ssh->ssh2_transport_icb);
@@ -9213,7 +9215,7 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
ssh->pinger = NULL;
- ssh->incoming_data_size = ssh->outgoing_data_size = 0L;
+ memset(&ssh->stats, 0, sizeof(ssh->stats));
ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf,
CONF_ssh_rekey_data));
ssh->kex_in_progress = FALSE;
@@ -9371,9 +9373,22 @@ static void ssh_reconfig(Backend *be, Conf *conf)
CONF_ssh_rekey_data));
if (old_max_data_size != ssh->max_data_size &&
ssh->max_data_size != 0) {
- if (ssh->outgoing_data_size > ssh->max_data_size ||
- ssh->incoming_data_size > ssh->max_data_size)
- rekeying = "data limit lowered";
+ if (ssh->max_data_size < old_max_data_size) {
+ unsigned long diff = old_max_data_size - ssh->max_data_size;
+
+ /* Intentionally use bitwise OR instead of logical, so
+ * that we decrement both counters even if the first one
+ * runs out */
+ if ((DTS_CONSUME(&ssh->stats, out, diff) != 0) |
+ (DTS_CONSUME(&ssh->stats, in, diff) != 0))
+ rekeying = "data limit lowered";
+ } else {
+ unsigned long diff = ssh->max_data_size - old_max_data_size;
+ if (ssh->stats.out.running)
+ ssh->stats.out.remaining += diff;
+ if (ssh->stats.in.running)
+ ssh->stats.in.remaining += diff;
+ }
}
if (conf_get_int(ssh->conf, CONF_compression) !=
diff --git a/ssh.h b/ssh.h
index 16224330..fd8d1677 100644
--- a/ssh.h
+++ b/ssh.h
@@ -59,7 +59,6 @@ typedef struct PktIn {
int refcount;
int type;
unsigned long sequence; /* SSH-2 incoming sequence number */
- long encrypted_len; /* for SSH-2 total-size counting */
PacketQueueNode qnode; /* for linking this packet on to a queue */
BinarySource_IMPLEMENTATION;
} PktIn;
@@ -71,7 +70,6 @@ typedef struct PktOut {
long minlen; /* SSH-2: ensure wire length is at least this */
unsigned char *data; /* allocated storage */
long maxlen; /* amount of storage allocated for `data' */
- long encrypted_len; /* for SSH-2 total-size counting */
/* Extra metadata used in SSH packet logging mode, allowing us to
* log in the packet header line that the packet came from a
diff --git a/ssh2bpp-bare.c b/ssh2bpp-bare.c
index bdf49a21..e16c8509 100644
--- a/ssh2bpp-bare.c
+++ b/ssh2bpp-bare.c
@@ -79,8 +79,6 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
s->pktin->refcount = 1;
s->data = snew_plus_get_aux(s->pktin);
- s->pktin->encrypted_len = s->packetlen;
-
s->pktin->sequence = s->incoming_sequence++;
/*
diff --git a/ssh2bpp.c b/ssh2bpp.c
index 0b95d7fe..2b7f4654 100644
--- a/ssh2bpp.c
+++ b/ssh2bpp.c
@@ -24,6 +24,7 @@ struct ssh2_bpp_state {
unsigned char *data;
unsigned cipherblk;
PktIn *pktin;
+ struct DataTransferStats *stats;
struct ssh2_bpp_direction in, out;
/* comp and decomp logically belong in the per-direction
@@ -48,11 +49,12 @@ const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = {
ssh2_bpp_format_packet,
};
-BinaryPacketProtocol *ssh2_bpp_new(void)
+BinaryPacketProtocol *ssh2_bpp_new(struct DataTransferStats *stats)
{
struct ssh2_bpp_state *s = snew(struct ssh2_bpp_state);
memset(s, 0, sizeof(*s));
s->bpp.vt = &ssh2_bpp_vtable;
+ s->stats = stats;
return &s->bpp;
}
@@ -407,7 +409,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
s->payload = s->len - s->pad - 1;
s->length = s->payload + 5;
- s->pktin->encrypted_len = s->packetlen;
+
+ DTS_CONSUME(s->stats, in, s->packetlen);
s->pktin->sequence = s->in.sequence++;
@@ -601,7 +604,8 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
}
s->out.sequence++; /* whether or not we MACed */
- pkt->encrypted_len = origlen + padding;
+
+ DTS_CONSUME(s->stats, out, origlen + padding);
}
diff --git a/sshbpp.h b/sshbpp.h
index 953add3a..a1c9d0ac 100644
--- a/sshbpp.h
+++ b/sshbpp.h
@@ -36,7 +36,32 @@ void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
const void *session_key);
void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
-BinaryPacketProtocol *ssh2_bpp_new(void);
+/*
+ * Structure that tracks how much data is sent and received, for
+ * purposes of triggering an SSH-2 rekey when either one gets over a
+ * configured limit. In each direction, the flag 'running' indicates
+ * that we haven't hit the limit yet, and 'remaining' tracks how much
+ * longer until we do. The macro DTS_CONSUME subtracts a given amount
+ * from the counter in a particular direction, and evaluates to a
+ * boolean indicating whether the limit has been hit.
+ *
+ * The limit is sticky: once 'running' has flipped to false,
+ * 'remaining' is no longer decremented, so it shouldn't dangerously
+ * wrap round.
+ */
+struct DataTransferStats {
+ struct {
+ int running;
+ unsigned long remaining;
+ } in, out;
+};
+#define DTS_CONSUME(stats, direction, size) \
+ ((stats)->direction.running && \
+ (stats)->direction.remaining <= (size) ? \
+ ((stats)->direction.running = FALSE, TRUE) : \
+ ((stats)->direction.remaining -= (size), FALSE))
+
+BinaryPacketProtocol *ssh2_bpp_new(struct DataTransferStats *stats);
void ssh2_bpp_new_outgoing_crypto(
BinaryPacketProtocol *bpp,
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
From 91a624fb70230a885656e74c89865270b27c9de9 Mon Sep 17 00:00:00 2001
From: Simon Tatham
Date: Thu, 20 Sep 2018 16:58:43 +0100
Subject: [PATCH 430/607] sshaes.c: add some missing clang target attributes.
The helper functions mm_shuffle_pd_i0 and mm_shuffle_pd_i1 need the
FUNC_ISA macro (which expands to __attribute__((target("sse4.1,aes")))
when building with clang) in order to avoid a build error complaining
that their use of the _mm_shuffle_pd intrinsic is illegal without at
least sse2.
This build error is new in the recently released clang 7.0.0, compared
to the svn trunk revision I was previously building with. But it
certainly seems plausible to me, so I assume there's been some
pre-release tightening up of the error reporting. In any case, those
helper functions are only ever called from other functions with the
same attribute, so it shouldn't cause trouble.
---
sshaes.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/sshaes.c b/sshaes.c
index 5259396f..c5c05d21 100644
--- a/sshaes.c
+++ b/sshaes.c
@@ -1243,6 +1243,7 @@ INLINE static int supports_aes_ni()
* Wrapper of SHUFPD instruction for MSVC
*/
#ifdef _MSC_VER
+FUNC_ISA
INLINE static __m128i mm_shuffle_pd_i0(__m128i a, __m128i b)
{
union {
@@ -1255,6 +1256,7 @@ INLINE static __m128i mm_shuffle_pd_i0(__m128i a, __m128i b)
return ru.i;
}
+FUNC_ISA
INLINE static __m128i mm_shuffle_pd_i1(__m128i a, __m128i b)
{
union {
From e71798a265a3e425c609dd464039319dfe33318f Mon Sep 17 00:00:00 2001
From: Simon Tatham