local.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /*
  2. * Implement LocalProxyOpener, a centralised system for setting up the
  3. * command string to be run by platform-specific local-subprocess
  4. * proxy types.
  5. *
  6. * The platform-specific local proxy code is expected to use this
  7. * system by calling local_proxy_opener() from
  8. * platform_new_connection(); then using the resulting
  9. * DeferredSocketOpener to make a deferred version of whatever local
  10. * socket type is used for talking to subcommands (Unix FdSocket,
  11. * Windows HandleSocket); then passing the 'Socket *' back to us via
  12. * local_proxy_opener_set_socket().
  13. *
  14. * The LocalProxyOpener object implemented by this code will set
  15. * itself up as an Interactor if possible, so that it can prompt for
  16. * the proxy username and/or password if they're referred to in the
  17. * command string but not given in the config (exactly as the Telnet
  18. * proxy does). Once it knows the exact command it wants to run -
  19. * whether that was done immediately or after user interaction - it
  20. * calls back to platform_setup_local_proxy() with the full command,
  21. * which is expected to actually start the subprocess and fill in the
  22. * missing details in the deferred socket, freeing the
  23. * LocalProxyOpener as a side effect.
  24. */
  25. #include "tree234.h"
  26. #include "putty.h"
  27. #include "network.h"
  28. #include "sshcr.h"
  29. #include "proxy/proxy.h"
  30. typedef struct LocalProxyOpener {
  31. int crLine;
  32. Socket *socket;
  33. char *formatted_cmd;
  34. Plug *plug;
  35. SockAddr *addr;
  36. int port;
  37. Conf *conf;
  38. Interactor *clientitr;
  39. LogPolicy *clientlp;
  40. Seat *clientseat;
  41. prompts_t *prompts;
  42. int username_prompt_index, password_prompt_index;
  43. Interactor interactor;
  44. DeferredSocketOpener opener;
  45. } LocalProxyOpener;
  46. static void local_proxy_opener_free(DeferredSocketOpener *opener)
  47. {
  48. LocalProxyOpener *lp = container_of(opener, LocalProxyOpener, opener);
  49. burnstr(lp->formatted_cmd);
  50. if (lp->prompts)
  51. free_prompts(lp->prompts);
  52. sk_addr_free(lp->addr);
  53. conf_free(lp->conf);
  54. sfree(lp);
  55. }
  56. static const DeferredSocketOpenerVtable LocalProxyOpener_openervt = {
  57. /*WINSCP .free =*/ local_proxy_opener_free,
  58. };
  59. static char *local_proxy_opener_description(Interactor *itr)
  60. {
  61. return dupstr("connection via local command");
  62. }
  63. static LogPolicy *local_proxy_opener_logpolicy(Interactor *itr)
  64. {
  65. LocalProxyOpener *lp = container_of(itr, LocalProxyOpener, interactor);
  66. return lp->clientlp;
  67. }
  68. static Seat *local_proxy_opener_get_seat(Interactor *itr)
  69. {
  70. LocalProxyOpener *lp = container_of(itr, LocalProxyOpener, interactor);
  71. return lp->clientseat;
  72. }
  73. static void local_proxy_opener_set_seat(Interactor *itr, Seat *seat)
  74. {
  75. LocalProxyOpener *lp = container_of(itr, LocalProxyOpener, interactor);
  76. lp->clientseat = seat;
  77. }
  78. static const InteractorVtable LocalProxyOpener_interactorvt = {
  79. // WINSCP
  80. /*.description =*/ local_proxy_opener_description,
  81. /*.logpolicy =*/ local_proxy_opener_logpolicy,
  82. /*.get_seat =*/ local_proxy_opener_get_seat,
  83. /*.set_seat =*/ local_proxy_opener_set_seat,
  84. };
  85. static void local_proxy_opener_cleanup_interactor(LocalProxyOpener *lp)
  86. {
  87. if (lp->clientseat) {
  88. interactor_return_seat(lp->clientitr);
  89. lp->clientitr = NULL;
  90. lp->clientseat = NULL;
  91. }
  92. }
  93. static void local_proxy_opener_coroutine(void *vctx)
  94. {
  95. LocalProxyOpener *lp = (LocalProxyOpener *)vctx;
  96. crBegin(lp->crLine);
  97. /*
  98. * Make an initial attempt to figure out the command we want, and
  99. * see if it tried to include a username or password that we don't
  100. * have.
  101. */
  102. {
  103. unsigned flags;
  104. lp->formatted_cmd = format_telnet_command(
  105. lp->addr, lp->port, lp->conf, &flags);
  106. if (lp->clientseat && (flags & (TELNET_CMD_MISSING_USERNAME |
  107. TELNET_CMD_MISSING_PASSWORD))) {
  108. burnstr(lp->formatted_cmd);
  109. lp->formatted_cmd = NULL;
  110. /*
  111. * We're missing at least one of the two parts, and we
  112. * have an Interactor we can use to prompt for them, so
  113. * try it.
  114. */
  115. lp->prompts = new_prompts();
  116. lp->prompts->callback = local_proxy_opener_coroutine;
  117. lp->prompts->callback_ctx = lp;
  118. lp->prompts->to_server = true;
  119. lp->prompts->from_server = false;
  120. lp->prompts->name = dupstr("Local proxy authentication");
  121. if (flags & TELNET_CMD_MISSING_USERNAME) {
  122. lp->username_prompt_index = lp->prompts->n_prompts;
  123. add_prompt(lp->prompts, dupstr("Proxy username: "), true);
  124. } else {
  125. lp->username_prompt_index = -1;
  126. }
  127. if (flags & TELNET_CMD_MISSING_PASSWORD) {
  128. lp->password_prompt_index = lp->prompts->n_prompts;
  129. add_prompt(lp->prompts, dupstr("Proxy password: "), false);
  130. } else {
  131. lp->password_prompt_index = -1;
  132. }
  133. while (true) {
  134. SeatPromptResult spr = seat_get_userpass_input(
  135. interactor_announce(&lp->interactor), lp->prompts);
  136. if (spr.kind == SPRK_OK) {
  137. break;
  138. } else if (spr.kind == SPRK_USER_ABORT) {
  139. local_proxy_opener_cleanup_interactor(lp);
  140. plug_closing_user_abort(lp->plug);
  141. /* That will have freed us, so we must just return
  142. * without calling any crStop */
  143. return;
  144. } else if (spr.kind == SPRK_SW_ABORT) {
  145. local_proxy_opener_cleanup_interactor(lp);
  146. { // WINSCP
  147. char *err = spr_get_error_message(spr);
  148. plug_closing_error(lp->plug, err);
  149. sfree(err);
  150. return; /* without crStop, as above */
  151. } // WINSCP
  152. }
  153. crReturnV;
  154. }
  155. if (lp->username_prompt_index != -1) {
  156. conf_set_str(
  157. lp->conf, CONF_proxy_username,
  158. prompt_get_result_ref(
  159. lp->prompts->prompts[lp->username_prompt_index]));
  160. }
  161. if (lp->password_prompt_index != -1) {
  162. conf_set_str(
  163. lp->conf, CONF_proxy_password,
  164. prompt_get_result_ref(
  165. lp->prompts->prompts[lp->password_prompt_index]));
  166. }
  167. free_prompts(lp->prompts);
  168. lp->prompts = NULL;
  169. }
  170. /*
  171. * Now format the command a second time, with the results of
  172. * those prompts written into lp->conf.
  173. */
  174. lp->formatted_cmd = format_telnet_command(
  175. lp->addr, lp->port, lp->conf, NULL);
  176. }
  177. /*
  178. * Log the command, with some changes. Firstly, we regenerate it
  179. * with the password masked; secondly, we escape control
  180. * characters so that the log message is printable.
  181. */
  182. conf_set_str(lp->conf, CONF_proxy_password, "*password*");
  183. {
  184. char *censored_cmd = format_telnet_command(
  185. lp->addr, lp->port, lp->conf, NULL);
  186. strbuf *logmsg = strbuf_new();
  187. put_datapl(logmsg, PTRLEN_LITERAL("Starting local proxy command: "));
  188. put_c_string_literal(logmsg, ptrlen_from_asciz(censored_cmd));
  189. plug_log(lp->plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg->s, 0);
  190. strbuf_free(logmsg);
  191. sfree(censored_cmd);
  192. }
  193. /*
  194. * Now we're ready to actually do the platform-specific socket
  195. * setup.
  196. */
  197. { // WINSCP
  198. char *cmd = lp->formatted_cmd;
  199. lp->formatted_cmd = NULL;
  200. local_proxy_opener_cleanup_interactor(lp);
  201. { // WINSCP
  202. char *error_msg = platform_setup_local_proxy(lp->socket, cmd);
  203. burnstr(cmd);
  204. if (error_msg) {
  205. plug_closing_error(lp->plug, error_msg);
  206. sfree(error_msg);
  207. } else {
  208. /* If error_msg was NULL, there was no error in setup,
  209. * which means that platform_setup_local_proxy will have
  210. * called back to free us. So return without calling any
  211. * crStop. */
  212. return;
  213. }
  214. } // WINSCP
  215. } // WINSCP
  216. crFinishV;
  217. }
  218. DeferredSocketOpener *local_proxy_opener(
  219. SockAddr *addr, int port, Plug *plug, Conf *conf, Interactor *itr)
  220. {
  221. LocalProxyOpener *lp = snew(LocalProxyOpener);
  222. memset(lp, 0, sizeof(*lp));
  223. lp->plug = plug;
  224. lp->opener.vt = &LocalProxyOpener_openervt;
  225. lp->interactor.vt = &LocalProxyOpener_interactorvt;
  226. lp->addr = sk_addr_dup(addr);
  227. lp->port = port;
  228. lp->conf = conf_copy(conf);
  229. if (itr) {
  230. lp->clientitr = itr;
  231. interactor_set_child(lp->clientitr, &lp->interactor);
  232. lp->clientlp = interactor_logpolicy(lp->clientitr);
  233. lp->clientseat = interactor_borrow_seat(lp->clientitr);
  234. }
  235. return &lp->opener;
  236. }
  237. void local_proxy_opener_set_socket(DeferredSocketOpener *opener,
  238. Socket *socket)
  239. {
  240. assert(opener->vt == &LocalProxyOpener_openervt);
  241. { // WINSCP
  242. LocalProxyOpener *lp = container_of(opener, LocalProxyOpener, opener);
  243. lp->socket = socket;
  244. queue_toplevel_callback(get_callback_set(lp->plug), local_proxy_opener_coroutine, lp);
  245. } // WINSCP
  246. }