Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'ssh/connection1-server.c')
-rw-r--r--ssh/connection1-server.c365
1 files changed, 365 insertions, 0 deletions
diff --git a/ssh/connection1-server.c b/ssh/connection1-server.c
new file mode 100644
index 00000000..cc69bdb3
--- /dev/null
+++ b/ssh/connection1-server.c
@@ -0,0 +1,365 @@
+/*
+ * Server-specific parts of the SSH-1 connection layer.
+ */
+
+#include <assert.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "bpp.h"
+#include "ppl.h"
+#include "channel.h"
+#include "sshcr.h"
+#include "connection1.h"
+#include "server.h"
+
+static size_t ssh1sesschan_write(SshChannel *c, bool is_stderr,
+ const void *, size_t);
+static void ssh1sesschan_write_eof(SshChannel *c);
+static void ssh1sesschan_initiate_close(SshChannel *c, const char *err);
+static void ssh1sesschan_send_exit_status(SshChannel *c, int status);
+static void ssh1sesschan_send_exit_signal(
+ SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg);
+
+static const SshChannelVtable ssh1sesschan_vtable = {
+ .write = ssh1sesschan_write,
+ .write_eof = ssh1sesschan_write_eof,
+ .initiate_close = ssh1sesschan_initiate_close,
+ .send_exit_status = ssh1sesschan_send_exit_status,
+ .send_exit_signal = ssh1sesschan_send_exit_signal,
+ /* everything else is NULL */
+};
+
+void ssh1connection_server_configure(
+ PacketProtocolLayer *ppl, const SshServerConfig *ssc)
+{
+ struct ssh1_connection_state *s =
+ container_of(ppl, struct ssh1_connection_state, ppl);
+ s->ssc = ssc;
+}
+
+void ssh1_connection_direction_specific_setup(
+ struct ssh1_connection_state *s)
+{
+ if (!s->mainchan_chan) {
+ s->mainchan_sc.vt = &ssh1sesschan_vtable;
+ s->mainchan_sc.cl = &s->cl;
+ s->mainchan_chan = sesschan_new(
+ &s->mainchan_sc, s->ppl.logctx, NULL, s->ssc);
+ }
+}
+
+bool ssh1_handle_direction_specific_packet(
+ struct ssh1_connection_state *s, PktIn *pktin)
+{
+ PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
+ PktOut *pktout;
+ struct ssh1_channel *c;
+ unsigned remid;
+ ptrlen host, cmd, data;
+ char *host_str, *err;
+ int port, listenport;
+ bool success;
+
+ switch (pktin->type) {
+ case SSH1_CMSG_EXEC_SHELL:
+ if (s->finished_setup)
+ goto unexpected_setup_packet;
+
+ ppl_logevent("Client requested a shell");
+ chan_run_shell(s->mainchan_chan);
+ s->finished_setup = true;
+ return true;
+
+ case SSH1_CMSG_EXEC_CMD:
+ if (s->finished_setup)
+ goto unexpected_setup_packet;
+
+ cmd = get_string(pktin);
+ ppl_logevent("Client sent command '%.*s'", PTRLEN_PRINTF(cmd));
+ chan_run_command(s->mainchan_chan, cmd);
+ s->finished_setup = true;
+ return true;
+
+ case SSH1_CMSG_REQUEST_COMPRESSION:
+ if (s->compressing || !s->ssc->ssh1_allow_compression) {
+ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_FAILURE);
+ pq_push(s->ppl.out_pq, pktout);
+ } else {
+ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_SUCCESS);
+ pq_push(s->ppl.out_pq, pktout);
+ /* Synchronous run of output formatting, to ensure that
+ * success packet is converted into wire format before we
+ * start compressing */
+ ssh_bpp_handle_output(s->ppl.bpp);
+ /* And now ensure that the _next_ packet will be the first
+ * compressed one. */
+ ssh1_bpp_start_compression(s->ppl.bpp);
+ s->compressing = true;
+ }
+
+ return true;
+
+ case SSH1_CMSG_REQUEST_PTY: {
+ if (s->finished_setup)
+ goto unexpected_setup_packet;
+
+ ptrlen termtype = get_string(pktin);
+ unsigned height = get_uint32(pktin);
+ unsigned width = get_uint32(pktin);
+ unsigned pixwidth = get_uint32(pktin);
+ unsigned pixheight = get_uint32(pktin);
+ struct ssh_ttymodes modes = read_ttymodes_from_packet(
+ BinarySource_UPCAST(pktin), 1);
+
+ if (get_err(pktin)) {
+ ppl_logevent("Unable to decode pty request packet");
+ success = false;
+ } else if (!chan_allocate_pty(
+ s->mainchan_chan, termtype, width, height,
+ pixwidth, pixheight, modes)) {
+ ppl_logevent("Unable to allocate a pty");
+ success = false;
+ } else {
+ success = true;
+ }
+
+ pktout = ssh_bpp_new_pktout(
+ s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE));
+ pq_push(s->ppl.out_pq, pktout);
+ return true;
+ }
+
+ case SSH1_CMSG_PORT_FORWARD_REQUEST:
+ if (s->finished_setup)
+ goto unexpected_setup_packet;
+
+ listenport = toint(get_uint32(pktin));
+ host = get_string(pktin);
+ port = toint(get_uint32(pktin));
+
+ ppl_logevent("Client requested port %d forward to %.*s:%d",
+ listenport, PTRLEN_PRINTF(host), port);
+
+ host_str = mkstr(host);
+ success = portfwdmgr_listen(
+ s->portfwdmgr, NULL, listenport, host_str, port, s->conf);
+ sfree(host_str);
+
+ pktout = ssh_bpp_new_pktout(
+ s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE));
+ pq_push(s->ppl.out_pq, pktout);
+ return true;
+
+ case SSH1_CMSG_X11_REQUEST_FORWARDING: {
+ if (s->finished_setup)
+ goto unexpected_setup_packet;
+
+ ptrlen authproto = get_string(pktin);
+ ptrlen authdata = get_string(pktin);
+ unsigned screen_number = 0;
+ if (s->remote_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER)
+ screen_number = get_uint32(pktin);
+
+ success = chan_enable_x11_forwarding(
+ s->mainchan_chan, false, authproto, authdata, screen_number);
+
+ pktout = ssh_bpp_new_pktout(
+ s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE));
+ pq_push(s->ppl.out_pq, pktout);
+ return true;
+ }
+
+ case SSH1_CMSG_AGENT_REQUEST_FORWARDING:
+ if (s->finished_setup)
+ goto unexpected_setup_packet;
+
+ success = chan_enable_agent_forwarding(s->mainchan_chan);
+
+ pktout = ssh_bpp_new_pktout(
+ s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE));
+ pq_push(s->ppl.out_pq, pktout);
+ return true;
+
+ case SSH1_CMSG_STDIN_DATA:
+ data = get_string(pktin);
+ chan_send(s->mainchan_chan, false, data.ptr, data.len);
+ return true;
+
+ case SSH1_CMSG_EOF:
+ chan_send_eof(s->mainchan_chan);
+ return true;
+
+ case SSH1_CMSG_WINDOW_SIZE:
+ return true;
+
+ case SSH1_MSG_PORT_OPEN:
+ remid = get_uint32(pktin);
+ host = get_string(pktin);
+ port = toint(get_uint32(pktin));
+
+ host_str = mkstr(host);
+
+ ppl_logevent("Received request to connect to port %s:%d",
+ host_str, port);
+ c = snew(struct ssh1_channel);
+ c->connlayer = s;
+ err = portfwdmgr_connect(
+ s->portfwdmgr, &c->chan, host_str, port,
+ &c->sc, ADDRTYPE_UNSPEC);
+
+ sfree(host_str);
+
+ if (err) {
+ ppl_logevent("Port open failed: %s", err);
+ sfree(err);
+ ssh1_channel_free(c);
+ pktout = ssh_bpp_new_pktout(
+ s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
+ put_uint32(pktout, remid);
+ pq_push(s->ppl.out_pq, pktout);
+ } else {
+ ssh1_channel_init(c);
+ c->remoteid = remid;
+ c->halfopen = false;
+ pktout = ssh_bpp_new_pktout(
+ s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
+ put_uint32(pktout, c->remoteid);
+ put_uint32(pktout, c->localid);
+ pq_push(s->ppl.out_pq, pktout);
+ ppl_logevent("Forwarded port opened successfully");
+ }
+
+ return true;
+
+ case SSH1_CMSG_EXIT_CONFIRMATION:
+ if (!s->sent_exit_status) {
+ ssh_proto_error(s->ppl.ssh, "Received SSH1_CMSG_EXIT_CONFIRMATION"
+ " without having sent SSH1_SMSG_EXIT_STATUS");
+ return true;
+ }
+ ppl_logevent("Client sent exit confirmation");
+ return true;
+
+ default:
+ return false;
+ }
+
+ unexpected_setup_packet:
+ ssh_proto_error(s->ppl.ssh, "Received unexpected setup packet after the "
+ "setup phase, type %d (%s)", pktin->type,
+ ssh1_pkt_type(pktin->type));
+ /* FIXME: ensure caller copes with us just having freed the whole layer */
+ return true;
+}
+
+SshChannel *ssh1_session_open(ConnectionLayer *cl, Channel *chan)
+{
+ unreachable("Should never be called in the server");
+}
+
+struct ssh_rportfwd *ssh1_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)
+{
+ unreachable("Should never be called in the server");
+}
+
+static size_t ssh1sesschan_write(SshChannel *sc, bool is_stderr,
+ const void *data, size_t len)
+{
+ struct ssh1_connection_state *s =
+ container_of(sc, struct ssh1_connection_state, mainchan_sc);
+ PktOut *pktout;
+
+ pktout = ssh_bpp_new_pktout(
+ s->ppl.bpp,
+ (is_stderr ? SSH1_SMSG_STDERR_DATA : SSH1_SMSG_STDOUT_DATA));
+ put_string(pktout, data, len);
+ pq_push(s->ppl.out_pq, pktout);
+
+ return 0;
+}
+
+static void ssh1sesschan_write_eof(SshChannel *sc)
+{
+ /* SSH-1 can't represent server-side EOF */
+ /* FIXME: some kind of check-termination system, whereby once this has been called _and_ we've had an exit status _and_ we've got no other channels open, we send the actual EXIT_STATUS message */
+}
+
+static void ssh1sesschan_initiate_close(SshChannel *sc, const char *err)
+{
+ /* SSH-1 relies on the client to close the connection in the end */
+}
+
+static void ssh1sesschan_send_exit_status(SshChannel *sc, int status)
+{
+ struct ssh1_connection_state *s =
+ container_of(sc, struct ssh1_connection_state, mainchan_sc);
+ PktOut *pktout;
+
+ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_EXIT_STATUS);
+ put_uint32(pktout, status);
+ pq_push(s->ppl.out_pq, pktout);
+
+ s->sent_exit_status = true;
+}
+
+static void ssh1sesschan_send_exit_signal(
+ SshChannel *sc, ptrlen signame, bool core_dumped, ptrlen msg)
+{
+ /* SSH-1 has no separate representation for signals */
+ ssh1sesschan_send_exit_status(sc, 128);
+}
+
+SshChannel *ssh1_serverside_x11_open(
+ ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi)
+{
+ struct ssh1_connection_state *s =
+ container_of(cl, struct ssh1_connection_state, cl);
+ PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
+ struct ssh1_channel *c = snew(struct ssh1_channel);
+ PktOut *pktout;
+
+ c->connlayer = s;
+ ssh1_channel_init(c);
+ c->halfopen = true;
+ c->chan = chan;
+
+ ppl_logevent("Forwarding X11 connection to client");
+
+ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_X11_OPEN);
+ put_uint32(pktout, c->localid);
+ pq_push(s->ppl.out_pq, pktout);
+
+ return &c->sc;
+}
+
+SshChannel *ssh1_serverside_agent_open(ConnectionLayer *cl, Channel *chan)
+{
+ struct ssh1_connection_state *s =
+ container_of(cl, struct ssh1_connection_state, cl);
+ PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
+ struct ssh1_channel *c = snew(struct ssh1_channel);
+ PktOut *pktout;
+
+ c->connlayer = s;
+ ssh1_channel_init(c);
+ c->halfopen = true;
+ c->chan = chan;
+
+ ppl_logevent("Forwarding agent connection to client");
+
+ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_AGENT_OPEN);
+ put_uint32(pktout, c->localid);
+ pq_push(s->ppl.out_pq, pktout);
+
+ return &c->sc;
+}
+
+bool ssh1_connection_need_antispoof_prompt(struct ssh1_connection_state *s)
+{
+ return false;
+}