mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 09:27:59 +00:00
3c6ab5bbb7
12 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
Simon Tatham
|
a859955689 |
Fix premature exit if 'plink -shareexists' happens early.
A user reported a phenomenon where running 'plink -shareexists' very early in the connection would cause the receiving upstream PuTTY to exit cleanly with the message 'All channels closed' in the log. That wasn't hard to track down: that happens as a result of the connection layer callback sharing_no_more_downstreams(), which causes the connection layer to check whether it has any channels left open, and if not, to terminate the connection on the grounds that everything has finished. But it's premature to draw that conclusion if the reason no channels are open if we haven't _started_ yet! Now we have a 'started' flag which is set when we initialise mainchan, and the 'we're all done now' check will never fire before that flag is set. But in the course of investigating that, I found a second problem in the same area: at an even earlier stage of an SSH connection, the connshare system doesn't _even_ have the real ConnectionLayer pointer yet. Instead, it has a pointer to a dummy one provided by the top-level ssh.c, which contains a NULL vtable pointer. So if it calls sharing_no_more_downstreams on _that_ ConnectionLayer, it will dereference NULL and crash. So I've filled in cl_dummy's vtable pointer with a trivial vtable, containing only the one callback sharing_no_more_downstreams, which itself is a no-op function. Hopefully that should all be stable now. |
||
Simon Tatham
|
b4e1110892 |
Relax criteria for accepting agent-forwarding channel-opens.
Previously, the instant at which we send to the server a request to enable agent forwarding (the "auth-agent-req@openssh.com" channel request, or SSH1_CMSG_AGENT_REQUEST_FORWARDING) was also the instant at which we set a flag indicating that we're prepared to accept attempts from the server to open a channel to talk to the forwarded agent. If the server attempts that when we haven't sent a forwarding request, we treat it with suspicion, and reject it. But it turns out that at least one SSH server does this, for what seems to be a _somewhat_ sensible purpose, and OpenSSH accepts it. So, on the basis that the @openssh.com domain suffix makes them the arbiters of this part of the spec, I'm following their practice. I've removed the 'agent_fwd_enabled' flag from both connection layer implementations, together with the ConnectionLayer method that sets it; now agent-forwarding CHANNEL_OPENs are gated only on the questions of whether agent forwarding was permitted in the configuration and whether an agent actually exists to talk to, and not also whether we had previously sent a message to the server announcing it. (The change to this condition is also applied in the SSH-1 agent forwarding code, mostly for the sake of keeping things parallel where possible. I think it doesn't actually make a difference in SSH-1, because in SSH-1, it's not _possible_ for the server to try to open an agent channel before the main channel is set up, due to the entirely separate setup phase of the protocol.) The use case is a proxy host which makes a secondary SSH connection to a real destination host. A user has run into one of these recently, announcing a version banner of "SSH-2.0-FudoSSH", which relies on agent forwarding to authenticate the secondary connection. You connect to the proxy host and authenticate with a username string of the form "realusername#real.destination.host", and then, at the start of the connection protocol, the server immediately opens a channel back to your SSH agent which it uses to authenticate to the destination host. And it delays answering any CHANNEL_OPEN requests from the client until that's all done. For example (seen from the client's POV, although the server's CHANNEL_OPEN may well have been _sent_ up front rather than in response to the client's): client: SSH2_MSG_CHANNEL_OPEN "session" server: SSH2_MSG_CHANNEL_OPEN "auth-agent@openssh.com" client: SSH2_MSG_CHANNEL_OPEN_CONFIRMATION to the auth-agent request <- data is exchanged on the agent channel; proxy host uses that signature to log in to the destination host -> server: SSH2_MSG_CHANNEL_OPEN_CONFIRMATION to the session request With PuTTY, this wasn't working, because at the point when the server sends the auth-agent CHANNEL_OPEN, we had not yet had any opportunity to send auth-agent-req (because that has to wait until we've had a CHANNEL_OPEN_CONFIRMATION). So we were rejecting the server's CHANNEL_OPEN, which broke this workflow: client: SSH2_MSG_CHANNEL_OPEN "session" server: SSH2_MSG_CHANNEL_OPEN "auth-agent@openssh.com" client: SSH2_MSG_CHANNEL_OPEN_FAILURE to the auth-agent request (hey, I haven't told you you can do that yet!) server: SSH2_MSG_CHANNEL_OPEN_FAILURE to the session request (in that case, no shell session for you!) |
||
Simon Tatham
|
22b492c4f6 |
New protocol: PROT_SSHCONN, bare ssh-connection.
This is the same protocol that PuTTY's connection sharing has been using for years, to communicate between the downstream and upstream PuTTYs. I'm now promoting it to be a first-class member of the protocols list: if you have a server for it, you can select it in the GUI or on the command line, and write out a saved session that specifies it. This would be completely insecure if you used it as an ordinary network protocol, of course. Not only is it non-cryptographic and wide open to eavesdropping and hijacking, but it's not even _authenticated_ - it begins after the userauth phase of SSH. So there isn't even the mild security theatre of entering an easy-to-eavesdrop password, as there is with, say, Telnet. However, that's not what I want to use it for. My aim is to use it for various specialist and niche purposes, all of which involve speaking it over an 8-bit-clean data channel that is already set up, secured and authenticated by other methods. There are lots of examples of such channels: - a userv(1) invocation - the console of a UML kernel - the stdio channels into other kinds of container, such as Docker - the 'adb shell' channel (although it seems quite hard to run a custom binary at the far end of that) - a pair of pipes between PuTTY and a Cygwin helper process - and so on. So this protocol is intended as a convenient way to get a client at one end of any those to run a shell session at the other end. Unlike other approaches, it will give you all the SSH-flavoured amenities you're already used to, like forwarding your SSH agent into the container, or forwarding selected network ports in or out of it, or letting it open a window on your X server, or doing SCP/SFTP style file transfer. Of course another way to get all those amenities would be to run an ordinary SSH server over the same channel - but this approach avoids having to manage a phony password or authentication key, or taking up your CPU time with pointless crypto. |
||
Simon Tatham
|
5d718ef64b |
Whitespace rationalisation of entire code base.
The number of people has been steadily increasing who read our source code with an editor that thinks tab stops are 4 spaces apart, as opposed to the traditional tty-derived 8 that the PuTTY code expects. So I've been wondering for ages about just fixing it, and switching to a spaces-only policy throughout the code. And I recently found out about 'git blame -w', which should make this change not too disruptive for the purposes of source-control archaeology; so perhaps now is the time. While I'm at it, I've also taken the opportunity to remove all the trailing spaces from source lines (on the basis that git dislikes them, and is the only thing that seems to have a strong opinion one way or the other). Apologies to anyone downstream of this code who has complicated patch sets to rebase past this change. I don't intend it to be needed again. |
||
Simon Tatham
|
8a884eaef9 |
Start of an SSH-server-specific config structure.
This is much simpler than Conf, because I don't expect to have to copy it around, load or save it to disk (or the Windows registry), or serialise it between processes. So it can be a straightforward struct. As yet there's nothing actually _in_ it. I've just created the structure and arranged to pass it through to all the SSH layers. But now it's here, it will be a place I can add configuration items as I find I need them. |
||
Simon Tatham
|
514796b7e4 |
Add an interactive anti-spoofing prompt in Plink.
At the point when we change over the seat's trust status to untrusted for the last time, to finish authentication, Plink will now present a final interactive prompt saying 'Press Return to begin session'. This is a hint that anything after that that resembles an auth prompt should be treated with suspicion, because _PuTTY_ thinks it's finished authenticating. This is of course an annoying inconvenience for interactive users, so I've tried to reduce its impact as much as I can. It doesn't happen in GUI PuTTY at all (because the trust sigil system is used instead); it doesn't happen if you use plink -batch (because then the user already knows that they _never_ expect an interactive prompt); and it doesn't happen if Plink's standard input is being redirected from anywhere other than the terminal / console (because then it would be pointless for the server to try to scam passphrases out of the user anyway, since the user isn't in a position to enter one in response to a spoof prompt). So it should only happen to people who are using Plink in a terminal for interactive login purposes, and that's not _really_ what I ever intended Plink to be used for (which is why it's never had any out-of-band control UI like OpenSSH's ~ system). If anyone _still_ doesn't like this new prompt, it can also be turned off using the new -no-antispoof flag, if the user is willing to knowingly assume the risk. |
||
Simon Tatham
|
3214563d8e |
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'! |
||
Simon Tatham
|
a081dd0a4c |
Add an SFTP server to the SSH server code.
Unlike the traditional Unix SSH server organisation, the SFTP server is built into the same process as all the rest of the code. sesschan.c spots a subsystem request for "sftp", and responds to it by instantiating an SftpServer object and swapping out its own vtable for one that talks to it. (I rather like the idea of an object swapping its own vtable for a different one in the middle of its lifetime! This is one of those tricks that would be absurdly hard to implement in a 'proper' OO language, but when you're doing vtables by hand in C, it's no more difficult than any other piece of ordinary pointer manipulation. As long as the methods in both vtables expect the same physical structure layout, it doesn't cause a problem.) The SftpServer object doesn't deal directly with SFTP packet formats; it implements the SFTP server logic in a more abstract way, by having a vtable method for each SFTP request type with an appropriate parameter list. It sends its replies by calling methods in another vtable called SftpReplyBuilder, which in the normal case will write an SFTP reply packet to send back to the client. So SftpServer can focus more or less completely on the details of a particular filesystem API - and hence, the implementation I've got lives in the unix source directory, and works directly with file descriptors and struct stat and the like. (One purpose of this abstraction layer is that I may well want to write a second dummy implementation, for test-suite purposes, with completely controllable behaviour, and now I have a handy place to plug it in in place of the live filesystem.) In between sesschan's parsing of the byte stream into SFTP packets and the SftpServer object, there's a layer in the new file sftpserver.c which does the actual packet decoding and encoding: each request packet is passed to that, which pulls the fields out of the request packet and calls the appropriate method of SftpServer. It also provides the default SftpReplyBuilder which makes the output packet. I've moved some code out of the previous SFTP client implementation - basic packet construction code, and in particular the BinarySink/ BinarySource marshalling fuinction for fxp_attrs - into sftpcommon.c, so that the two directions can share as much as possible. |
||
Simon Tatham
|
1d323d5c80 |
Add an actual SSH server program.
This server is NOT SECURE! If anyone is reading this commit message, DO NOT DEPLOY IT IN A HOSTILE-FACING ENVIRONMENT! Its purpose is to speak the server end of everything PuTTY speaks on the client side, so that I can test that I haven't broken PuTTY when I reorganise its code, even things like RSA key exchange or chained auth methods which it's hard to find a server that speaks at all. (For this reason, it's declared with [UT] in the Recipe file, so that it falls into the same category as programs like testbn, which won't be installed by 'make install'.) Working title is 'Uppity', partly for 'Universal PuTTY Protocol Interaction Test Yoke', but mostly because it looks quite like the word 'PuTTY' with part of it reversed. (Apparently 'test yoke' is a very rarely used term meaning something not altogether unlike 'test harness', which is a bit of a stretch, but it'll do.) It doesn't actually _support_ everything I want yet. At the moment, it's a proof of concept only. But it has most of the machinery present, and the parts it's missing - such as chained auth methods - should be easy enough to add because I've built in the required flexibility, in the form of an AuthPolicy object which can request them if it wants to. However, the current AuthPolicy object is entirely trivial, and will let in any user with the password "weasel". (Another way in which this is not a production-ready server is that it also has no interaction with the OS's authentication system. In particular, it will not only let in any user with the same password, but it won't even change uid - it will open shells and forwardings under whatever user id you started it up as.) Currently, the program can only speak the SSH protocol on its standard I/O channels (using the new FdSocket facility), so if you want it to listen on a network port, you'll have to run it from some kind of separate listening program similar to inetd. For my own tests, I'm not even doing that: I'm just having PuTTY spawn it as a local proxy process, which also conveniently eliminates the risk of anyone hostile connecting to it. The bulk of the actual code reorganisation is already done by previous commits, so this change is _mostly_ just dropping in a new set of server-specific source files alongside the client-specific ones I created recently. The remaining changes in the shared SSH code are numerous, but all minor: - a few extra parameters to BPP and PPL constructors (e.g. 'are you in server mode?'), and pass both sets of SSH-1 protocol flags from the login to the connection layer - in server mode, unconditionally send our version string _before_ waiting for the remote one - a new hook in the SSH-1 BPP to handle enabling compression in server mode, where the message exchange works the other way round - new code in the SSH-2 BPP to do _deferred_ compression the other way round (the non-deferred version is still nicely symmetric) - in the SSH-2 transport layer, some adjustments to do key derivation either way round (swapping round the identifying letters in the various hash preimages, and making sure to list the KEXINITs in the right order) - also in the SSH-2 transport layer, an if statement that controls whether we send SERVICE_REQUEST and wait for SERVICE_ACCEPT, or vice versa - new ConnectionLayer methods for opening outgoing channels for X and agent forwardings - new functions in portfwd.c to establish listening sockets suitable for remote-to-local port forwarding (i.e. not under the direction of a Conf the way it's done on the client side). |
||
Simon Tatham
|
9fe719f47d |
Server prep: parse a lot of new channel requests.
ssh2connection.c now knows how to unmarshal the message formats for all the channel requests we'll need to handle when we're the server and a client sends them. Each one is translated into a call to a new method in the Channel vtable, which is implemented by a trivial 'always fail' routine in every channel type we know about so far. |
||
Simon Tatham
|
445030b3ea |
Server prep: support stderr output on channels.
The vtable method underneath sshfwd_write now takes an is_stderr parameter, and in SSH-2, this is implemented by having separate stdout and stderr bufchains in each outgoing channel, and counting the size of both for the purposes of measuring backlog and so forth. To avoid making _most_ call sites more verbose, the usual macro wrapper hasn't changed its API; it just sets is_stderr=FALSE. To use the new feature, there's an sshfwd_write_ext macro that exposes the extra parameter. |
||
Simon Tatham
|
b94c6a7e38 |
Move client-specific SSH code into new files.
This is a major code reorganisation in preparation for making this code base into one that can build an SSH server as well as a client. (Mostly for purposes of using the server as a regression test suite for the client, though I have some other possible uses in mind too. However, it's currently no part of my plan to harden the server to the point where it can sensibly be deployed in a hostile environment.) In this preparatory commit, I've broken up the SSH-2 transport and connection layers, and the SSH-1 connection layer, into multiple source files, with each layer having its own header file containing the shared type definitions. In each case, the new source file contains code that's specific to the client side of the protocol, so that a new file can be swapped in in its place when building the server. Mostly this is just a straightforward moving of code without changing it very much, but there are a couple of actual changes in the process: The parsing of SSH-2 global-request and channel open-messages is now done by a new pair of functions in the client module. For channel opens, I've invented a new union data type to be the return value from that function, representing either failure (plus error message), success (plus Channel instance to manage the new channel), or an instruction to hand the channel over to a sharing downstream (plus a pointer to the downstream in question). Also, the tree234 of remote port forwardings in ssh2connection is now initialised on first use by the client-specific code, so that's where its compare function lives. The shared ssh2connection_free() still takes responsibility for freeing it, but now has to check if it's non-null first. The outer shell of the ssh2_lportfwd_open method, for making a local-to-remote port forwarding, is still centralised in ssh2connection.c, but the part of it that actually constructs the outgoing channel-open message has moved into the client code, because that will have to change depending on whether the channel-open has to have type direct-tcpip or forwarded-tcpip. In the SSH-1 connection layer, half the filter_queue method has moved out into the new client-specific code, but not all of it - bidirectional channel maintenance messages are still handled centrally. One exception is SSH_MSG_PORT_OPEN, which can be sent in both directions, but with subtly different semantics - from server to client, it's referring to a previously established remote forwarding (and must be rejected if there isn't one that matches it), but from client to server it's just a "direct-tcpip" request with no prior context. So that one is in the client-specific module, and when I add the server code it will have its own different handler. |