2016-03-23 22:22:30 +00:00
|
|
|
/*
|
2022-01-22 15:38:53 +00:00
|
|
|
* main-gtk-application.c: a top-level front end to GUI PuTTY and
|
|
|
|
* pterm, using GtkApplication. Suitable for OS X. Currently
|
|
|
|
* unfinished.
|
2016-03-23 22:22:30 +00:00
|
|
|
*
|
|
|
|
* (You could run it on ordinary Linux GTK too, in principle, but I
|
|
|
|
* don't think it would be particularly useful to do so, even once
|
|
|
|
* it's fully working.)
|
|
|
|
*/
|
|
|
|
|
2016-03-23 22:14:13 +00:00
|
|
|
/*
|
|
|
|
|
Replace mkfiles.pl with a CMake build system.
This brings various concrete advantages over the previous system:
- consistent support for out-of-tree builds on all platforms
- more thorough support for Visual Studio IDE project files
- support for Ninja-based builds, which is particularly useful on
Windows where the alternative nmake has no parallel option
- a really simple set of build instructions that work the same way on
all the major platforms (look how much shorter README is!)
- better decoupling of the project configuration from the toolchain
configuration, so that my Windows cross-building doesn't need
(much) special treatment in CMakeLists.txt
- configure-time tests on Windows as well as Linux, so that a lot of
ad-hoc #ifdefs second-guessing a particular feature's presence from
the compiler version can now be replaced by tests of the feature
itself
Also some longer-term software-engineering advantages:
- other people have actually heard of CMake, so they'll be able to
produce patches to the new build setup more easily
- unlike the old mkfiles.pl, CMake is not my personal problem to
maintain
- most importantly, mkfiles.pl was just a horrible pile of
unmaintainable cruft, which even I found it painful to make changes
to or to use, and desperately needed throwing in the bin. I've
already thrown away all the variants of it I had in other projects
of mine, and was only delaying this one so we could make the 0.75
release branch first.
This change comes with a noticeable build-level restructuring. The
previous Recipe worked by compiling every object file exactly once,
and then making each executable by linking a precisely specified
subset of the same object files. But in CMake, that's not the natural
way to work - if you write the obvious command that puts the same
source file into two executable targets, CMake generates a makefile
that compiles it once per target. That can be an advantage, because it
gives you the freedom to compile it differently in each case (e.g.
with a #define telling it which program it's part of). But in a
project that has many executable targets and had carefully contrived
to _never_ need to build any module more than once, all it does is
bloat the build time pointlessly!
To avoid slowing down the build by a large factor, I've put most of
the modules of the code base into a collection of static libraries
organised vaguely thematically (SSH, other backends, crypto, network,
...). That means all those modules can still be compiled just once
each, because once each library is built it's reused unchanged for all
the executable targets.
One upside of this library-based structure is that now I don't have to
manually specify exactly which objects go into which programs any more
- it's enough to specify which libraries are needed, and the linker
will figure out the fine detail automatically. So there's less
maintenance to do in CMakeLists.txt when the source code changes.
But that reorganisation also adds fragility, because of the trad Unix
linker semantics of walking along the library list once each, so that
cyclic references between your libraries will provoke link errors. The
current setup builds successfully, but I suspect it only just manages
it.
(In particular, I've found that MinGW is the most finicky on this
score of the Windows compilers I've tried building with. So I've
included a MinGW test build in the new-look Buildscr, because
otherwise I think there'd be a significant risk of introducing
MinGW-only build failures due to library search order, which wasn't a
risk in the previous library-free build organisation.)
In the longer term I hope to be able to reduce the risk of that, via
gradual reorganisation (in particular, breaking up too-monolithic
modules, to reduce the risk of knock-on references when you included a
module for function A and it also contains function B with an
unsatisfied dependency you didn't really need). Ideally I want to
reach a state in which the libraries all have sensibly described
purposes, a clearly documented (partial) order in which they're
permitted to depend on each other, and a specification of what stubs
you have to put where if you're leaving one of them out (e.g.
nocrypto) and what callbacks you have to define in your non-library
objects to satisfy dependencies from things low in the stack (e.g.
out_of_memory()).
One thing that's gone completely missing in this migration,
unfortunately, is the unfinished MacOS port linked against Quartz GTK.
That's because it turned out that I can't currently build it myself,
on my own Mac: my previous installation of GTK had bit-rotted as a
side effect of an Xcode upgrade, and I haven't yet been able to
persuade jhbuild to make me a new one. So I can't even build the MacOS
port with the _old_ makefiles, and hence, I have no way of checking
that the new ones also work. I hope to bring that port back to life at
some point, but I don't want it to block the rest of this change.
2021-04-10 14:21:11 +00:00
|
|
|
Building this for OS X is currently broken, because the new
|
|
|
|
CMake-based build system doesn't support it yet. Probably what needs
|
|
|
|
doing is to add it back in to unix/CMakeLists.txt under a condition
|
|
|
|
like if(CMAKE_SYSTEM_NAME MATCHES "Darwin").
|
2016-03-23 22:14:13 +00:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
2016-03-23 22:17:09 +00:00
|
|
|
/*
|
|
|
|
|
|
|
|
TODO list for a sensible GTK3 PuTTY/pterm on OS X:
|
|
|
|
|
2017-12-18 14:04:57 +00:00
|
|
|
Still to do on the application menu bar: items that have to vary with
|
|
|
|
context or user action (saved sessions and mid-session special
|
|
|
|
commands), and disabling/enabling the main actions in parallel with
|
|
|
|
their counterparts in the Ctrl-rightclick context menu.
|
|
|
|
|
2016-03-23 22:17:09 +00:00
|
|
|
Mouse wheel events and trackpad scrolling gestures don't work quite
|
2017-12-18 14:04:57 +00:00
|
|
|
right in the terminal drawing area. This seems to be a combination of
|
|
|
|
two things, neither of which I completely understand yet. Firstly, on
|
|
|
|
OS X GTK my trackpad seems to generate GDK scroll events for which
|
|
|
|
gdk_event_get_scroll_deltas returns integers rather than integer
|
|
|
|
multiples of 1/30, so we end up scrolling by very large amounts;
|
|
|
|
secondly, the window doesn't seem to receive a GTK "draw" event until
|
|
|
|
after the entire scroll gesture is complete, which means we don't get
|
|
|
|
constant visual feedback on how much we're scrolling by.
|
2016-03-23 22:17:09 +00:00
|
|
|
|
2017-12-18 14:04:57 +00:00
|
|
|
There doesn't seem to be a resize handle on terminal windows. Then
|
|
|
|
again, they do seem to _be_ resizable; the handle just isn't shown.
|
|
|
|
Perhaps that's a feature (certainly in a scrollbarless configuration
|
|
|
|
the handle gets in the way of the bottom right character cell in the
|
|
|
|
terminal itself), but it would be nice to at least understand _why_ it
|
|
|
|
happens and perhaps include an option to put it back again.
|
2016-03-23 22:17:09 +00:00
|
|
|
|
|
|
|
A slight oddity with menus that pop up directly under the mouse
|
|
|
|
pointer: mousing over the menu items doesn't highlight them initially,
|
|
|
|
but if I mouse off the menu and back on (without un-popping-it-up)
|
|
|
|
then suddenly that does work. I don't know if this is something I can
|
|
|
|
fix, though; it might very well be a quirk of the underlying GTK.
|
|
|
|
|
|
|
|
Does OS X have a standard system of online help that I could tie into?
|
|
|
|
|
|
|
|
Need to work out what if anything we can do with Pageant on OS X.
|
|
|
|
Perhaps it's too much bother and we should just talk to the
|
|
|
|
system-provided SSH agent? Or perhaps not.
|
|
|
|
|
|
|
|
Nice-to-have: a custom right-click menu from the application's dock
|
|
|
|
tile, listing the saved sessions for quick launch. As far as I know
|
|
|
|
there's nothing built in to GtkApplication that can produce this, but
|
|
|
|
it's possible we might be able to drop a piece of native Cocoa code in
|
|
|
|
under ifdef, substituting an application delegate of our own which
|
|
|
|
forwards all methods we're not interested in to the GTK-provided one?
|
|
|
|
|
|
|
|
At the point where this becomes polished enough to publish pre-built,
|
|
|
|
I suppose I'll have to look into OS X code signing.
|
|
|
|
https://wiki.gnome.org/Projects/GTK%2B/OSX/Bundling has some links.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2016-03-23 22:22:30 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
#define MAY_REFER_TO_GTK_IN_HEADERS
|
|
|
|
|
|
|
|
#include "putty.h"
|
Arm: turn on PSTATE.DIT if available and needed.
DIT, for 'Data-Independent Timing', is a bit you can set in the
processor state on sufficiently new Arm CPUs, which promises that a
long list of instructions will deliberately avoid varying their timing
based on the input register values. Just what you want for keeping
your constant-time crypto primitives constant-time.
As far as I'm aware, no CPU has _yet_ implemented any data-dependent
optimisations, so DIT is a safety precaution against them doing so in
future. It would be embarrassing to be caught without it if a future
CPU does do that, so we now turn on DIT in the PuTTY process state.
I've put a call to the new enable_dit() function at the start of every
main() and WinMain() belonging to a program that might do
cryptography (even testcrypt, in case someone uses it for something!),
and in case I missed one there, also added a second call at the first
moment that any cryptography-using part of the code looks as if it
might become active: when an instance of the SSH protocol object is
configured, when the system PRNG is initialised, and when selecting
any cryptographic authentication protocol in an HTTP or SOCKS proxy
connection. With any luck those precautions between them should ensure
it's on whenever we need it.
Arm's own recommendation is that you should carefully choose the
granularity at which you enable and disable DIT: there's a potential
time cost to turning it on and off (I'm not sure what, but plausibly
something of the order of a pipeline flush), so it's a performance hit
to do it _inside_ each individual crypto function, but if CPUs start
supporting significant data-dependent optimisation in future, then it
will also become a noticeable performance hit to just leave it on
across the whole process. So you'd like to do it somewhere in the
middle: for example, you might turn on DIT once around the whole
process of verifying and decrypting an SSH packet, instead of once for
decryption and once for MAC.
With all respect to that recommendation as a strategy for maximum
performance, I'm not following it here. I turn on DIT at the start of
the PuTTY process, and then leave it on. Rationale:
1. PuTTY is not otherwise a performance-critical application: it's
not likely to max out your CPU for any purpose _other_ than
cryptography. The most CPU-intensive non-cryptographic thing I can
imagine a PuTTY process doing is the complicated computation of
font rendering in the terminal, and that will normally be cached
(you don't recompute each glyph from its outline and hints for
every time you display it).
2. I think a bigger risk lies in accidental side channels from having
DIT turned off when it should have been on. I can imagine lots of
causes for that. Missing a crypto operation in some unswept corner
of the code; confusing control flow (like my coroutine macros)
jumping with DIT clear into the middle of a region of code that
expected DIT to have been set at the beginning; having a reference
counter of DIT requests and getting it out of sync.
In a more sophisticated programming language, it might be possible to
avoid the risk in #2 by cleverness with the type system. For example,
in Rust, you could have a zero-sized type that acts as a proof token
for DIT being enabled (it would be constructed by a function that also
sets DIT, have a Drop implementation that clears DIT, and be !Send so
you couldn't use it in a thread other than the one where DIT was set),
and then you could require all the actual crypto functions to take a
DitToken as an extra parameter, at zero runtime cost. Then "oops I
forgot to set DIT around this piece of crypto" would become a compile
error. Even so, you'd have to take some care with coroutine-structured
code (what happens if a Rust async function yields while holding a DIT
token?) and with nesting (if you have two DIT tokens, you don't want
dropping the inner one to clear DIT while the outer one is still there
to wrongly convince callees that it's set). Maybe in Rust you could
get this all to work reliably. But not in C!
DIT is an optional feature of the Arm architecture, so we must first
test to see if it's supported. This is done the same way as we already
do for the various Arm crypto accelerators: on ELF-based systems,
check the appropriate bit in the 'hwcap' words in the ELF aux vector;
on Mac, look for an appropriate sysctl flag.
On Windows I don't know of a way to query the DIT feature, _or_ of a
way to write the necessary enabling instruction in an MSVC-compatible
way. I've _heard_ that it might not be necessary, because Windows
might just turn on DIT unconditionally and leave it on, in an even
more extreme version of my own strategy. I don't have a source for
that - I heard it by word of mouth - but I _hope_ it's true, because
that would suit me very well! Certainly I can't write code to enable
DIT without knowing (a) how to do it, (b) how to know if it's safe.
Nonetheless, I've put the enable_dit() call in all the right places in
the Windows main programs as well as the Unix and cross-platform code,
so that if I later find out that I _can_ put in an explicit enable of
DIT in some way, I'll only have to arrange to set HAVE_ARM_DIT and
compile the enable_dit() function appropriately.
2024-12-19 08:47:08 +00:00
|
|
|
#include "ssh.h"
|
2017-11-27 20:09:54 +00:00
|
|
|
#include "gtkmisc.h"
|
2024-09-08 18:02:44 +00:00
|
|
|
#include "gtkcompat.h"
|
2016-03-23 22:22:30 +00:00
|
|
|
|
|
|
|
char *x_get_default(const char *key) { return NULL; }
|
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
const bool buildinfo_gtk_relevant = true;
|
2017-02-22 22:10:05 +00:00
|
|
|
|
2016-03-23 22:22:30 +00:00
|
|
|
#if !GTK_CHECK_VERSION(3,0,0)
|
2021-12-18 11:43:57 +00:00
|
|
|
#error This front end only works in GTK 3
|
|
|
|
#endif
|
2016-03-23 22:22:30 +00:00
|
|
|
|
|
|
|
static void startup(GApplication *app, gpointer user_data)
|
|
|
|
{
|
|
|
|
GMenu *menubar, *menu, *section;
|
|
|
|
|
|
|
|
menubar = g_menu_new();
|
|
|
|
|
|
|
|
menu = g_menu_new();
|
|
|
|
g_menu_append_submenu(menubar, "File", G_MENU_MODEL(menu));
|
|
|
|
|
|
|
|
section = g_menu_new();
|
|
|
|
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
|
|
|
|
g_menu_append(section, "New Window", "app.newwin");
|
|
|
|
|
|
|
|
menu = g_menu_new();
|
|
|
|
g_menu_append_submenu(menubar, "Edit", G_MENU_MODEL(menu));
|
|
|
|
|
|
|
|
section = g_menu_new();
|
|
|
|
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
|
2017-12-17 20:35:45 +00:00
|
|
|
g_menu_append(section, "Copy", "win.copy");
|
2016-03-23 22:22:30 +00:00
|
|
|
g_menu_append(section, "Paste", "win.paste");
|
2017-12-18 11:46:48 +00:00
|
|
|
g_menu_append(section, "Copy All", "win.copyall");
|
|
|
|
|
|
|
|
menu = g_menu_new();
|
|
|
|
g_menu_append_submenu(menubar, "Window", G_MENU_MODEL(menu));
|
|
|
|
|
|
|
|
section = g_menu_new();
|
|
|
|
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
|
|
|
|
g_menu_append(section, "Restart Session", "win.restart");
|
|
|
|
g_menu_append(section, "Duplicate Session", "win.duplicate");
|
|
|
|
|
|
|
|
section = g_menu_new();
|
|
|
|
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
|
|
|
|
g_menu_append(section, "Change Settings", "win.changesettings");
|
|
|
|
|
|
|
|
if (use_event_log) {
|
|
|
|
section = g_menu_new();
|
|
|
|
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
|
|
|
|
g_menu_append(section, "Event Log", "win.eventlog");
|
|
|
|
}
|
|
|
|
|
|
|
|
section = g_menu_new();
|
|
|
|
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
|
|
|
|
g_menu_append(section, "Clear Scrollback", "win.clearscrollback");
|
|
|
|
g_menu_append(section, "Reset Terminal", "win.resetterm");
|
2016-03-23 22:22:30 +00:00
|
|
|
|
2017-12-20 11:55:51 +00:00
|
|
|
#if GTK_CHECK_VERSION(3,12,0)
|
2017-12-18 11:01:42 +00:00
|
|
|
#define SET_ACCEL(app, command, accel) do \
|
|
|
|
{ \
|
|
|
|
static const char *const accels[] = { accel, NULL }; \
|
|
|
|
gtk_application_set_accels_for_action( \
|
|
|
|
GTK_APPLICATION(app), command, accels); \
|
|
|
|
} while (0)
|
2017-12-20 11:55:51 +00:00
|
|
|
#else
|
|
|
|
/* The Gtk function used above was new in 3.12; the one below
|
|
|
|
* was deprecated from 3.14. */
|
|
|
|
#define SET_ACCEL(app, command, accel) \
|
|
|
|
gtk_application_add_accelerator(GTK_APPLICATION(app), accel, \
|
|
|
|
command, NULL)
|
|
|
|
#endif
|
2017-12-18 11:01:42 +00:00
|
|
|
|
|
|
|
SET_ACCEL(app, "app.newwin", "<Primary>n");
|
|
|
|
SET_ACCEL(app, "win.copy", "<Primary>c");
|
|
|
|
SET_ACCEL(app, "win.paste", "<Primary>v");
|
|
|
|
|
|
|
|
#undef SET_ACCEL
|
|
|
|
|
2016-03-23 22:22:30 +00:00
|
|
|
gtk_application_set_menubar(GTK_APPLICATION(app),
|
|
|
|
G_MENU_MODEL(menubar));
|
|
|
|
}
|
|
|
|
|
2017-12-18 11:15:44 +00:00
|
|
|
#define WIN_ACTION_LIST(X) \
|
|
|
|
X("copy", MA_COPY) \
|
|
|
|
X("paste", MA_PASTE) \
|
2017-12-18 11:46:48 +00:00
|
|
|
X("copyall", MA_COPY_ALL) \
|
|
|
|
X("duplicate", MA_DUPLICATE_SESSION) \
|
|
|
|
X("restart", MA_RESTART_SESSION) \
|
|
|
|
X("changesettings", MA_CHANGE_SETTINGS) \
|
|
|
|
X("clearscrollback", MA_CLEAR_SCROLLBACK) \
|
|
|
|
X("resetterm", MA_RESET_TERMINAL) \
|
|
|
|
X("eventlog", MA_EVENT_LOG) \
|
2017-12-18 11:15:44 +00:00
|
|
|
/* end of list */
|
2017-12-17 20:35:45 +00:00
|
|
|
|
2017-12-18 11:15:44 +00:00
|
|
|
#define WIN_ACTION_CALLBACK(name, id) \
|
|
|
|
static void win_action_cb_ ## id(GSimpleAction *a, GVariant *p, gpointer d) \
|
|
|
|
{ app_menu_action(d, id); }
|
|
|
|
WIN_ACTION_LIST(WIN_ACTION_CALLBACK)
|
|
|
|
#undef WIN_ACTION_CALLBACK
|
2016-03-23 22:22:30 +00:00
|
|
|
|
|
|
|
static const GActionEntry win_actions[] = {
|
2017-12-18 11:15:44 +00:00
|
|
|
#define WIN_ACTION_ENTRY(name, id) { name, win_action_cb_ ## id },
|
|
|
|
WIN_ACTION_LIST(WIN_ACTION_ENTRY)
|
|
|
|
#undef WIN_ACTION_ENTRY
|
2016-03-23 22:22:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static GtkApplication *app;
|
Remove the 'Frontend' type and replace it with a vtable.
After the recent Seat and LogContext revamps, _nearly_ all the
remaining uses of the type 'Frontend' were in terminal.c, which needs
all sorts of interactions with the GUI window the terminal lives in,
from the obvious (actually drawing text on the window, reading and
writing the clipboard) to the obscure (minimising, maximising and
moving the window in response to particular escape sequences).
All of those functions are now provided by an abstraction called
TermWin. The few remaining uses of Frontend after _that_ are internal
to a particular platform directory, so as to spread the implementation
of that particular kind of Frontend between multiple source files; so
I've renamed all of those so that they take a more specifically named
type that refers to the particular implementation rather than the
general abstraction.
So now the name 'Frontend' no longer exists in the code base at all,
and everywhere one used to be used, it's completely clear whether it
was operating in one of Frontend's three abstract roles (and if so,
which), or whether it was specific to a particular implementation.
Another type that's disappeared is 'Context', which used to be a
typedef defined to something different on each platform, describing
whatever short-lived resources were necessary to draw on the terminal
window: the front end would provide a ready-made one when calling
term_paint, and the terminal could request one with get_ctx/free_ctx
if it wanted to do proactive window updates. Now that drawing context
lives inside the TermWin itself, because there was never any need to
have two of those contexts live at the same time.
(Another minor API change is that the window-title functions - both
reading and writing - have had a missing 'const' added to their char *
parameters / return values.)
I don't expect this change to enable any particularly interesting new
functionality (in particular, I have no plans that need more than one
implementation of TermWin in the same application). But it completes
the tidying-up that began with the Seat and LogContext rework.
2018-10-25 17:44:04 +00:00
|
|
|
GtkWidget *make_gtk_toplevel_window(GtkFrontend *frontend)
|
2016-03-23 22:22:30 +00:00
|
|
|
{
|
|
|
|
GtkWidget *win = gtk_application_window_new(app);
|
|
|
|
g_action_map_add_action_entries(G_ACTION_MAP(win),
|
|
|
|
win_actions,
|
|
|
|
G_N_ELEMENTS(win_actions),
|
|
|
|
frontend);
|
|
|
|
return win;
|
|
|
|
}
|
|
|
|
|
|
|
|
void launch_duplicate_session(Conf *conf)
|
|
|
|
{
|
2016-03-27 13:10:06 +00:00
|
|
|
assert(!dup_check_launchable || conf_launchable(conf));
|
2017-12-17 20:18:20 +00:00
|
|
|
g_application_hold(G_APPLICATION(app));
|
2017-11-25 21:51:45 +00:00
|
|
|
new_session_window(conf_copy(conf), NULL);
|
2016-03-23 22:22:30 +00:00
|
|
|
}
|
|
|
|
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
void session_window_closed(void)
|
2016-03-23 22:22:30 +00:00
|
|
|
{
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
g_application_release(G_APPLICATION(app));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void post_initial_config_box(void *vctx, int result)
|
|
|
|
{
|
|
|
|
Conf *conf = (Conf *)vctx;
|
|
|
|
|
2017-11-26 14:37:38 +00:00
|
|
|
if (result > 0) {
|
2016-03-23 22:22:30 +00:00
|
|
|
new_session_window(conf, NULL);
|
2017-11-26 14:37:38 +00:00
|
|
|
} else if (result == 0) {
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
conf_free(conf);
|
|
|
|
g_application_release(G_APPLICATION(app));
|
2016-03-23 22:22:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void launch_saved_session(const char *str)
|
|
|
|
{
|
|
|
|
Conf *conf = conf_new();
|
|
|
|
do_defaults(str, conf);
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
|
|
|
|
g_application_hold(G_APPLICATION(app));
|
|
|
|
|
|
|
|
if (!conf_launchable(conf)) {
|
|
|
|
initial_config_box(conf, post_initial_config_box, conf);
|
|
|
|
} else {
|
2016-03-23 22:22:30 +00:00
|
|
|
new_session_window(conf, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Make the configuration dialog non-modal.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
2017-11-26 11:58:02 +00:00
|
|
|
void launch_new_session(void)
|
|
|
|
{
|
|
|
|
/* Same as launch_saved_session except that we pass NULL to
|
|
|
|
* do_defaults. */
|
|
|
|
launch_saved_session(NULL);
|
|
|
|
}
|
|
|
|
|
2016-03-23 22:22:30 +00:00
|
|
|
void new_app_win(GtkApplication *app)
|
|
|
|
{
|
|
|
|
launch_new_session();
|
|
|
|
}
|
|
|
|
|
2017-11-27 20:09:54 +00:00
|
|
|
static void window_setup_error_callback(void *vctx, int result)
|
|
|
|
{
|
|
|
|
g_application_release(G_APPLICATION(app));
|
|
|
|
}
|
|
|
|
|
|
|
|
void window_setup_error(const char *errmsg)
|
|
|
|
{
|
|
|
|
create_message_box(NULL, "Error creating session window", errmsg,
|
|
|
|
string_width("Some sort of fiddly error message that "
|
|
|
|
"might be technical"),
|
2018-10-29 19:50:29 +00:00
|
|
|
true, &buttons_ok, window_setup_error_callback, NULL);
|
2017-11-27 20:09:54 +00:00
|
|
|
}
|
|
|
|
|
2016-03-23 22:22:30 +00:00
|
|
|
static void activate(GApplication *app,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
new_app_win(GTK_APPLICATION(app));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void newwin_cb(GSimpleAction *action,
|
|
|
|
GVariant *parameter,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
new_app_win(GTK_APPLICATION(user_data));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void quit_cb(GSimpleAction *action,
|
|
|
|
GVariant *parameter,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
g_application_quit(G_APPLICATION(user_data));
|
|
|
|
}
|
|
|
|
|
2017-12-18 11:46:48 +00:00
|
|
|
static void about_cb(GSimpleAction *action,
|
|
|
|
GVariant *parameter,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
about_box(NULL);
|
|
|
|
}
|
|
|
|
|
2016-03-23 22:22:30 +00:00
|
|
|
static const GActionEntry app_actions[] = {
|
|
|
|
{ "newwin", newwin_cb },
|
2017-12-18 11:46:48 +00:00
|
|
|
{ "about", about_cb },
|
2016-03-23 22:22:30 +00:00
|
|
|
{ "quit", quit_cb },
|
|
|
|
};
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
Arm: turn on PSTATE.DIT if available and needed.
DIT, for 'Data-Independent Timing', is a bit you can set in the
processor state on sufficiently new Arm CPUs, which promises that a
long list of instructions will deliberately avoid varying their timing
based on the input register values. Just what you want for keeping
your constant-time crypto primitives constant-time.
As far as I'm aware, no CPU has _yet_ implemented any data-dependent
optimisations, so DIT is a safety precaution against them doing so in
future. It would be embarrassing to be caught without it if a future
CPU does do that, so we now turn on DIT in the PuTTY process state.
I've put a call to the new enable_dit() function at the start of every
main() and WinMain() belonging to a program that might do
cryptography (even testcrypt, in case someone uses it for something!),
and in case I missed one there, also added a second call at the first
moment that any cryptography-using part of the code looks as if it
might become active: when an instance of the SSH protocol object is
configured, when the system PRNG is initialised, and when selecting
any cryptographic authentication protocol in an HTTP or SOCKS proxy
connection. With any luck those precautions between them should ensure
it's on whenever we need it.
Arm's own recommendation is that you should carefully choose the
granularity at which you enable and disable DIT: there's a potential
time cost to turning it on and off (I'm not sure what, but plausibly
something of the order of a pipeline flush), so it's a performance hit
to do it _inside_ each individual crypto function, but if CPUs start
supporting significant data-dependent optimisation in future, then it
will also become a noticeable performance hit to just leave it on
across the whole process. So you'd like to do it somewhere in the
middle: for example, you might turn on DIT once around the whole
process of verifying and decrypting an SSH packet, instead of once for
decryption and once for MAC.
With all respect to that recommendation as a strategy for maximum
performance, I'm not following it here. I turn on DIT at the start of
the PuTTY process, and then leave it on. Rationale:
1. PuTTY is not otherwise a performance-critical application: it's
not likely to max out your CPU for any purpose _other_ than
cryptography. The most CPU-intensive non-cryptographic thing I can
imagine a PuTTY process doing is the complicated computation of
font rendering in the terminal, and that will normally be cached
(you don't recompute each glyph from its outline and hints for
every time you display it).
2. I think a bigger risk lies in accidental side channels from having
DIT turned off when it should have been on. I can imagine lots of
causes for that. Missing a crypto operation in some unswept corner
of the code; confusing control flow (like my coroutine macros)
jumping with DIT clear into the middle of a region of code that
expected DIT to have been set at the beginning; having a reference
counter of DIT requests and getting it out of sync.
In a more sophisticated programming language, it might be possible to
avoid the risk in #2 by cleverness with the type system. For example,
in Rust, you could have a zero-sized type that acts as a proof token
for DIT being enabled (it would be constructed by a function that also
sets DIT, have a Drop implementation that clears DIT, and be !Send so
you couldn't use it in a thread other than the one where DIT was set),
and then you could require all the actual crypto functions to take a
DitToken as an extra parameter, at zero runtime cost. Then "oops I
forgot to set DIT around this piece of crypto" would become a compile
error. Even so, you'd have to take some care with coroutine-structured
code (what happens if a Rust async function yields while holding a DIT
token?) and with nesting (if you have two DIT tokens, you don't want
dropping the inner one to clear DIT while the outer one is still there
to wrongly convince callees that it's set). Maybe in Rust you could
get this all to work reliably. But not in C!
DIT is an optional feature of the Arm architecture, so we must first
test to see if it's supported. This is done the same way as we already
do for the various Arm crypto accelerators: on ELF-based systems,
check the appropriate bit in the 'hwcap' words in the ELF aux vector;
on Mac, look for an appropriate sysctl flag.
On Windows I don't know of a way to query the DIT feature, _or_ of a
way to write the necessary enabling instruction in an MSVC-compatible
way. I've _heard_ that it might not be necessary, because Windows
might just turn on DIT unconditionally and leave it on, in an even
more extreme version of my own strategy. I don't have a source for
that - I heard it by word of mouth - but I _hope_ it's true, because
that would suit me very well! Certainly I can't write code to enable
DIT without knowing (a) how to do it, (b) how to know if it's safe.
Nonetheless, I've put the enable_dit() call in all the right places in
the Windows main programs as well as the Unix and cross-platform code,
so that if I later find out that I _can_ put in an explicit enable of
DIT in some way, I'll only have to arrange to set HAVE_ARM_DIT and
compile the enable_dit() function appropriately.
2024-12-19 08:47:08 +00:00
|
|
|
enable_dit();
|
|
|
|
|
2018-11-03 10:06:33 +00:00
|
|
|
/* Call the function in ux{putty,pterm}.c to do app-type
|
|
|
|
* specific setup */
|
|
|
|
setup(false); /* false means we are not a one-session process */
|
2016-03-23 22:22:30 +00:00
|
|
|
|
2016-03-23 22:13:30 +00:00
|
|
|
if (argc > 1) {
|
|
|
|
pty_osx_envrestore_prefix = argv[--argc];
|
|
|
|
}
|
|
|
|
|
2016-03-23 22:22:30 +00:00
|
|
|
{
|
|
|
|
const char *home = getenv("HOME");
|
|
|
|
if (home) {
|
|
|
|
if (chdir(home)) {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gtkcomm_setup();
|
|
|
|
|
|
|
|
app = gtk_application_new("org.tartarus.projects.putty.macputty",
|
2024-09-08 16:20:16 +00:00
|
|
|
G_APPLICATION_DEFAULT_FLAGS);
|
2016-03-23 22:22:30 +00:00
|
|
|
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
|
|
|
|
g_signal_connect(app, "startup", G_CALLBACK(startup), NULL);
|
|
|
|
g_action_map_add_action_entries(G_ACTION_MAP(app),
|
|
|
|
app_actions,
|
|
|
|
G_N_ELEMENTS(app_actions),
|
|
|
|
app);
|
|
|
|
|
|
|
|
status = g_application_run(G_APPLICATION(app), argc, argv);
|
|
|
|
g_object_unref(app);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|