123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780 |
- /*
- * sshproxy.c: implement a Socket type that talks to an entire
- * subsidiary SSH connection (sometimes called a 'jump host').
- */
- #include <stdio.h>
- #include <assert.h>
- #include "putty.h"
- #include "ssh.h"
- #include "network.h"
- #include "storage.h"
- #include "proxy.h"
- const bool ssh_proxy_supported = true;
- typedef struct SshProxy {
- char *errmsg;
- Conf *conf;
- LogContext *logctx;
- Backend *backend;
- LogPolicy *clientlp;
- Seat *clientseat;
- Interactor *clientitr;
- bool got_proxy_password, tried_proxy_password;
- char *proxy_password;
- ProxyStderrBuf psb;
- Plug *plug;
- bool frozen;
- bufchain ssh_to_socket;
- bool rcvd_eof_ssh_to_socket, sent_eof_ssh_to_socket;
- bool conn_established;
- SockAddr *addr;
- int port;
- /* Traits implemented: we're a Socket from the point of view of
- * the client connection, and a Seat from the POV of the SSH
- * backend we instantiate. */
- Socket sock;
- LogPolicy logpolicy;
- Seat seat;
- } SshProxy;
- static Plug *sshproxy_plug(Socket *s, Plug *p)
- {
- SshProxy *sp = container_of(s, SshProxy, sock);
- Plug *oldplug = sp->plug;
- if (p)
- sp->plug = p;
- return oldplug;
- }
- static void sshproxy_close(Socket *s)
- {
- SshProxy *sp = container_of(s, SshProxy, sock);
- sk_addr_free(sp->addr);
- sfree(sp->errmsg);
- conf_free(sp->conf);
- if (sp->backend)
- backend_free(sp->backend);
- if (sp->logctx)
- log_free(sp->logctx);
- if (sp->proxy_password)
- burnstr(sp->proxy_password);
- bufchain_clear(&sp->ssh_to_socket);
- delete_callbacks_for_context(sp);
- sfree(sp);
- }
- static size_t sshproxy_write(Socket *s, const void *data, size_t len)
- {
- SshProxy *sp = container_of(s, SshProxy, sock);
- if (!sp->backend)
- return 0;
- backend_send(sp->backend, data, len);
- return backend_sendbuffer(sp->backend);
- }
- static size_t sshproxy_write_oob(Socket *s, const void *data, size_t len)
- {
- /*
- * oob data is treated as inband; nasty, but nothing really
- * better we can do
- */
- return sshproxy_write(s, data, len);
- }
- static void sshproxy_write_eof(Socket *s)
- {
- SshProxy *sp = container_of(s, SshProxy, sock);
- if (!sp->backend)
- return;
- backend_special(sp->backend, SS_EOF, 0);
- }
- static void try_send_ssh_to_socket(void *ctx);
- static void try_send_ssh_to_socket_cb(void *ctx)
- {
- SshProxy *sp = (SshProxy *)ctx;
- try_send_ssh_to_socket(sp);
- if (sp->backend)
- backend_unthrottle(sp->backend, bufchain_size(&sp->ssh_to_socket));
- }
- static void sshproxy_set_frozen(Socket *s, bool is_frozen)
- {
- SshProxy *sp = container_of(s, SshProxy, sock);
- sp->frozen = is_frozen;
- if (!sp->frozen)
- queue_toplevel_callback(try_send_ssh_to_socket_cb, sp);
- }
- static const char *sshproxy_socket_error(Socket *s)
- {
- SshProxy *sp = container_of(s, SshProxy, sock);
- return sp->errmsg;
- }
- static const SocketVtable SshProxy_sock_vt = {
- .plug = sshproxy_plug,
- .close = sshproxy_close,
- .write = sshproxy_write,
- .write_oob = sshproxy_write_oob,
- .write_eof = sshproxy_write_eof,
- .set_frozen = sshproxy_set_frozen,
- .socket_error = sshproxy_socket_error,
- .endpoint_info = nullsock_endpoint_info,
- };
- static void sshproxy_eventlog(LogPolicy *lp, const char *event)
- {
- SshProxy *sp = container_of(lp, SshProxy, logpolicy);
- log_proxy_stderr(sp->plug, &sp->sock, &sp->psb, event, strlen(event));
- log_proxy_stderr(sp->plug, &sp->sock, &sp->psb, "\n", 1);
- }
- static int sshproxy_askappend(LogPolicy *lp, Filename *filename,
- void (*callback)(void *ctx, int result),
- void *ctx)
- {
- SshProxy *sp = container_of(lp, SshProxy, logpolicy);
- /*
- * If we have access to the outer LogPolicy, pass on this request
- * to the end user.
- */
- if (sp->clientlp)
- return lp_askappend(sp->clientlp, filename, callback, ctx);
- /*
- * Otherwise, fall back to the safe noninteractive assumption.
- */
- char *msg = dupprintf("Log file \"%s\" already exists; logging cancelled",
- filename_to_str(filename));
- sshproxy_eventlog(lp, msg);
- sfree(msg);
- return 0;
- }
- static void sshproxy_logging_error(LogPolicy *lp, const char *event)
- {
- SshProxy *sp = container_of(lp, SshProxy, logpolicy);
- /*
- * If we have access to the outer LogPolicy, pass on this request
- * to it.
- */
- if (sp->clientlp) {
- lp_logging_error(sp->clientlp, event);
- return;
- }
- /*
- * Otherwise, the best we can do is to put it in the outer SSH
- * connection's Event Log.
- */
- char *msg = dupprintf("Logging error: %s", event);
- sshproxy_eventlog(lp, msg);
- sfree(msg);
- }
- static const LogPolicyVtable SshProxy_logpolicy_vt = {
- .eventlog = sshproxy_eventlog,
- .askappend = sshproxy_askappend,
- .logging_error = sshproxy_logging_error,
- .verbose = null_lp_verbose_no,
- };
- /*
- * Function called when we encounter an error during connection setup that's
- * likely to be the cause of terminating the proxy SSH connection. Putting it
- * in the Event Log is useful on general principles; also putting it in
- * sp->errmsg meaks that it will be passed back through plug_closing when the
- * proxy SSH connection actually terminates, so that the end user will see
- * what went wrong in the proxy connection.
- */
- static void sshproxy_error(SshProxy *sp, const char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- char *msg = dupvprintf(fmt, ap);
- va_end(ap);
- if (!sp->errmsg)
- sp->errmsg = dupstr(msg);
- sshproxy_eventlog(&sp->logpolicy, msg);
- sfree(msg);
- }
- static void try_send_ssh_to_socket(void *ctx)
- {
- SshProxy *sp = (SshProxy *)ctx;
- if (sp->frozen)
- return;
- while (bufchain_size(&sp->ssh_to_socket)) {
- ptrlen pl = bufchain_prefix(&sp->ssh_to_socket);
- plug_receive(sp->plug, 0, pl.ptr, pl.len);
- bufchain_consume(&sp->ssh_to_socket, pl.len);
- }
- if (sp->rcvd_eof_ssh_to_socket &&
- !sp->sent_eof_ssh_to_socket) {
- sp->sent_eof_ssh_to_socket = true;
- plug_closing_normal(sp->plug);
- }
- }
- static void sshproxy_notify_session_started(Seat *seat)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- if (sp->clientseat)
- interactor_return_seat(sp->clientitr);
- sp->conn_established = true;
- plug_log(sp->plug, &sp->sock, PLUGLOG_CONNECT_SUCCESS, sp->addr, sp->port,
- NULL, 0);
- }
- static size_t sshproxy_output(Seat *seat, SeatOutputType type,
- const void *data, size_t len)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- switch (type) {
- case SEAT_OUTPUT_STDOUT:
- bufchain_add(&sp->ssh_to_socket, data, len);
- try_send_ssh_to_socket(sp);
- break;
- case SEAT_OUTPUT_STDERR:
- log_proxy_stderr(sp->plug, &sp->sock, &sp->psb, data, len);
- break;
- }
- return bufchain_size(&sp->ssh_to_socket);
- }
- static inline InteractionReadySeat wrap(Seat *seat)
- {
- /*
- * When we receive interaction requests from the proxy and want to
- * pass them on to our client Seat, we have to present them to the
- * latter in the form of an InteractionReadySeat. This forwarding
- * scenario is the one case where we _mustn't_ get an
- * InteractionReadySeat by calling interactor_announce(), because
- * the point is that we're _not_ the originating Interactor, we're
- * just forwarding the request from the real one, which has
- * already announced itself.
- *
- * So, just here in the code, it really is the right thing to make
- * an InteractionReadySeat out of a plain Seat * without an
- * announcement.
- */
- InteractionReadySeat iseat;
- iseat.seat = seat;
- return iseat;
- }
- static size_t sshproxy_banner(Seat *seat, const void *data, size_t len)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- if (sp->clientseat) {
- /*
- * If we have access to the outer Seat, pass the SSH login
- * banner on to it.
- */
- return seat_banner(wrap(sp->clientseat), data, len);
- } else {
- return 0;
- }
- }
- static bool sshproxy_eof(Seat *seat)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- sp->rcvd_eof_ssh_to_socket = true;
- try_send_ssh_to_socket(sp);
- return false;
- }
- static void sshproxy_sent(Seat *seat, size_t new_bufsize)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- plug_sent(sp->plug, new_bufsize);
- }
- static void sshproxy_send_close(SshProxy *sp)
- {
- if (sp->clientseat)
- interactor_return_seat(sp->clientitr);
- if (!sp->conn_established)
- plug_log(sp->plug, &sp->sock, PLUGLOG_CONNECT_FAILED, sp->addr,
- sp->port, sp->errmsg, 0);
- if (sp->errmsg)
- plug_closing_error(sp->plug, sp->errmsg);
- else if (!sp->conn_established && backend_exitcode(sp->backend) == 0)
- plug_closing_user_abort(sp->plug);
- else
- plug_closing_normal(sp->plug);
- }
- static void sshproxy_notify_remote_disconnect_callback(void *vctx)
- {
- SshProxy *sp = (SshProxy *)vctx;
- /* notify_remote_disconnect can be called redundantly, so first
- * check if the backend really has become disconnected */
- if (backend_connected(sp->backend))
- return;
- sshproxy_send_close(sp);
- }
- static void sshproxy_notify_remote_disconnect(Seat *seat)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- queue_toplevel_callback(sshproxy_notify_remote_disconnect_callback, sp);
- }
- static SeatPromptResult sshproxy_get_userpass_input(Seat *seat, prompts_t *p)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- /*
- * If we have a stored proxy_password, use that, via logic similar
- * to cmdline_get_passwd_input: we only try it if we're given a
- * prompts_t containing exactly one prompt, and that prompt is set
- * to non-echoing.
- */
- if (sp->got_proxy_password && !sp->tried_proxy_password &&
- p->n_prompts == 1 && !p->prompts[0]->echo) {
- prompt_set_result(p->prompts[0], sp->proxy_password);
- burnstr(sp->proxy_password);
- sp->proxy_password = NULL;
- sp->tried_proxy_password = true;
- return SPR_OK;
- }
- if (sp->clientseat) {
- /*
- * If we have access to the outer Seat, pass this prompt
- * request on to it.
- */
- return seat_get_userpass_input(wrap(sp->clientseat), p);
- }
- /*
- * Otherwise, behave as if noninteractive (like plink -batch):
- * reject all attempts to present a prompt to the user, and log in
- * the Event Log to say why not.
- */
- sshproxy_error(sp, "Unable to provide interactive authentication "
- "requested by proxy SSH connection");
- return SPR_SW_ABORT("Noninteractive SSH proxy cannot perform "
- "interactive authentication");
- }
- static void sshproxy_connection_fatal_callback(void *vctx)
- {
- SshProxy *sp = (SshProxy *)vctx;
- sshproxy_send_close(sp);
- }
- static void sshproxy_connection_fatal(Seat *seat, const char *message)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- if (!sp->errmsg) {
- sp->errmsg = dupprintf(
- "fatal error in proxy SSH connection: %s", message);
- queue_toplevel_callback(sshproxy_connection_fatal_callback, sp);
- }
- }
- static void sshproxy_nonfatal(Seat *seat, const char *message)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- if (sp->clientseat)
- seat_nonfatal(sp->clientseat, "error in proxy SSH connection: %s",
- message);
- }
- static SeatPromptResult sshproxy_confirm_ssh_host_key(
- Seat *seat, const char *host, int port, const char *keytype,
- char *keystr, SeatDialogText *text, HelpCtx helpctx,
- void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- if (sp->clientseat) {
- /*
- * If we have access to the outer Seat, pass this prompt
- * request on to it.
- */
- return seat_confirm_ssh_host_key(
- wrap(sp->clientseat), host, port, keytype, keystr, text,
- helpctx, callback, ctx);
- }
- /*
- * Otherwise, behave as if we're in batch mode, i.e. take the safe
- * option in the absence of interactive confirmation, i.e. abort
- * the connection.
- */
- return SPR_SW_ABORT("Noninteractive SSH proxy cannot confirm host key");
- }
- static void sshproxy_format_seatdialogtext(strbuf *sb, SeatDialogText *text)
- {
- for (SeatDialogTextItem *item = text->items,
- *end = item+text->nitems; item < end; item++) {
- switch (item->type) {
- case SDT_SCARY_HEADING:
- case SDT_PARA:
- case SDT_DISPLAY:
- put_stringz(sb, item->text);
- put_byte(sb, '\n');
- break;
- case SDT_BATCH_ABORT:
- put_stringz(sb, item->text);
- put_byte(sb, '\n');
- goto endloop;
- default:
- break;
- }
- }
- endloop:
- while (strbuf_chomp(sb, '\n'));
- }
- static SeatPromptResult sshproxy_confirm_weak_crypto_primitive(
- Seat *seat, SeatDialogText *text,
- void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- if (sp->clientseat) {
- /*
- * If we have access to the outer Seat, pass this prompt
- * request on to it.
- */
- return seat_confirm_weak_crypto_primitive(
- wrap(sp->clientseat), text, callback, ctx);
- }
- /*
- * Otherwise, behave as if we're in batch mode: take the safest
- * option.
- */
- strbuf *sb = strbuf_new();
- sshproxy_format_seatdialogtext(sb, text);
- sshproxy_error(sp, sb->s);
- strbuf_free(sb);
- return SPR_SW_ABORT("Noninteractive SSH proxy cannot confirm "
- "weak crypto primitive");
- }
- static SeatPromptResult sshproxy_confirm_weak_cached_hostkey(
- Seat *seat, SeatDialogText *text,
- void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- if (sp->clientseat) {
- /*
- * If we have access to the outer Seat, pass this prompt
- * request on to it.
- */
- return seat_confirm_weak_cached_hostkey(
- wrap(sp->clientseat), text, callback, ctx);
- }
- /*
- * Otherwise, behave as if we're in batch mode: take the safest
- * option.
- */
- strbuf *sb = strbuf_new();
- sshproxy_format_seatdialogtext(sb, text);
- sshproxy_error(sp, sb->s);
- strbuf_free(sb);
- return SPR_SW_ABORT("Noninteractive SSH proxy cannot confirm "
- "weak cached host key");
- }
- static const SeatDialogPromptDescriptions *sshproxy_prompt_descriptions(
- Seat *seat)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- /* If we have a client seat, return their prompt descriptions, so
- * that prompts passed on to them will make sense. */
- if (sp->clientseat)
- return seat_prompt_descriptions(sp->clientseat);
- /* Otherwise, it doesn't matter what we return, so do the easiest thing. */
- return nullseat_prompt_descriptions(NULL);
- }
- static StripCtrlChars *sshproxy_stripctrl_new(
- Seat *seat, BinarySink *bs_out, SeatInteractionContext sic)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- if (sp->clientseat)
- return seat_stripctrl_new(sp->clientseat, bs_out, sic);
- else
- return NULL;
- }
- static void sshproxy_set_trust_status(Seat *seat, bool trusted)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- if (sp->clientseat)
- seat_set_trust_status(sp->clientseat, trusted);
- }
- static bool sshproxy_can_set_trust_status(Seat *seat)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- return sp->clientseat && seat_can_set_trust_status(sp->clientseat);
- }
- static bool sshproxy_verbose(Seat *seat)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- return sp->clientseat && seat_verbose(sp->clientseat);
- }
- static bool sshproxy_interactive(Seat *seat)
- {
- SshProxy *sp = container_of(seat, SshProxy, seat);
- return sp->clientseat && seat_interactive(sp->clientseat);
- }
- static const SeatVtable SshProxy_seat_vt = {
- .output = sshproxy_output,
- .eof = sshproxy_eof,
- .sent = sshproxy_sent,
- .banner = sshproxy_banner,
- .get_userpass_input = sshproxy_get_userpass_input,
- .notify_session_started = sshproxy_notify_session_started,
- .notify_remote_exit = nullseat_notify_remote_exit,
- .notify_remote_disconnect = sshproxy_notify_remote_disconnect,
- .connection_fatal = sshproxy_connection_fatal,
- .nonfatal = sshproxy_nonfatal,
- .update_specials_menu = nullseat_update_specials_menu,
- .get_ttymode = nullseat_get_ttymode,
- .set_busy_status = nullseat_set_busy_status,
- .confirm_ssh_host_key = sshproxy_confirm_ssh_host_key,
- .confirm_weak_crypto_primitive = sshproxy_confirm_weak_crypto_primitive,
- .confirm_weak_cached_hostkey = sshproxy_confirm_weak_cached_hostkey,
- .prompt_descriptions = sshproxy_prompt_descriptions,
- .is_utf8 = nullseat_is_never_utf8,
- .echoedit_update = nullseat_echoedit_update,
- .get_x_display = nullseat_get_x_display,
- .get_windowid = nullseat_get_windowid,
- .get_window_pixel_size = nullseat_get_window_pixel_size,
- .stripctrl_new = sshproxy_stripctrl_new,
- .set_trust_status = sshproxy_set_trust_status,
- .can_set_trust_status = sshproxy_can_set_trust_status,
- .has_mixed_input_stream = nullseat_has_mixed_input_stream_no,
- .verbose = sshproxy_verbose,
- .interactive = sshproxy_interactive,
- .get_cursor_position = nullseat_get_cursor_position,
- };
- Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
- int port, bool privport,
- bool oobinline, bool nodelay, bool keepalive,
- Plug *plug, Conf *clientconf,
- Interactor *clientitr)
- {
- SshProxy *sp = snew(SshProxy);
- memset(sp, 0, sizeof(*sp));
- sp->sock.vt = &SshProxy_sock_vt;
- sp->logpolicy.vt = &SshProxy_logpolicy_vt;
- sp->seat.vt = &SshProxy_seat_vt;
- sp->plug = plug;
- psb_init(&sp->psb);
- bufchain_init(&sp->ssh_to_socket);
- sp->addr = addr;
- sp->port = port;
- sp->conf = conf_new();
- /* Try to treat proxy_hostname as the title of a saved session. If
- * that fails, set up a default Conf of our own treating it as a
- * hostname. */
- const char *proxy_hostname = conf_get_str(clientconf, CONF_proxy_host);
- if (do_defaults(proxy_hostname, sp->conf)) {
- if (!conf_launchable(sp->conf)) {
- sp->errmsg = dupprintf("saved session '%s' is not launchable",
- proxy_hostname);
- return &sp->sock;
- }
- } else {
- do_defaults(NULL, sp->conf);
- /* In hostname mode, we default to PROT_SSH. This is more useful than
- * the obvious approach of defaulting to the protocol defined in
- * Default Settings, because only SSH (ok, and bare ssh-connection)
- * can be used for this kind of proxy. */
- conf_set_int(sp->conf, CONF_protocol, PROT_SSH);
- conf_set_str(sp->conf, CONF_host, proxy_hostname);
- conf_set_int(sp->conf, CONF_port,
- conf_get_int(clientconf, CONF_proxy_port));
- }
- const char *proxy_username = conf_get_str(clientconf, CONF_proxy_username);
- if (*proxy_username)
- conf_set_str(sp->conf, CONF_username, proxy_username);
- const char *proxy_password = conf_get_str(clientconf, CONF_proxy_password);
- if (*proxy_password) {
- sp->proxy_password = dupstr(proxy_password);
- sp->got_proxy_password = true;
- }
- const struct BackendVtable *backvt = backend_vt_from_proto(
- conf_get_int(sp->conf, CONF_protocol));
- /*
- * We don't actually need an _SSH_ session specifically: it's also
- * OK to use PROT_SSHCONN, because really, the criterion is
- * whether setting CONF_ssh_nc_host will do anything useful. So
- * our check is for whether the backend sets the flag promising
- * that it does.
- */
- if (!backvt || !(backvt->flags & BACKEND_SUPPORTS_NC_HOST)) {
- sp->errmsg = dupprintf("saved session '%s' is not an SSH session",
- proxy_hostname);
- return &sp->sock;
- }
- /*
- * We also expect that the backend will announce a willingness to
- * notify us that the session has started. Any backend providing
- * NC_HOST should also provide this.
- */
- assert(backvt->flags & BACKEND_NOTIFIES_SESSION_START &&
- "Backend provides NC_HOST without SESSION_START!");
- /*
- * Turn off SSH features we definitely don't want. It would be
- * awkward and counterintuitive to have the proxy SSH connection
- * become a connection-sharing upstream (but it's fine to have it
- * be a downstream, if that's configured). And we don't want to
- * open X forwardings, agent forwardings or (other) port
- * forwardings as a side effect of this one operation.
- */
- conf_set_bool(sp->conf, CONF_ssh_connection_sharing_upstream, false);
- conf_set_bool(sp->conf, CONF_x11_forward, false);
- conf_set_bool(sp->conf, CONF_agentfwd, false);
- for (const char *subkey;
- (subkey = conf_get_str_nthstrkey(sp->conf, CONF_portfwd, 0)) != NULL;)
- conf_del_str_str(sp->conf, CONF_portfwd, subkey);
- /*
- * We'll only be running one channel through this connection
- * (since we've just turned off all the other things we might have
- * done with it), so we can configure it as simple.
- */
- conf_set_bool(sp->conf, CONF_ssh_simple, true);
- int proxy_type = conf_get_int(clientconf, CONF_proxy_type);
- switch (proxy_type) {
- case PROXY_SSH_TCPIP:
- /*
- * Configure the main channel of this SSH session to be a
- * direct-tcpip connection to the destination host/port.
- */
- conf_set_str(sp->conf, CONF_ssh_nc_host, hostname);
- conf_set_int(sp->conf, CONF_ssh_nc_port, port);
- break;
- case PROXY_SSH_SUBSYSTEM:
- case PROXY_SSH_EXEC: {
- Conf *cmd_conf = conf_copy(clientconf);
- /*
- * Unlike the Telnet and Local proxy types, we don't use the
- * proxy username and password fields in the formatted
- * command, because if we use them at all, it's for
- * authenticating to the proxy SSH server.
- */
- conf_set_str(cmd_conf, CONF_proxy_username, "");
- conf_set_str(cmd_conf, CONF_proxy_password, "");
- char *cmd = format_telnet_command(sp->addr, sp->port, cmd_conf, NULL);
- conf_free(cmd_conf);
- conf_set_str(sp->conf, CONF_remote_cmd, cmd);
- sfree(cmd);
- conf_set_bool(sp->conf, CONF_nopty, true);
- if (proxy_type == PROXY_SSH_SUBSYSTEM)
- conf_set_bool(sp->conf, CONF_ssh_subsys, true);
- break;
- }
- default:
- unreachable("bad SSH proxy type");
- }
- /*
- * Do the usual normalisation of things in the Conf like a "user@"
- * prefix on the hostname field.
- */
- prepare_session(sp->conf);
- sp->logctx = log_init(&sp->logpolicy, sp->conf);
- char *error, *realhost;
- error = backend_init(backvt, &sp->seat, &sp->backend, sp->logctx, sp->conf,
- conf_get_str(sp->conf, CONF_host),
- conf_get_int(sp->conf, CONF_port),
- &realhost, nodelay,
- conf_get_bool(sp->conf, CONF_tcp_keepalives));
- if (error) {
- sp->errmsg = dupprintf("unable to open SSH proxy connection: %s",
- error);
- return &sp->sock;
- }
- sfree(realhost);
- /*
- * If we've been given an Interactor by the caller, set ourselves
- * up to work with it.
- */
- if (clientitr) {
- sp->clientitr = clientitr;
- interactor_set_child(sp->clientitr, sp->backend->interactor);
- sp->clientlp = interactor_logpolicy(clientitr);
- /*
- * We can only borrow the client's Seat if our own backend
- * will tell us when to give it back. (SSH-based backends
- * _should_ do that, but we check the flag here anyway.)
- */
- if (backvt->flags & BACKEND_NOTIFIES_SESSION_START)
- sp->clientseat = interactor_borrow_seat(clientitr);
- }
- return &sp->sock;
- }
|