123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- /*
- * Client-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"
- void ssh1_connection_direction_specific_setup(
- struct ssh1_connection_state *s)
- {
- if (!s->mainchan) {
- /*
- * Start up the main session, by telling mainchan.c to do it
- * all just as it would in SSH-2, and translating those
- * concepts to SSH-1's non-channel-shaped idea of the main
- * session.
- */
- s->mainchan = mainchan_new(
- &s->ppl, &s->cl, s->conf, s->term_width, s->term_height,
- false /* is_simple */, NULL);
- }
- }
- typedef void (*sf_handler_fn_t)(struct ssh1_connection_state *s,
- bool success, void *ctx);
- struct outstanding_succfail {
- sf_handler_fn_t handler;
- void *ctx;
- struct outstanding_succfail *next;
- /*
- * The 'trivial' flag is set if this handler is in response to a
- * request for which the SSH-1 protocol doesn't actually specify a
- * response packet. The client of this system (mainchan.c) will
- * expect to get an acknowledgment regardless, so we arrange to
- * send that ack immediately after the rest of the queue empties.
- */
- bool trivial;
- };
- static void ssh1_connection_process_trivial_succfails(void *vs);
- static void ssh1_queue_succfail_handler(
- struct ssh1_connection_state *s, sf_handler_fn_t handler, void *ctx,
- bool trivial)
- {
- struct outstanding_succfail *osf = snew(struct outstanding_succfail);
- osf->handler = handler;
- osf->ctx = ctx;
- osf->trivial = trivial;
- osf->next = NULL;
- if (s->succfail_tail)
- s->succfail_tail->next = osf;
- else
- s->succfail_head = osf;
- s->succfail_tail = osf;
- /* In case this one was trivial and the queue was already empty,
- * we should make sure we run the handler promptly, and the
- * easiest way is to queue it anyway and then run a trivials pass
- * by callback. */
- queue_toplevel_callback(ssh1_connection_process_trivial_succfails, s);
- }
- static void ssh1_connection_process_succfail(
- struct ssh1_connection_state *s, bool success)
- {
- struct outstanding_succfail *prevhead = s->succfail_head;
- s->succfail_head = s->succfail_head->next;
- if (!s->succfail_head)
- s->succfail_tail = NULL;
- prevhead->handler(s, success, prevhead->ctx);
- sfree(prevhead);
- }
- static void ssh1_connection_process_trivial_succfails(void *vs)
- {
- struct ssh1_connection_state *s = (struct ssh1_connection_state *)vs;
- while (s->succfail_head && s->succfail_head->trivial)
- ssh1_connection_process_succfail(s, true);
- }
- 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;
- struct ssh_rportfwd pf, *pfp;
- ptrlen host, data;
- int port;
- switch (pktin->type) {
- case SSH1_SMSG_SUCCESS:
- case SSH1_SMSG_FAILURE:
- if (!s->succfail_head) {
- ssh_remote_error(s->ppl.ssh,
- "Received %s with no outstanding request",
- ssh1_pkt_type(pktin->type));
- return true;
- }
- ssh1_connection_process_succfail(
- s, pktin->type == SSH1_SMSG_SUCCESS);
- queue_toplevel_callback(
- ssh1_connection_process_trivial_succfails, s);
- return true;
- case SSH1_SMSG_X11_OPEN:
- remid = get_uint32(pktin);
- /* Refuse if X11 forwarding is disabled. */
- if (!s->X11_fwd_enabled) {
- pktout = ssh_bpp_new_pktout(
- s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
- put_uint32(pktout, remid);
- pq_push(s->ppl.out_pq, pktout);
- ppl_logevent("Rejected X11 connect request");
- } else {
- c = snew(struct ssh1_channel);
- c->connlayer = s;
- ssh1_channel_init(c);
- c->remoteid = remid;
- c->chan = x11_new_channel(s->x11authtree, &c->sc,
- NULL, -1, false);
- 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("Opened X11 forward channel");
- }
- return true;
- case SSH1_SMSG_AGENT_OPEN:
- remid = get_uint32(pktin);
- /* Refuse if agent forwarding is disabled. */
- if (!ssh_agent_forwarding_permitted(&s->cl)) {
- 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 {
- c = snew(struct ssh1_channel);
- c->connlayer = s;
- ssh1_channel_init(c);
- c->remoteid = remid;
- c->halfopen = false;
- /*
- * 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, &c->sc);
- c->chan = 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().
- */
- c->chan = agentf_new(&c->sc);
- }
- 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);
- }
- return true;
- case SSH1_MSG_PORT_OPEN:
- remid = get_uint32(pktin);
- host = get_string(pktin);
- port = toint(get_uint32(pktin));
- pf.dhost = mkstr(host);
- pf.dport = port;
- pfp = find234(s->rportfwds, &pf, NULL);
- if (!pfp) {
- ppl_logevent("Rejected remote port open request for %s:%d",
- pf.dhost, port);
- 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 {
- char *err;
- c = snew(struct ssh1_channel);
- c->connlayer = s;
- ppl_logevent("Received remote port open request for %s:%d",
- pf.dhost, port);
- err = portfwdmgr_connect(
- s->portfwdmgr, &c->chan, pf.dhost, port,
- &c->sc, pfp->addressfamily);
- 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");
- }
- }
- sfree(pf.dhost);
- return true;
- case SSH1_SMSG_STDOUT_DATA:
- case SSH1_SMSG_STDERR_DATA:
- data = get_string(pktin);
- if (!get_err(pktin)) {
- int bufsize = seat_output(
- s->ppl.seat, pktin->type == SSH1_SMSG_STDERR_DATA,
- data.ptr, data.len);
- if (!s->stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
- s->stdout_throttling = true;
- ssh_throttle_conn(s->ppl.ssh, +1);
- }
- }
- return true;
- case SSH1_SMSG_EXIT_STATUS: {
- int exitcode = get_uint32(pktin);
- ppl_logevent("Server sent command exit status %d", exitcode);
- ssh_got_exitcode(s->ppl.ssh, exitcode);
- s->session_terminated = true;
- return true;
- }
- default:
- return false;
- }
- }
- static void ssh1mainchan_succfail_wantreply(struct ssh1_connection_state *s,
- bool success, void *ctx)
- {
- chan_request_response(s->mainchan_chan, success);
- }
- static void ssh1mainchan_succfail_nowantreply(struct ssh1_connection_state *s,
- bool success, void *ctx)
- {
- }
- static void ssh1mainchan_queue_response(struct ssh1_connection_state *s,
- bool want_reply, bool trivial)
- {
- sf_handler_fn_t handler = (want_reply ? ssh1mainchan_succfail_wantreply :
- ssh1mainchan_succfail_nowantreply);
- ssh1_queue_succfail_handler(s, handler, NULL, trivial);
- }
- static void ssh1mainchan_request_x11_forwarding(
- SshChannel *sc, bool want_reply, const char *authproto,
- const char *authdata, int screen_number, bool oneshot)
- {
- 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_CMSG_X11_REQUEST_FORWARDING);
- put_stringz(pktout, authproto);
- put_stringz(pktout, authdata);
- if (s->local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER)
- put_uint32(pktout, screen_number);
- pq_push(s->ppl.out_pq, pktout);
- ssh1mainchan_queue_response(s, want_reply, false);
- }
- static void ssh1mainchan_request_agent_forwarding(
- SshChannel *sc, bool want_reply)
- {
- 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_CMSG_AGENT_REQUEST_FORWARDING);
- pq_push(s->ppl.out_pq, pktout);
- ssh1mainchan_queue_response(s, want_reply, false);
- }
- static void ssh1mainchan_request_pty(
- SshChannel *sc, bool want_reply, Conf *conf, int w, int h)
- {
- 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_CMSG_REQUEST_PTY);
- put_stringz(pktout, conf_get_str(s->conf, CONF_termtype));
- put_uint32(pktout, h);
- put_uint32(pktout, w);
- put_uint32(pktout, 0); /* width in pixels */
- put_uint32(pktout, 0); /* height in pixels */
- write_ttymodes_to_packet(
- BinarySink_UPCAST(pktout), 1,
- get_ttymodes_from_conf(s->ppl.seat, conf));
- pq_push(s->ppl.out_pq, pktout);
- ssh1mainchan_queue_response(s, want_reply, false);
- }
- static bool ssh1mainchan_send_env_var(
- SshChannel *sc, bool want_reply, const char *var, const char *value)
- {
- return false; /* SSH-1 doesn't support this at all */
- }
- static void ssh1mainchan_start_shell(SshChannel *sc, bool want_reply)
- {
- 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_CMSG_EXEC_SHELL);
- pq_push(s->ppl.out_pq, pktout);
- ssh1mainchan_queue_response(s, want_reply, true);
- }
- static void ssh1mainchan_start_command(
- SshChannel *sc, bool want_reply, const char *command)
- {
- 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_CMSG_EXEC_CMD);
- put_stringz(pktout, command);
- pq_push(s->ppl.out_pq, pktout);
- ssh1mainchan_queue_response(s, want_reply, true);
- }
- static bool ssh1mainchan_start_subsystem(
- SshChannel *sc, bool want_reply, const char *subsystem)
- {
- return false; /* SSH-1 doesn't support this at all */
- }
- static bool ssh1mainchan_send_serial_break(
- SshChannel *sc, bool want_reply, int length)
- {
- return false; /* SSH-1 doesn't support this at all */
- }
- static bool ssh1mainchan_send_signal(
- SshChannel *sc, bool want_reply, const char *signame)
- {
- return false; /* SSH-1 doesn't support this at all */
- }
- static void ssh1mainchan_send_terminal_size_change(
- SshChannel *sc, int w, int h)
- {
- 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_CMSG_WINDOW_SIZE);
- put_uint32(pktout, h);
- put_uint32(pktout, w);
- put_uint32(pktout, 0); /* width in pixels */
- put_uint32(pktout, 0); /* height in pixels */
- pq_push(s->ppl.out_pq, pktout);
- }
- static void ssh1mainchan_hint_channel_is_simple(SshChannel *sc)
- {
- }
- static size_t ssh1mainchan_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, SSH1_CMSG_STDIN_DATA);
- put_string(pktout, data, len);
- pq_push(s->ppl.out_pq, pktout);
- return 0;
- }
- static void ssh1mainchan_write_eof(SshChannel *sc)
- {
- 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_CMSG_EOF);
- pq_push(s->ppl.out_pq, pktout);
- }
- static const SshChannelVtable ssh1mainchan_vtable = {
- .write = ssh1mainchan_write,
- .write_eof = ssh1mainchan_write_eof,
- .request_x11_forwarding = ssh1mainchan_request_x11_forwarding,
- .request_agent_forwarding = ssh1mainchan_request_agent_forwarding,
- .request_pty = ssh1mainchan_request_pty,
- .send_env_var = ssh1mainchan_send_env_var,
- .start_shell = ssh1mainchan_start_shell,
- .start_command = ssh1mainchan_start_command,
- .start_subsystem = ssh1mainchan_start_subsystem,
- .send_serial_break = ssh1mainchan_send_serial_break,
- .send_signal = ssh1mainchan_send_signal,
- .send_terminal_size_change = ssh1mainchan_send_terminal_size_change,
- .hint_channel_is_simple = ssh1mainchan_hint_channel_is_simple,
- /* other methods are NULL */
- };
- static void ssh1_session_confirm_callback(void *vctx)
- {
- struct ssh1_connection_state *s = (struct ssh1_connection_state *)vctx;
- chan_open_confirmation(s->mainchan_chan);
- }
- SshChannel *ssh1_session_open(ConnectionLayer *cl, Channel *chan)
- {
- struct ssh1_connection_state *s =
- container_of(cl, struct ssh1_connection_state, cl);
- s->mainchan_sc.vt = &ssh1mainchan_vtable;
- s->mainchan_sc.cl = &s->cl;
- s->mainchan_chan = chan;
- queue_toplevel_callback(ssh1_session_confirm_callback, s);
- return &s->mainchan_sc;
- }
- static void ssh1_rportfwd_response(struct ssh1_connection_state *s,
- bool success, void *ctx)
- {
- PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
- struct ssh_rportfwd *rpf = (struct ssh_rportfwd *)ctx;
- if (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 *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)
- {
- struct ssh1_connection_state *s =
- container_of(cl, struct ssh1_connection_state, cl);
- struct ssh_rportfwd *rpf = snew(struct ssh_rportfwd);
- 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;
- if (add234(s->rportfwds, rpf) != rpf) {
- free_rportfwd(rpf);
- return NULL;
- }
- PktOut *pktout = ssh_bpp_new_pktout(
- s->ppl.bpp, SSH1_CMSG_PORT_FORWARD_REQUEST);
- put_uint32(pktout, rpf->sport);
- put_stringz(pktout, rpf->dhost);
- put_uint32(pktout, rpf->dport);
- pq_push(s->ppl.out_pq, pktout);
- ssh1_queue_succfail_handler(s, ssh1_rportfwd_response, rpf, false);
- return rpf;
- }
- SshChannel *ssh1_serverside_x11_open(
- ConnectionLayer *cl, Channel *chan, const SocketEndpointInfo *pi)
- {
- unreachable("Should never be called in the client");
- }
- SshChannel *ssh1_serverside_agent_open(ConnectionLayer *cl, Channel *chan)
- {
- unreachable("Should never be called in the client");
- }
- bool ssh1_connection_need_antispoof_prompt(struct ssh1_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;
- return true;
- }
|