Partly to reassure the user that they got what they asked for, and
partly so that's a clue for us in the logs when we get bug reports.
This involved repurposing platform_psftp_post_option_setup() (no longer
used since e22120fe) as platform_psftp_pre_conn_setup(), and moving it
to after logging is set up.
These are benign, I think. clang warns about casting non-pointer-sized
integers to pointers, but the Windows API actually does sometimes
involve values that are either pointers or _small_ integers, so in the
two cases involved I just cast through ULONG_PTR to silence the
warning. And clang insists that the integer whose address I give to
sk_getxdmdata is still uninitialised afterwards, which is just a lie.
clang-cl generates warnings saying they're deprecated, in favour of
the same names but prefixed with an underscore. The warnings are
coming from the standard MS headers, and I'm already #defining those
names differently on Unix, so I'll honour them.
When I added some extra braces in commit 095072fa4 to suppress this
warning, I think in fact I did the wrong thing, because the
declaration syntax I was originally using is the Microsoft-recommended
one in spite of clang not liking it - I think MS would be within their
rights (should they feel like it) to add those missing braces in a
later version of the WinSock headers, which would make the current
warning-clean code stop compiling. So it's better to put the code back
as it was, and avoid the clang warning by using clang's
warning-suppression pragmas for just those declarations.
I've also done the same thing in winnet.c, for two initialisers of
IPv6 well-known addresses which had the same problem (but which I
didn't notice yesterday because a misjudged set of Windows version
macros had prevented me from compiling that file successfully at all).
We used to offer to clean up saved sessions, so we should mention that
we don't for the benefit of users of old versions, who might have been
relying on it.
This change applies to every situation when GUI PuTTY knowingly spawns
another GUI PuTTY, to wit, the System menu options 'New Session',
'Duplicate Session' and the 'Saved Sessions' submenu.
(Literally speaking, what we actually pass through to the sub-PuTTY's
command line is not the "-restrict-acl" option itself, but a special
prefix "&R", which has the same meaning but which lives in the special
pre-argv-splitting command-line namespace like the magic options used
for Duplicate Session and the old '@sessionname' prefix which the
Saved Sessions submenu still uses. Otherwise, by the time we split up
argv and recognised -restrict-acl, it would be too late to parse those
other options.)
One case in which PuTTY spawns a subprocess and this change _doesn't_
apply is when the subprocess is a proxy command which happens to be a
Plink. Recognising Plink commands in that situation would be fragile
and unreliable, and in any case if the user wants a proxy Plink to be
ACL-restricted, they are in control of its exact command line so they
can add -restrict-acl themselves.
These ones are stylistic rather than potential bugs: mostly signedness
of char pointers in cases where they clearly aren't going to cause the
wrong thing to actually happen, and one thing in winsecur.c where
clang would have preferred an extra pair of braces around some
initialisers but it's legal with or without. But since some of clang's
warnings turn out to be quite useful, it seems worth silencing these
harmless ones so as to be able to see the rest.
I was having a play with clang's MSVC compatibility mode, just to see
how much of PuTTY it could compile, and one of its warnings pointed
out this error which must have crept in when I was changing the EOF
flags in winhandl.c from booleans to three-state enums - I left the !
on the front of what was previously an if (!thing) and needed to turn
into if (thing == EOF_NO).
Several functions were passing a 'char *error' and assigning error
messages directly into 'error', where they should have been passing
'char **error' and assigning error messages into '*error' if the error
message is to be returned to the caller. This would have led to
incomplete error messages.
As documented in bug 'win-process-acl-finesse', we've had enough
assorted complaints about it breaking various non-malicious pieces of
Windows process interaction (ranging from git->plink integration to
screen readers for the vision-impaired) that I think it's more
sensible to set the process back to its default level of protection.
This precaution was never a fully effective protection anyway, due to
the race condition at process startup; the only properly effective
defence would have been to prevent malware running under the same user
ID as PuTTY in the first place, so in that sense, nothing has changed.
But people who want the arguable defence-in-depth advantage of the ACL
restriction can now turn it on with the '-restrict-acl' command-line
option, and it's up to them whether they can live with the assorted
inconveniences that come with it.
In the course of this change, I've centralised a bit more of the
restriction code into winsecur.c, to avoid repeating the error
handling in multiple places.
The previous agent-forwarding system worked by passing each complete
query received from the input to agent_query() as soon as it was
ready. So if the remote client were to pipeline multiple requests,
then Unix PuTTY (in which agent_query() works asynchronously) would
parallelise them into many _simultaneous_ connections to the real
agent - and would not track which query went out first, so that if the
real agent happened to send its replies (to what _it_ thought were
independent clients) in the wrong order, then PuTTY would serialise
the replies on to the forwarding channel in whatever order it got
them, which wouldn't be the order the remote client was expecting.
To solve this, I've done a considerable rewrite, which keeps the
request stream in a bufchain, and only removes data from the bufchain
when it has a complete request. Then, if agent_query decides to be
asynchronous, the forwarding system waits for _that_ agent response
before even trying to extract the next request's worth of data from
the bufchain.
As an added bonus (in principle), this gives agent-forwarding channels
some actual flow control for the first time ever! If a client spams us
with an endless stream of rapid requests, and never reads its
responses, then the output side of the channel will run out of window,
which causes us to stop processing requests until we have space to
send responses again, which in turn causes us to stop granting extra
window on the input side, which serves the client right.
Now, instead of returning a boolean indicating whether the query has
completed or is still pending, agent_query() returns NULL to indicate
that the query _has_ completed, and if it hasn't, it returns a pointer
to a context structure representing the pending query, so that the
latter can be used to cancel the query if (for example) you later
decide you need to free the thing its callback was using as a context.
This should fix a potential race-condition segfault if you overload an
agent forwarding channel and then close it abruptly. (Which nobody
will be doing for sensible purposes, of course! But I ran across this
while stress-testing other aspects of agent forwarding.)
It's been commented out for ages because it never really worked, and
it's about to become further out of date when I make other changes to
the agent client code, so it's time to get rid of it before it gets in
the way.
If and when I do get round to supporting asynchronous agent requests
on Windows, it's now pretty clear to me that trying to coerce this
ghastly window-message IPC into the right shape is the wrong way, and
a better approach will be to make Pageant support a named-pipe based
alternative transport for its agent connections, and speaking the
ordinary stream-oriented agent protocol over that. Then Pageant will
be able to start adding interactive features (like confirmation
dialogs or on-demand decryption) with freedom to reply to multiple
simultaneous agent connections in whatever order it finds convenient.
backend_socket_log was generating the IP address in its error messages
by means of calling sk_getaddr(). But sk_getaddr only gets a SockAddr,
which may contain a whole list of candidate addresses; it doesn't also
get the information stored in the 'step' field of the Socket that was
actually trying to make the connection, which says _which_ of those
addresses we were in the middle of trying to connect to.
So now we construct a temporary SockAddr that points at the
appropriate one of the addresses, and use that for calls to plug_log
during connection setup.
In case of connection errors before and during the handshake,
net_select_result is retrying with the next address of the server. It
however was immediately going to the last address as it was not
checking the return value of try_connect for all intermediate
addresses.
This shows the build platform (32- vs 64-bit in particular, and also
whether Unix GTK builds were compiled with or without the X11 pieces),
what compiler was used to build the binary, and any interesting build
options that might have been set on the make command line (especially,
but not limited to, the security-damaging ones like NO_SECURITY or
UNPROTECT). This will probably be useful all over the place, but in
particular it should allow the different Windows binaries to be told
apart!
Commits 21101c739 and 2eb952ca3 laid the groundwork for this, by
allowing the various About boxes to contain free text and also
ensuring they could be copied and pasted easily as part of a bug
report.
It's a bit conceptually incoherent anyway - if you're uninstalling
PuTTY _systemwide_ across a multi-user system, it doesn't really make
sense that you'd also want to wipe the saved sessions for the
individual user running the uninstaller.
Also, making this change to the Inno Setup uninstaller opens up a
nicer migration path to MSI for people doing large corporate rollouts:
they can upgrade to this version of the Inno Setup package, then do a
silent uninstall of it (which should now _actually_ be silent, since
this cleanup step was the thing that interrupted it otherwise) and
then a silent install of the MSI.
The MSI format has a fixed field for target architecture, so there's
no way to build a single MSI that can decide at install time whether
to install 32-bit or 64-bit (or both). The best you can do along those
lines, apparently, is to have two MSI files plus a bootstrap .EXE that
decides which of them to run, and as far as I'm concerned that would
just reintroduce all the same risks and annoyances that made us want
to migrate away from .EXE installers anyway.
Uses the BUILDDIR mechanism I added to Makefile.vc in commit
d3db17f3e.
This change is purely internal to Buildscr, and shouldn't affect the
output of a build. It paves the way to have Buildscr run multiple
Windows builds using different compilers, by putting each one in a
different subdirectory so that their outputs don't collide.
A user points out that buf[] in sk_tcp_peer_info is only used in the
IPv6 branch of an ifdef, and is declared with a size of
INET6_ADDRSTRLEN, which won't be defined in NO_IPV6 mode. So moving
the definition inside another IPv6-only ifdef fixes the resulting
build failure.
The algorithm Windows uses to generate AppUserModelIDs "hangs on" to
removable media (CDs/DVDs) if PuTTY is launched with a CD/DVD in a drive.
Set the AppUserModelID explicitly to avoid using this algorithm.
At least on systems providing SetDefaultDllDirectories, this should
stop PuTTY from being willing to load DLLs from its containing
directory - which makes no difference when it's been properly
installed (in which case the application dir contains no DLLs anyway),
but does if it's being run from somewhere uncontrolled like a browser
downloads directory.
Preliminary testing suggests that this shouldn't break any existing
deliberate use of DLLs, including GSSAPI providers.
The Windows implementation of get_file_posn is calling SetFilePointer
to obtain the current position in the file. However it did not
initialize the variable holding the high order 32-bit to 0. Thus,
SetFilePointer either returned -1 to indicate an error or did move the
file pointer to a different location instead of just returning the
current position. This change just initializes the variable to 0.
As a result, this bug has caused psftp's reget command to fail
resuming transfers or to create corrupt files due to setting up an
incorrect resume offset.
Previously only Unix front ends bothered to include it, on the basis
that only the pty backend needed it (to set IUTF8 in the pty). We're
about to need it everywhere else too.
It's really only useful with MinGW rather than a Cygwin toolchain these
days, as recent versions of the latter insist against linking with the
Cygwin DLL.
(I think it may no longer be possible to build with Cygwin out of the
box at all these days, but I'm not going to say so without having
actually checked that's the case. Settle for listing MinGW first in
various comments and docs.)
I've just upgraded my build environment to the latest Inno Setup
(apparently fixing some DLL hijacking issues), and found that the
build script doesn't run any more because the name of the output file
has changed - it used to produce Output/setup.exe, but now it produces
Output/mysetup.exe.
Rather than just fixing the build script to expect the new name, I've
explicitly specified an output filename of my own choice in putty.iss,
so that the build script should now work with versions before and
after the change.
I can't believe this codebase is around 20 years old and has had
multiple giant const-fixing patches, and yet there are _still_ things
that should have been const for years and aren't.
Ahem. Cut-and-paste goof that I introduced in commit 2eb952ca3, when I
moved the application names out of separate text controls in the
resource-file dialog descriptions.
Blocking PROCESS_QUERY_INFORMATION access to the process turned out to
stop screen readers like Microsoft Narrator from reading parts of the
PuTTY window like the System Menu.
strcspn() returns a size_t, which is not safe to pass as the parameter
in a printf argument list corresponding to a "*" field width specifier
in the format string, because the latter should be int, which may not
be the same size as size_t.
We were calling Windows file-handling API functions GetFilesize and
SetFilePointer, each of which returns two halves of a large integer by
writing the high half through a pointer, with pointers to the wrong
integer types. Now we're always passing the exact type defined in the
API, and converting after the fact to our own uint64 type, so this
should avoid any risk of wrong-sized pointers.
These integer types are correct for the id/handle parameter to
AppendMenu / InsertMenu / DeleteMenu, and also for the return type of
dialog box procedures.
We also have the special-purpose -DUNPROTECT to disable just the ACL
changes, but if you want to compile without any Windows security API
support at all (e.g. experimentally building against winelib) then
it's easier not to have to specify both defines separately.
The old README.txt instructed you to manually update PATH if you
wanted to run pscp from a command prompt. But the MSI installer can do
that automatically, so the wording needs tweaks. And now that we're
actually launching README (at least optionally) from the installer UI,
it's more important to not make it look silly.
This is a thing that the Inno Setup installer did, and that I didn't
get round to replicating when I rushed out the initial MSI in a hurry.
I've checked that this doesn't prevent unattended installation by
administrators: running 'msiexec /q /i putty-whatever.msi' as
administrator still installs silently after this change, without
popping up the README unexpectedly on anyone's desktop as a side
effect.
(I _think_ - but I'm still a long way from an MSI expert - that that's
because /q turns off the whole UI part of the MSI system, and the
loading of README is actually triggered by the transition away from
the final UI dialog box, which we now never visit in the first place.)
I rushed out the MSI in too much of a hurry to sort out this kind of
thing, but now we've got leisure to reconsider, I think it's better
behaviour not to clutter everyone's desktops unless specifically asked
to.
It's only a warning; Windows PuTTYgen puts it up as a message box, and
will still generate the key if you click yes, and Unix PuTTYgen just
prints the warning and gets on with generation anyway. But it might
help encourage people to move away from 1024-bit keys, if they're
still using them.
Protecting our processes from outside interference need not be limited
to just PuTTY: there's no reason why the other SSH-speaking tools
shouldn't have the same treatment (PSFTP, PSCP, Plink), and PuTTYgen
and Pageant which handle private key material.