sshproxy.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  1. /*
  2. * sshproxy.c: implement a Socket type that talks to an entire
  3. * subsidiary SSH connection (sometimes called a 'jump host').
  4. */
  5. #include <stdio.h>
  6. #include <assert.h>
  7. #include "putty.h"
  8. #include "ssh.h"
  9. #include "network.h"
  10. #include "storage.h"
  11. #include "proxy.h"
  12. const bool ssh_proxy_supported = true;
  13. typedef struct SshProxy {
  14. char *errmsg;
  15. Conf *conf;
  16. LogContext *logctx;
  17. Backend *backend;
  18. LogPolicy *clientlp;
  19. Seat *clientseat;
  20. Interactor *clientitr;
  21. bool got_proxy_password, tried_proxy_password;
  22. char *proxy_password;
  23. ProxyStderrBuf psb;
  24. Plug *plug;
  25. bool frozen;
  26. bufchain ssh_to_socket;
  27. bool rcvd_eof_ssh_to_socket, sent_eof_ssh_to_socket;
  28. bool conn_established;
  29. SockAddr *addr;
  30. int port;
  31. /* Traits implemented: we're a Socket from the point of view of
  32. * the client connection, and a Seat from the POV of the SSH
  33. * backend we instantiate. */
  34. Socket sock;
  35. LogPolicy logpolicy;
  36. Seat seat;
  37. } SshProxy;
  38. static Plug *sshproxy_plug(Socket *s, Plug *p)
  39. {
  40. SshProxy *sp = container_of(s, SshProxy, sock);
  41. Plug *oldplug = sp->plug;
  42. if (p)
  43. sp->plug = p;
  44. return oldplug;
  45. }
  46. static void sshproxy_close(Socket *s)
  47. {
  48. SshProxy *sp = container_of(s, SshProxy, sock);
  49. sk_addr_free(sp->addr);
  50. sfree(sp->errmsg);
  51. conf_free(sp->conf);
  52. if (sp->backend)
  53. backend_free(sp->backend);
  54. if (sp->logctx)
  55. log_free(sp->logctx);
  56. if (sp->proxy_password)
  57. burnstr(sp->proxy_password);
  58. bufchain_clear(&sp->ssh_to_socket);
  59. delete_callbacks_for_context(sp);
  60. sfree(sp);
  61. }
  62. static size_t sshproxy_write(Socket *s, const void *data, size_t len)
  63. {
  64. SshProxy *sp = container_of(s, SshProxy, sock);
  65. if (!sp->backend)
  66. return 0;
  67. backend_send(sp->backend, data, len);
  68. return backend_sendbuffer(sp->backend);
  69. }
  70. static size_t sshproxy_write_oob(Socket *s, const void *data, size_t len)
  71. {
  72. /*
  73. * oob data is treated as inband; nasty, but nothing really
  74. * better we can do
  75. */
  76. return sshproxy_write(s, data, len);
  77. }
  78. static void sshproxy_write_eof(Socket *s)
  79. {
  80. SshProxy *sp = container_of(s, SshProxy, sock);
  81. if (!sp->backend)
  82. return;
  83. backend_special(sp->backend, SS_EOF, 0);
  84. }
  85. static void try_send_ssh_to_socket(void *ctx);
  86. static void try_send_ssh_to_socket_cb(void *ctx)
  87. {
  88. SshProxy *sp = (SshProxy *)ctx;
  89. try_send_ssh_to_socket(sp);
  90. if (sp->backend)
  91. backend_unthrottle(sp->backend, bufchain_size(&sp->ssh_to_socket));
  92. }
  93. static void sshproxy_set_frozen(Socket *s, bool is_frozen)
  94. {
  95. SshProxy *sp = container_of(s, SshProxy, sock);
  96. sp->frozen = is_frozen;
  97. if (!sp->frozen)
  98. queue_toplevel_callback(try_send_ssh_to_socket_cb, sp);
  99. }
  100. static const char *sshproxy_socket_error(Socket *s)
  101. {
  102. SshProxy *sp = container_of(s, SshProxy, sock);
  103. return sp->errmsg;
  104. }
  105. static const SocketVtable SshProxy_sock_vt = {
  106. .plug = sshproxy_plug,
  107. .close = sshproxy_close,
  108. .write = sshproxy_write,
  109. .write_oob = sshproxy_write_oob,
  110. .write_eof = sshproxy_write_eof,
  111. .set_frozen = sshproxy_set_frozen,
  112. .socket_error = sshproxy_socket_error,
  113. .endpoint_info = nullsock_endpoint_info,
  114. };
  115. static void sshproxy_eventlog(LogPolicy *lp, const char *event)
  116. {
  117. SshProxy *sp = container_of(lp, SshProxy, logpolicy);
  118. log_proxy_stderr(sp->plug, &sp->sock, &sp->psb, event, strlen(event));
  119. log_proxy_stderr(sp->plug, &sp->sock, &sp->psb, "\n", 1);
  120. }
  121. static int sshproxy_askappend(LogPolicy *lp, Filename *filename,
  122. void (*callback)(void *ctx, int result),
  123. void *ctx)
  124. {
  125. SshProxy *sp = container_of(lp, SshProxy, logpolicy);
  126. /*
  127. * If we have access to the outer LogPolicy, pass on this request
  128. * to the end user.
  129. */
  130. if (sp->clientlp)
  131. return lp_askappend(sp->clientlp, filename, callback, ctx);
  132. /*
  133. * Otherwise, fall back to the safe noninteractive assumption.
  134. */
  135. char *msg = dupprintf("Log file \"%s\" already exists; logging cancelled",
  136. filename_to_str(filename));
  137. sshproxy_eventlog(lp, msg);
  138. sfree(msg);
  139. return 0;
  140. }
  141. static void sshproxy_logging_error(LogPolicy *lp, const char *event)
  142. {
  143. SshProxy *sp = container_of(lp, SshProxy, logpolicy);
  144. /*
  145. * If we have access to the outer LogPolicy, pass on this request
  146. * to it.
  147. */
  148. if (sp->clientlp) {
  149. lp_logging_error(sp->clientlp, event);
  150. return;
  151. }
  152. /*
  153. * Otherwise, the best we can do is to put it in the outer SSH
  154. * connection's Event Log.
  155. */
  156. char *msg = dupprintf("Logging error: %s", event);
  157. sshproxy_eventlog(lp, msg);
  158. sfree(msg);
  159. }
  160. static const LogPolicyVtable SshProxy_logpolicy_vt = {
  161. .eventlog = sshproxy_eventlog,
  162. .askappend = sshproxy_askappend,
  163. .logging_error = sshproxy_logging_error,
  164. .verbose = null_lp_verbose_no,
  165. };
  166. /*
  167. * Function called when we encounter an error during connection setup that's
  168. * likely to be the cause of terminating the proxy SSH connection. Putting it
  169. * in the Event Log is useful on general principles; also putting it in
  170. * sp->errmsg meaks that it will be passed back through plug_closing when the
  171. * proxy SSH connection actually terminates, so that the end user will see
  172. * what went wrong in the proxy connection.
  173. */
  174. static void sshproxy_error(SshProxy *sp, const char *fmt, ...)
  175. {
  176. va_list ap;
  177. va_start(ap, fmt);
  178. char *msg = dupvprintf(fmt, ap);
  179. va_end(ap);
  180. if (!sp->errmsg)
  181. sp->errmsg = dupstr(msg);
  182. sshproxy_eventlog(&sp->logpolicy, msg);
  183. sfree(msg);
  184. }
  185. static void try_send_ssh_to_socket(void *ctx)
  186. {
  187. SshProxy *sp = (SshProxy *)ctx;
  188. if (sp->frozen)
  189. return;
  190. while (bufchain_size(&sp->ssh_to_socket)) {
  191. ptrlen pl = bufchain_prefix(&sp->ssh_to_socket);
  192. plug_receive(sp->plug, 0, pl.ptr, pl.len);
  193. bufchain_consume(&sp->ssh_to_socket, pl.len);
  194. }
  195. if (sp->rcvd_eof_ssh_to_socket &&
  196. !sp->sent_eof_ssh_to_socket) {
  197. sp->sent_eof_ssh_to_socket = true;
  198. plug_closing_normal(sp->plug);
  199. }
  200. }
  201. static void sshproxy_notify_session_started(Seat *seat)
  202. {
  203. SshProxy *sp = container_of(seat, SshProxy, seat);
  204. if (sp->clientseat)
  205. interactor_return_seat(sp->clientitr);
  206. sp->conn_established = true;
  207. plug_log(sp->plug, &sp->sock, PLUGLOG_CONNECT_SUCCESS, sp->addr, sp->port,
  208. NULL, 0);
  209. }
  210. static size_t sshproxy_output(Seat *seat, SeatOutputType type,
  211. const void *data, size_t len)
  212. {
  213. SshProxy *sp = container_of(seat, SshProxy, seat);
  214. switch (type) {
  215. case SEAT_OUTPUT_STDOUT:
  216. bufchain_add(&sp->ssh_to_socket, data, len);
  217. try_send_ssh_to_socket(sp);
  218. break;
  219. case SEAT_OUTPUT_STDERR:
  220. log_proxy_stderr(sp->plug, &sp->sock, &sp->psb, data, len);
  221. break;
  222. }
  223. return bufchain_size(&sp->ssh_to_socket);
  224. }
  225. static inline InteractionReadySeat wrap(Seat *seat)
  226. {
  227. /*
  228. * When we receive interaction requests from the proxy and want to
  229. * pass them on to our client Seat, we have to present them to the
  230. * latter in the form of an InteractionReadySeat. This forwarding
  231. * scenario is the one case where we _mustn't_ get an
  232. * InteractionReadySeat by calling interactor_announce(), because
  233. * the point is that we're _not_ the originating Interactor, we're
  234. * just forwarding the request from the real one, which has
  235. * already announced itself.
  236. *
  237. * So, just here in the code, it really is the right thing to make
  238. * an InteractionReadySeat out of a plain Seat * without an
  239. * announcement.
  240. */
  241. InteractionReadySeat iseat;
  242. iseat.seat = seat;
  243. return iseat;
  244. }
  245. static size_t sshproxy_banner(Seat *seat, const void *data, size_t len)
  246. {
  247. SshProxy *sp = container_of(seat, SshProxy, seat);
  248. if (sp->clientseat) {
  249. /*
  250. * If we have access to the outer Seat, pass the SSH login
  251. * banner on to it.
  252. */
  253. return seat_banner(wrap(sp->clientseat), data, len);
  254. } else {
  255. return 0;
  256. }
  257. }
  258. static bool sshproxy_eof(Seat *seat)
  259. {
  260. SshProxy *sp = container_of(seat, SshProxy, seat);
  261. sp->rcvd_eof_ssh_to_socket = true;
  262. try_send_ssh_to_socket(sp);
  263. return false;
  264. }
  265. static void sshproxy_sent(Seat *seat, size_t new_bufsize)
  266. {
  267. SshProxy *sp = container_of(seat, SshProxy, seat);
  268. plug_sent(sp->plug, new_bufsize);
  269. }
  270. static void sshproxy_send_close(SshProxy *sp)
  271. {
  272. if (sp->clientseat)
  273. interactor_return_seat(sp->clientitr);
  274. if (!sp->conn_established)
  275. plug_log(sp->plug, &sp->sock, PLUGLOG_CONNECT_FAILED, sp->addr,
  276. sp->port, sp->errmsg, 0);
  277. if (sp->errmsg)
  278. plug_closing_error(sp->plug, sp->errmsg);
  279. else if (!sp->conn_established && backend_exitcode(sp->backend) == 0)
  280. plug_closing_user_abort(sp->plug);
  281. else
  282. plug_closing_normal(sp->plug);
  283. }
  284. static void sshproxy_notify_remote_disconnect_callback(void *vctx)
  285. {
  286. SshProxy *sp = (SshProxy *)vctx;
  287. /* notify_remote_disconnect can be called redundantly, so first
  288. * check if the backend really has become disconnected */
  289. if (backend_connected(sp->backend))
  290. return;
  291. sshproxy_send_close(sp);
  292. }
  293. static void sshproxy_notify_remote_disconnect(Seat *seat)
  294. {
  295. SshProxy *sp = container_of(seat, SshProxy, seat);
  296. queue_toplevel_callback(sshproxy_notify_remote_disconnect_callback, sp);
  297. }
  298. static SeatPromptResult sshproxy_get_userpass_input(Seat *seat, prompts_t *p)
  299. {
  300. SshProxy *sp = container_of(seat, SshProxy, seat);
  301. /*
  302. * If we have a stored proxy_password, use that, via logic similar
  303. * to cmdline_get_passwd_input: we only try it if we're given a
  304. * prompts_t containing exactly one prompt, and that prompt is set
  305. * to non-echoing.
  306. */
  307. if (sp->got_proxy_password && !sp->tried_proxy_password &&
  308. p->n_prompts == 1 && !p->prompts[0]->echo) {
  309. prompt_set_result(p->prompts[0], sp->proxy_password);
  310. burnstr(sp->proxy_password);
  311. sp->proxy_password = NULL;
  312. sp->tried_proxy_password = true;
  313. return SPR_OK;
  314. }
  315. if (sp->clientseat) {
  316. /*
  317. * If we have access to the outer Seat, pass this prompt
  318. * request on to it.
  319. */
  320. return seat_get_userpass_input(wrap(sp->clientseat), p);
  321. }
  322. /*
  323. * Otherwise, behave as if noninteractive (like plink -batch):
  324. * reject all attempts to present a prompt to the user, and log in
  325. * the Event Log to say why not.
  326. */
  327. sshproxy_error(sp, "Unable to provide interactive authentication "
  328. "requested by proxy SSH connection");
  329. return SPR_SW_ABORT("Noninteractive SSH proxy cannot perform "
  330. "interactive authentication");
  331. }
  332. static void sshproxy_connection_fatal_callback(void *vctx)
  333. {
  334. SshProxy *sp = (SshProxy *)vctx;
  335. sshproxy_send_close(sp);
  336. }
  337. static void sshproxy_connection_fatal(Seat *seat, const char *message)
  338. {
  339. SshProxy *sp = container_of(seat, SshProxy, seat);
  340. if (!sp->errmsg) {
  341. sp->errmsg = dupprintf(
  342. "fatal error in proxy SSH connection: %s", message);
  343. queue_toplevel_callback(sshproxy_connection_fatal_callback, sp);
  344. }
  345. }
  346. static void sshproxy_nonfatal(Seat *seat, const char *message)
  347. {
  348. SshProxy *sp = container_of(seat, SshProxy, seat);
  349. if (sp->clientseat)
  350. seat_nonfatal(sp->clientseat, "error in proxy SSH connection: %s",
  351. message);
  352. }
  353. static SeatPromptResult sshproxy_confirm_ssh_host_key(
  354. Seat *seat, const char *host, int port, const char *keytype,
  355. char *keystr, SeatDialogText *text, HelpCtx helpctx,
  356. void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
  357. {
  358. SshProxy *sp = container_of(seat, SshProxy, seat);
  359. if (sp->clientseat) {
  360. /*
  361. * If we have access to the outer Seat, pass this prompt
  362. * request on to it.
  363. */
  364. return seat_confirm_ssh_host_key(
  365. wrap(sp->clientseat), host, port, keytype, keystr, text,
  366. helpctx, callback, ctx);
  367. }
  368. /*
  369. * Otherwise, behave as if we're in batch mode, i.e. take the safe
  370. * option in the absence of interactive confirmation, i.e. abort
  371. * the connection.
  372. */
  373. return SPR_SW_ABORT("Noninteractive SSH proxy cannot confirm host key");
  374. }
  375. static void sshproxy_format_seatdialogtext(strbuf *sb, SeatDialogText *text)
  376. {
  377. for (SeatDialogTextItem *item = text->items,
  378. *end = item+text->nitems; item < end; item++) {
  379. switch (item->type) {
  380. case SDT_SCARY_HEADING:
  381. case SDT_PARA:
  382. case SDT_DISPLAY:
  383. put_stringz(sb, item->text);
  384. put_byte(sb, '\n');
  385. break;
  386. case SDT_BATCH_ABORT:
  387. put_stringz(sb, item->text);
  388. put_byte(sb, '\n');
  389. goto endloop;
  390. default:
  391. break;
  392. }
  393. }
  394. endloop:
  395. while (strbuf_chomp(sb, '\n'));
  396. }
  397. static SeatPromptResult sshproxy_confirm_weak_crypto_primitive(
  398. Seat *seat, SeatDialogText *text,
  399. void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
  400. {
  401. SshProxy *sp = container_of(seat, SshProxy, seat);
  402. if (sp->clientseat) {
  403. /*
  404. * If we have access to the outer Seat, pass this prompt
  405. * request on to it.
  406. */
  407. return seat_confirm_weak_crypto_primitive(
  408. wrap(sp->clientseat), text, callback, ctx);
  409. }
  410. /*
  411. * Otherwise, behave as if we're in batch mode: take the safest
  412. * option.
  413. */
  414. strbuf *sb = strbuf_new();
  415. sshproxy_format_seatdialogtext(sb, text);
  416. sshproxy_error(sp, sb->s);
  417. strbuf_free(sb);
  418. return SPR_SW_ABORT("Noninteractive SSH proxy cannot confirm "
  419. "weak crypto primitive");
  420. }
  421. static SeatPromptResult sshproxy_confirm_weak_cached_hostkey(
  422. Seat *seat, SeatDialogText *text,
  423. void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
  424. {
  425. SshProxy *sp = container_of(seat, SshProxy, seat);
  426. if (sp->clientseat) {
  427. /*
  428. * If we have access to the outer Seat, pass this prompt
  429. * request on to it.
  430. */
  431. return seat_confirm_weak_cached_hostkey(
  432. wrap(sp->clientseat), text, callback, ctx);
  433. }
  434. /*
  435. * Otherwise, behave as if we're in batch mode: take the safest
  436. * option.
  437. */
  438. strbuf *sb = strbuf_new();
  439. sshproxy_format_seatdialogtext(sb, text);
  440. sshproxy_error(sp, sb->s);
  441. strbuf_free(sb);
  442. return SPR_SW_ABORT("Noninteractive SSH proxy cannot confirm "
  443. "weak cached host key");
  444. }
  445. static const SeatDialogPromptDescriptions *sshproxy_prompt_descriptions(
  446. Seat *seat)
  447. {
  448. SshProxy *sp = container_of(seat, SshProxy, seat);
  449. /* If we have a client seat, return their prompt descriptions, so
  450. * that prompts passed on to them will make sense. */
  451. if (sp->clientseat)
  452. return seat_prompt_descriptions(sp->clientseat);
  453. /* Otherwise, it doesn't matter what we return, so do the easiest thing. */
  454. return nullseat_prompt_descriptions(NULL);
  455. }
  456. static StripCtrlChars *sshproxy_stripctrl_new(
  457. Seat *seat, BinarySink *bs_out, SeatInteractionContext sic)
  458. {
  459. SshProxy *sp = container_of(seat, SshProxy, seat);
  460. if (sp->clientseat)
  461. return seat_stripctrl_new(sp->clientseat, bs_out, sic);
  462. else
  463. return NULL;
  464. }
  465. static void sshproxy_set_trust_status(Seat *seat, bool trusted)
  466. {
  467. SshProxy *sp = container_of(seat, SshProxy, seat);
  468. if (sp->clientseat)
  469. seat_set_trust_status(sp->clientseat, trusted);
  470. }
  471. static bool sshproxy_can_set_trust_status(Seat *seat)
  472. {
  473. SshProxy *sp = container_of(seat, SshProxy, seat);
  474. return sp->clientseat && seat_can_set_trust_status(sp->clientseat);
  475. }
  476. static bool sshproxy_verbose(Seat *seat)
  477. {
  478. SshProxy *sp = container_of(seat, SshProxy, seat);
  479. return sp->clientseat && seat_verbose(sp->clientseat);
  480. }
  481. static bool sshproxy_interactive(Seat *seat)
  482. {
  483. SshProxy *sp = container_of(seat, SshProxy, seat);
  484. return sp->clientseat && seat_interactive(sp->clientseat);
  485. }
  486. static const SeatVtable SshProxy_seat_vt = {
  487. .output = sshproxy_output,
  488. .eof = sshproxy_eof,
  489. .sent = sshproxy_sent,
  490. .banner = sshproxy_banner,
  491. .get_userpass_input = sshproxy_get_userpass_input,
  492. .notify_session_started = sshproxy_notify_session_started,
  493. .notify_remote_exit = nullseat_notify_remote_exit,
  494. .notify_remote_disconnect = sshproxy_notify_remote_disconnect,
  495. .connection_fatal = sshproxy_connection_fatal,
  496. .nonfatal = sshproxy_nonfatal,
  497. .update_specials_menu = nullseat_update_specials_menu,
  498. .get_ttymode = nullseat_get_ttymode,
  499. .set_busy_status = nullseat_set_busy_status,
  500. .confirm_ssh_host_key = sshproxy_confirm_ssh_host_key,
  501. .confirm_weak_crypto_primitive = sshproxy_confirm_weak_crypto_primitive,
  502. .confirm_weak_cached_hostkey = sshproxy_confirm_weak_cached_hostkey,
  503. .prompt_descriptions = sshproxy_prompt_descriptions,
  504. .is_utf8 = nullseat_is_never_utf8,
  505. .echoedit_update = nullseat_echoedit_update,
  506. .get_x_display = nullseat_get_x_display,
  507. .get_windowid = nullseat_get_windowid,
  508. .get_window_pixel_size = nullseat_get_window_pixel_size,
  509. .stripctrl_new = sshproxy_stripctrl_new,
  510. .set_trust_status = sshproxy_set_trust_status,
  511. .can_set_trust_status = sshproxy_can_set_trust_status,
  512. .has_mixed_input_stream = nullseat_has_mixed_input_stream_no,
  513. .verbose = sshproxy_verbose,
  514. .interactive = sshproxy_interactive,
  515. .get_cursor_position = nullseat_get_cursor_position,
  516. };
  517. Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
  518. int port, bool privport,
  519. bool oobinline, bool nodelay, bool keepalive,
  520. Plug *plug, Conf *clientconf,
  521. Interactor *clientitr)
  522. {
  523. SshProxy *sp = snew(SshProxy);
  524. memset(sp, 0, sizeof(*sp));
  525. sp->sock.vt = &SshProxy_sock_vt;
  526. sp->logpolicy.vt = &SshProxy_logpolicy_vt;
  527. sp->seat.vt = &SshProxy_seat_vt;
  528. sp->plug = plug;
  529. psb_init(&sp->psb);
  530. bufchain_init(&sp->ssh_to_socket);
  531. sp->addr = addr;
  532. sp->port = port;
  533. sp->conf = conf_new();
  534. /* Try to treat proxy_hostname as the title of a saved session. If
  535. * that fails, set up a default Conf of our own treating it as a
  536. * hostname. */
  537. const char *proxy_hostname = conf_get_str(clientconf, CONF_proxy_host);
  538. if (do_defaults(proxy_hostname, sp->conf)) {
  539. if (!conf_launchable(sp->conf)) {
  540. sp->errmsg = dupprintf("saved session '%s' is not launchable",
  541. proxy_hostname);
  542. return &sp->sock;
  543. }
  544. } else {
  545. do_defaults(NULL, sp->conf);
  546. /* In hostname mode, we default to PROT_SSH. This is more useful than
  547. * the obvious approach of defaulting to the protocol defined in
  548. * Default Settings, because only SSH (ok, and bare ssh-connection)
  549. * can be used for this kind of proxy. */
  550. conf_set_int(sp->conf, CONF_protocol, PROT_SSH);
  551. conf_set_str(sp->conf, CONF_host, proxy_hostname);
  552. conf_set_int(sp->conf, CONF_port,
  553. conf_get_int(clientconf, CONF_proxy_port));
  554. }
  555. const char *proxy_username = conf_get_str(clientconf, CONF_proxy_username);
  556. if (*proxy_username)
  557. conf_set_str(sp->conf, CONF_username, proxy_username);
  558. const char *proxy_password = conf_get_str(clientconf, CONF_proxy_password);
  559. if (*proxy_password) {
  560. sp->proxy_password = dupstr(proxy_password);
  561. sp->got_proxy_password = true;
  562. }
  563. const struct BackendVtable *backvt = backend_vt_from_proto(
  564. conf_get_int(sp->conf, CONF_protocol));
  565. /*
  566. * We don't actually need an _SSH_ session specifically: it's also
  567. * OK to use PROT_SSHCONN, because really, the criterion is
  568. * whether setting CONF_ssh_nc_host will do anything useful. So
  569. * our check is for whether the backend sets the flag promising
  570. * that it does.
  571. */
  572. if (!backvt || !(backvt->flags & BACKEND_SUPPORTS_NC_HOST)) {
  573. sp->errmsg = dupprintf("saved session '%s' is not an SSH session",
  574. proxy_hostname);
  575. return &sp->sock;
  576. }
  577. /*
  578. * We also expect that the backend will announce a willingness to
  579. * notify us that the session has started. Any backend providing
  580. * NC_HOST should also provide this.
  581. */
  582. assert(backvt->flags & BACKEND_NOTIFIES_SESSION_START &&
  583. "Backend provides NC_HOST without SESSION_START!");
  584. /*
  585. * Turn off SSH features we definitely don't want. It would be
  586. * awkward and counterintuitive to have the proxy SSH connection
  587. * become a connection-sharing upstream (but it's fine to have it
  588. * be a downstream, if that's configured). And we don't want to
  589. * open X forwardings, agent forwardings or (other) port
  590. * forwardings as a side effect of this one operation.
  591. */
  592. conf_set_bool(sp->conf, CONF_ssh_connection_sharing_upstream, false);
  593. conf_set_bool(sp->conf, CONF_x11_forward, false);
  594. conf_set_bool(sp->conf, CONF_agentfwd, false);
  595. for (const char *subkey;
  596. (subkey = conf_get_str_nthstrkey(sp->conf, CONF_portfwd, 0)) != NULL;)
  597. conf_del_str_str(sp->conf, CONF_portfwd, subkey);
  598. /*
  599. * We'll only be running one channel through this connection
  600. * (since we've just turned off all the other things we might have
  601. * done with it), so we can configure it as simple.
  602. */
  603. conf_set_bool(sp->conf, CONF_ssh_simple, true);
  604. int proxy_type = conf_get_int(clientconf, CONF_proxy_type);
  605. switch (proxy_type) {
  606. case PROXY_SSH_TCPIP:
  607. /*
  608. * Configure the main channel of this SSH session to be a
  609. * direct-tcpip connection to the destination host/port.
  610. */
  611. conf_set_str(sp->conf, CONF_ssh_nc_host, hostname);
  612. conf_set_int(sp->conf, CONF_ssh_nc_port, port);
  613. break;
  614. case PROXY_SSH_SUBSYSTEM:
  615. case PROXY_SSH_EXEC: {
  616. Conf *cmd_conf = conf_copy(clientconf);
  617. /*
  618. * Unlike the Telnet and Local proxy types, we don't use the
  619. * proxy username and password fields in the formatted
  620. * command, because if we use them at all, it's for
  621. * authenticating to the proxy SSH server.
  622. */
  623. conf_set_str(cmd_conf, CONF_proxy_username, "");
  624. conf_set_str(cmd_conf, CONF_proxy_password, "");
  625. char *cmd = format_telnet_command(sp->addr, sp->port, cmd_conf, NULL);
  626. conf_free(cmd_conf);
  627. conf_set_str(sp->conf, CONF_remote_cmd, cmd);
  628. sfree(cmd);
  629. conf_set_bool(sp->conf, CONF_nopty, true);
  630. if (proxy_type == PROXY_SSH_SUBSYSTEM)
  631. conf_set_bool(sp->conf, CONF_ssh_subsys, true);
  632. break;
  633. }
  634. default:
  635. unreachable("bad SSH proxy type");
  636. }
  637. /*
  638. * Do the usual normalisation of things in the Conf like a "user@"
  639. * prefix on the hostname field.
  640. */
  641. prepare_session(sp->conf);
  642. sp->logctx = log_init(&sp->logpolicy, sp->conf);
  643. char *error, *realhost;
  644. error = backend_init(backvt, &sp->seat, &sp->backend, sp->logctx, sp->conf,
  645. conf_get_str(sp->conf, CONF_host),
  646. conf_get_int(sp->conf, CONF_port),
  647. &realhost, nodelay,
  648. conf_get_bool(sp->conf, CONF_tcp_keepalives));
  649. if (error) {
  650. sp->errmsg = dupprintf("unable to open SSH proxy connection: %s",
  651. error);
  652. return &sp->sock;
  653. }
  654. sfree(realhost);
  655. /*
  656. * If we've been given an Interactor by the caller, set ourselves
  657. * up to work with it.
  658. */
  659. if (clientitr) {
  660. sp->clientitr = clientitr;
  661. interactor_set_child(sp->clientitr, sp->backend->interactor);
  662. sp->clientlp = interactor_logpolicy(clientitr);
  663. /*
  664. * We can only borrow the client's Seat if our own backend
  665. * will tell us when to give it back. (SSH-based backends
  666. * _should_ do that, but we check the flag here anyway.)
  667. */
  668. if (backvt->flags & BACKEND_NOTIFIES_SESSION_START)
  669. sp->clientseat = interactor_borrow_seat(clientitr);
  670. }
  671. return &sp->sock;
  672. }