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/connection2-client.c')
-rw-r--r--ssh/connection2-client.c511
1 files changed, 511 insertions, 0 deletions
diff --git a/ssh/connection2-client.c b/ssh/connection2-client.c
new file mode 100644
index 00000000..f17d1e21
--- /dev/null
+++ b/ssh/connection2-client.c
@@ -0,0 +1,511 @@
+/*
+ * Client-specific parts of the SSH-2 connection layer.
+ */
+
+#include <assert.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "bpp.h"
+#include "ppl.h"
+#include "channel.h"
+#include "sshcr.h"
+#include "connection2.h"
+
+static ChanopenResult chan_open_x11(
+ struct ssh2_connection_state *s, SshChannel *sc,
+ ptrlen peeraddr, int peerport)
+{
+ PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
+ char *peeraddr_str;
+ Channel *ch;
+
+ ppl_logevent("Received X11 connect request from %.*s:%d",
+ PTRLEN_PRINTF(peeraddr), peerport);
+
+ if (!s->X11_fwd_enabled && !s->connshare) {
+ CHANOPEN_RETURN_FAILURE(
+ SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
+ ("X11 forwarding is not enabled"));
+ }
+
+ peeraddr_str = peeraddr.ptr ? mkstr(peeraddr) : NULL;
+ ch = x11_new_channel(
+ s->x11authtree, sc, peeraddr_str, peerport, s->connshare != NULL);
+ sfree(peeraddr_str);
+ ppl_logevent("Opened X11 forward channel");
+ CHANOPEN_RETURN_SUCCESS(ch);
+}
+
+static ChanopenResult chan_open_forwarded_tcpip(
+ struct ssh2_connection_state *s, SshChannel *sc,
+ ptrlen fwdaddr, int fwdport, ptrlen peeraddr, int peerport)
+{
+ PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
+ struct ssh_rportfwd pf, *realpf;
+ Channel *ch;
+ char *err;
+
+ ppl_logevent("Received remote port %.*s:%d open request from %.*s:%d",
+ PTRLEN_PRINTF(fwdaddr), fwdport,
+ PTRLEN_PRINTF(peeraddr), peerport);
+
+ pf.shost = mkstr(fwdaddr);
+ pf.sport = fwdport;
+ realpf = find234(s->rportfwds, &pf, NULL);
+ sfree(pf.shost);
+
+ if (realpf == NULL) {
+ CHANOPEN_RETURN_FAILURE(
+ SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
+ ("Remote port is not recognised"));
+ }
+
+ if (realpf->share_ctx) {
+ /*
+ * This port forwarding is on behalf of a connection-sharing
+ * downstream.
+ */
+ CHANOPEN_RETURN_DOWNSTREAM(realpf->share_ctx);
+ }
+
+ err = portfwdmgr_connect(
+ s->portfwdmgr, &ch, realpf->dhost, realpf->dport,
+ sc, realpf->addressfamily);
+ ppl_logevent("Attempting to forward remote port to %s:%d",
+ realpf->dhost, realpf->dport);
+ if (err != NULL) {
+ ppl_logevent("Port open failed: %s", err);
+ sfree(err);
+ CHANOPEN_RETURN_FAILURE(
+ SSH2_OPEN_CONNECT_FAILED,
+ ("Port open failed"));
+ }
+
+ ppl_logevent("Forwarded port opened successfully");
+ CHANOPEN_RETURN_SUCCESS(ch);
+}
+
+static ChanopenResult chan_open_auth_agent(
+ struct ssh2_connection_state *s, SshChannel *sc)
+{
+ if (!ssh_agent_forwarding_permitted(&s->cl)) {
+ CHANOPEN_RETURN_FAILURE(
+ SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
+ ("Agent forwarding is not enabled"));
+ }
+
+ /*
+ * If possible, make a stream-oriented connection to the agent and
+ * set up an ordinary port-forwarding type channel over it.
+ */
+ Plug *plug;
+ Channel *ch = portfwd_raw_new(&s->cl, &plug, true);
+ Socket *skt = agent_connect(plug);
+
+ if (!sk_socket_error(skt)) {
+ portfwd_raw_setup(ch, skt, sc);
+ CHANOPEN_RETURN_SUCCESS(ch);
+ } else {
+ portfwd_raw_free(ch);
+ /*
+ * Otherwise, fall back to the old-fashioned system of parsing the
+ * forwarded data stream ourselves for message boundaries, and
+ * passing each individual message to the one-off agent_query().
+ */
+ CHANOPEN_RETURN_SUCCESS(agentf_new(sc));
+ }
+}
+
+ChanopenResult ssh2_connection_parse_channel_open(
+ struct ssh2_connection_state *s, ptrlen type,
+ PktIn *pktin, SshChannel *sc)
+{
+ if (ptrlen_eq_string(type, "x11")) {
+ ptrlen peeraddr = get_string(pktin);
+ int peerport = get_uint32(pktin);
+
+ return chan_open_x11(s, sc, peeraddr, peerport);
+ } else if (ptrlen_eq_string(type, "forwarded-tcpip")) {
+ ptrlen fwdaddr = get_string(pktin);
+ int fwdport = toint(get_uint32(pktin));
+ ptrlen peeraddr = get_string(pktin);
+ int peerport = toint(get_uint32(pktin));
+
+ return chan_open_forwarded_tcpip(
+ s, sc, fwdaddr, fwdport, peeraddr, peerport);
+ } else if (ptrlen_eq_string(type, "auth-agent@openssh.com")) {
+ return chan_open_auth_agent(s, sc);
+ } else {
+ CHANOPEN_RETURN_FAILURE(
+ SSH2_OPEN_UNKNOWN_CHANNEL_TYPE,
+ ("Unsupported channel type requested"));
+ }
+}
+
+bool ssh2_connection_parse_global_request(
+ struct ssh2_connection_state *s, ptrlen type, PktIn *pktin)
+{
+ /*
+ * We don't know of any global requests that an SSH client needs
+ * to honour.
+ */
+ return false;
+}
+
+PktOut *ssh2_portfwd_chanopen(
+ struct ssh2_connection_state *s, struct ssh2_channel *c,
+ const char *hostname, int port,
+ const char *description, const SocketPeerInfo *peerinfo)
+{
+ PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
+ PktOut *pktout;
+
+ /*
+ * In client mode, this function is called by portfwdmgr in
+ * response to PortListeners that were set up in
+ * portfwdmgr_config, which means that the hostname and port
+ * parameters will indicate the host we want to tell the server to
+ * connect _to_.
+ */
+
+ ppl_logevent("Opening connection to %s:%d for %s",
+ hostname, port, description);
+
+ pktout = ssh2_chanopen_init(c, "direct-tcpip");
+ {
+ char *trimmed_host = host_strduptrim(hostname);
+ put_stringz(pktout, trimmed_host);
+ sfree(trimmed_host);
+ }
+ put_uint32(pktout, port);
+
+ /*
+ * We make up values for the originator data; partly it's too much
+ * hassle to keep track, and partly I'm not convinced the server
+ * should be told details like that about my local network
+ * configuration. The "originator IP address" is syntactically a
+ * numeric IP address, and some servers (e.g., Tectia) get upset
+ * if it doesn't match this syntax.
+ */
+ put_stringz(pktout, "0.0.0.0");
+ put_uint32(pktout, 0);
+
+ return pktout;
+}
+
+static int ssh2_rportfwd_cmp(void *av, void *bv)
+{
+ struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
+ struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
+ int i;
+ if ( (i = strcmp(a->shost, b->shost)) != 0)
+ return i < 0 ? -1 : +1;
+ if (a->sport > b->sport)
+ return +1;
+ if (a->sport < b->sport)
+ return -1;
+ return 0;
+}
+
+static void ssh2_rportfwd_globreq_response(struct ssh2_connection_state *s,
+ PktIn *pktin, void *ctx)
+{
+ PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
+ struct ssh_rportfwd *rpf = (struct ssh_rportfwd *)ctx;
+
+ if (pktin->type == SSH2_MSG_REQUEST_SUCCESS) {
+ ppl_logevent("Remote port forwarding from %s enabled",
+ rpf->log_description);
+ } else {
+ ppl_logevent("Remote port forwarding from %s refused",
+ rpf->log_description);
+
+ struct ssh_rportfwd *realpf = del234(s->rportfwds, rpf);
+ assert(realpf == rpf);
+ portfwdmgr_close(s->portfwdmgr, rpf->pfr);
+ free_rportfwd(rpf);
+ }
+}
+
+struct ssh_rportfwd *ssh2_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)
+{
+ struct ssh2_connection_state *s =
+ container_of(cl, struct ssh2_connection_state, cl);
+ struct ssh_rportfwd *rpf = snew(struct ssh_rportfwd);
+
+ if (!s->rportfwds)
+ s->rportfwds = newtree234(ssh2_rportfwd_cmp);
+
+ rpf->shost = dupstr(shost);
+ rpf->sport = sport;
+ rpf->dhost = dupstr(dhost);
+ rpf->dport = dport;
+ rpf->addressfamily = addressfamily;
+ rpf->log_description = dupstr(log_description);
+ rpf->pfr = pfr;
+ rpf->share_ctx = share_ctx;
+
+ if (add234(s->rportfwds, rpf) != rpf) {
+ free_rportfwd(rpf);
+ return NULL;
+ }
+
+ if (!rpf->share_ctx) {
+ PktOut *pktout = ssh_bpp_new_pktout(
+ s->ppl.bpp, SSH2_MSG_GLOBAL_REQUEST);
+ put_stringz(pktout, "tcpip-forward");
+ put_bool(pktout, true); /* want reply */
+ put_stringz(pktout, rpf->shost);
+ put_uint32(pktout, rpf->sport);
+ pq_push(s->ppl.out_pq, pktout);
+
+ ssh2_queue_global_request_handler(
+ s, ssh2_rportfwd_globreq_response, rpf);
+ }
+
+ return rpf;
+}
+
+void ssh2_rportfwd_remove(ConnectionLayer *cl, struct ssh_rportfwd *rpf)
+{
+ struct ssh2_connection_state *s =
+ container_of(cl, struct ssh2_connection_state, cl);
+
+ if (rpf->share_ctx) {
+ /*
+ * We don't manufacture a cancel-tcpip-forward message for
+ * remote port forwardings being removed on behalf of a
+ * downstream; we just pass through the one the downstream
+ * sent to us.
+ */
+ } else {
+ PktOut *pktout = ssh_bpp_new_pktout(
+ s->ppl.bpp, SSH2_MSG_GLOBAL_REQUEST);
+ put_stringz(pktout, "cancel-tcpip-forward");
+ put_bool(pktout, false); /* _don't_ want reply */
+ put_stringz(pktout, rpf->shost);
+ put_uint32(pktout, rpf->sport);
+ pq_push(s->ppl.out_pq, pktout);
+ }
+
+ assert(s->rportfwds);
+ struct ssh_rportfwd *realpf = del234(s->rportfwds, rpf);
+ assert(realpf == rpf);
+ free_rportfwd(rpf);
+}
+
+SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan)
+{
+ struct ssh2_connection_state *s =
+ container_of(cl, struct ssh2_connection_state, cl);
+ PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
+ struct ssh2_channel *c = snew(struct ssh2_channel);
+ PktOut *pktout;
+
+ c->connlayer = s;
+ ssh2_channel_init(c);
+ c->halfopen = true;
+ c->chan = chan;
+
+ ppl_logevent("Opening main session channel");
+
+ pktout = ssh2_chanopen_init(c, "session");
+ pq_push(s->ppl.out_pq, pktout);
+
+ return &c->sc;
+}
+
+SshChannel *ssh2_serverside_x11_open(
+ ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi)
+{
+ unreachable("Should never be called in the client");
+}
+
+SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan)
+{
+ unreachable("Should never be called in the client");
+}
+
+static void ssh2_channel_response(
+ struct ssh2_channel *c, PktIn *pkt, void *ctx)
+{
+ /* If pkt==NULL (because this handler has been called in response
+ * to CHANNEL_CLOSE arriving while the request was still
+ * outstanding), we treat that the same as CHANNEL_FAILURE. */
+ chan_request_response(c->chan,
+ pkt && pkt->type == SSH2_MSG_CHANNEL_SUCCESS);
+}
+
+void ssh2channel_start_shell(SshChannel *sc, bool want_reply)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+
+ PktOut *pktout = ssh2_chanreq_init(
+ c, "shell", want_reply ? ssh2_channel_response : NULL, NULL);
+ pq_push(s->ppl.out_pq, pktout);
+}
+
+void ssh2channel_start_command(
+ SshChannel *sc, bool want_reply, const char *command)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+
+ PktOut *pktout = ssh2_chanreq_init(
+ c, "exec", want_reply ? ssh2_channel_response : NULL, NULL);
+ put_stringz(pktout, command);
+ pq_push(s->ppl.out_pq, pktout);
+}
+
+bool ssh2channel_start_subsystem(
+ SshChannel *sc, bool want_reply, const char *subsystem)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+
+ PktOut *pktout = ssh2_chanreq_init(
+ c, "subsystem", want_reply ? ssh2_channel_response : NULL, NULL);
+ put_stringz(pktout, subsystem);
+ pq_push(s->ppl.out_pq, pktout);
+
+ return true;
+}
+
+void ssh2channel_send_exit_status(SshChannel *sc, int status)
+{
+ unreachable("Should never be called in the client");
+}
+
+void ssh2channel_send_exit_signal(
+ SshChannel *sc, ptrlen signame, bool core_dumped, ptrlen msg)
+{
+ unreachable("Should never be called in the client");
+}
+
+void ssh2channel_send_exit_signal_numeric(
+ SshChannel *sc, int signum, bool core_dumped, ptrlen msg)
+{
+ unreachable("Should never be called in the client");
+}
+
+void ssh2channel_request_x11_forwarding(
+ SshChannel *sc, bool want_reply, const char *authproto,
+ const char *authdata, int screen_number, bool oneshot)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+
+ PktOut *pktout = ssh2_chanreq_init(
+ c, "x11-req", want_reply ? ssh2_channel_response : NULL, NULL);
+ put_bool(pktout, oneshot);
+ put_stringz(pktout, authproto);
+ put_stringz(pktout, authdata);
+ put_uint32(pktout, screen_number);
+ pq_push(s->ppl.out_pq, pktout);
+}
+
+void ssh2channel_request_agent_forwarding(SshChannel *sc, bool want_reply)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+
+ PktOut *pktout = ssh2_chanreq_init(
+ c, "auth-agent-req@openssh.com",
+ want_reply ? ssh2_channel_response : NULL, NULL);
+ pq_push(s->ppl.out_pq, pktout);
+}
+
+void ssh2channel_request_pty(
+ SshChannel *sc, bool want_reply, Conf *conf, int w, int h)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+ strbuf *modebuf;
+
+ PktOut *pktout = ssh2_chanreq_init(
+ c, "pty-req", want_reply ? ssh2_channel_response : NULL, NULL);
+ put_stringz(pktout, conf_get_str(conf, CONF_termtype));
+ put_uint32(pktout, w);
+ put_uint32(pktout, h);
+ put_uint32(pktout, 0); /* pixel width */
+ put_uint32(pktout, 0); /* pixel height */
+ modebuf = strbuf_new();
+ write_ttymodes_to_packet(
+ BinarySink_UPCAST(modebuf), 2,
+ get_ttymodes_from_conf(s->ppl.seat, conf));
+ put_stringsb(pktout, modebuf);
+ pq_push(s->ppl.out_pq, pktout);
+}
+
+bool ssh2channel_send_env_var(
+ SshChannel *sc, bool want_reply, const char *var, const char *value)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+
+ PktOut *pktout = ssh2_chanreq_init(
+ c, "env", want_reply ? ssh2_channel_response : NULL, NULL);
+ put_stringz(pktout, var);
+ put_stringz(pktout, value);
+ pq_push(s->ppl.out_pq, pktout);
+
+ return true;
+}
+
+bool ssh2channel_send_serial_break(SshChannel *sc, bool want_reply, int length)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+
+ PktOut *pktout = ssh2_chanreq_init(
+ c, "break", want_reply ? ssh2_channel_response : NULL, NULL);
+ put_uint32(pktout, length);
+ pq_push(s->ppl.out_pq, pktout);
+
+ return true;
+}
+
+bool ssh2channel_send_signal(
+ SshChannel *sc, bool want_reply, const char *signame)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+
+ PktOut *pktout = ssh2_chanreq_init(
+ c, "signal", want_reply ? ssh2_channel_response : NULL, NULL);
+ put_stringz(pktout, signame);
+ pq_push(s->ppl.out_pq, pktout);
+
+ return true;
+}
+
+void ssh2channel_send_terminal_size_change(SshChannel *sc, int w, int h)
+{
+ struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
+ struct ssh2_connection_state *s = c->connlayer;
+
+ PktOut *pktout = ssh2_chanreq_init(c, "window-change", NULL, NULL);
+ put_uint32(pktout, w);
+ put_uint32(pktout, h);
+ put_uint32(pktout, 0); /* pixel width */
+ put_uint32(pktout, 0); /* pixel height */
+ pq_push(s->ppl.out_pq, pktout);
+}
+
+bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s)
+{
+ seat_set_trust_status(s->ppl.seat, false);
+ if (!seat_has_mixed_input_stream(s->ppl.seat))
+ return false;
+ if (seat_can_set_trust_status(s->ppl.seat))
+ return false;
+ if (ssh_is_bare(s->ppl.ssh))
+ return false;
+ return true;
+}