local.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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. .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. .description = local_proxy_opener_description,
  80. .logpolicy = local_proxy_opener_logpolicy,
  81. .get_seat = local_proxy_opener_get_seat,
  82. .set_seat = local_proxy_opener_set_seat,
  83. };
  84. static void local_proxy_opener_cleanup_interactor(LocalProxyOpener *lp)
  85. {
  86. if (lp->clientseat) {
  87. interactor_return_seat(lp->clientitr);
  88. lp->clientitr = NULL;
  89. lp->clientseat = NULL;
  90. }
  91. }
  92. static void local_proxy_opener_coroutine(void *vctx)
  93. {
  94. LocalProxyOpener *lp = (LocalProxyOpener *)vctx;
  95. crBegin(lp->crLine);
  96. /*
  97. * Make an initial attempt to figure out the command we want, and
  98. * see if it tried to include a username or password that we don't
  99. * have.
  100. */
  101. {
  102. unsigned flags;
  103. lp->formatted_cmd = format_telnet_command(
  104. lp->addr, lp->port, lp->conf, &flags);
  105. if (lp->clientseat && (flags & (TELNET_CMD_MISSING_USERNAME |
  106. TELNET_CMD_MISSING_PASSWORD))) {
  107. burnstr(lp->formatted_cmd);
  108. lp->formatted_cmd = NULL;
  109. /*
  110. * We're missing at least one of the two parts, and we
  111. * have an Interactor we can use to prompt for them, so
  112. * try it.
  113. */
  114. lp->prompts = new_prompts();
  115. lp->prompts->callback = local_proxy_opener_coroutine;
  116. lp->prompts->callback_ctx = lp;
  117. lp->prompts->to_server = true;
  118. lp->prompts->from_server = false;
  119. lp->prompts->name = dupstr("Local proxy authentication");
  120. if (flags & TELNET_CMD_MISSING_USERNAME) {
  121. lp->username_prompt_index = lp->prompts->n_prompts;
  122. add_prompt(lp->prompts, dupstr("Proxy username: "), true);
  123. } else {
  124. lp->username_prompt_index = -1;
  125. }
  126. if (flags & TELNET_CMD_MISSING_PASSWORD) {
  127. lp->password_prompt_index = lp->prompts->n_prompts;
  128. add_prompt(lp->prompts, dupstr("Proxy password: "), false);
  129. } else {
  130. lp->password_prompt_index = -1;
  131. }
  132. while (true) {
  133. SeatPromptResult spr = seat_get_userpass_input(
  134. interactor_announce(&lp->interactor), lp->prompts);
  135. if (spr.kind == SPRK_OK) {
  136. break;
  137. } else if (spr.kind == SPRK_USER_ABORT) {
  138. local_proxy_opener_cleanup_interactor(lp);
  139. plug_closing_user_abort(lp->plug);
  140. /* That will have freed us, so we must just return
  141. * without calling any crStop */
  142. return;
  143. } else if (spr.kind == SPRK_SW_ABORT) {
  144. local_proxy_opener_cleanup_interactor(lp);
  145. char *err = spr_get_error_message(spr);
  146. plug_closing_error(lp->plug, err);
  147. sfree(err);
  148. return; /* without crStop, as above */
  149. }
  150. crReturnV;
  151. }
  152. if (lp->username_prompt_index != -1) {
  153. conf_set_str(
  154. lp->conf, CONF_proxy_username,
  155. prompt_get_result_ref(
  156. lp->prompts->prompts[lp->username_prompt_index]));
  157. }
  158. if (lp->password_prompt_index != -1) {
  159. conf_set_str(
  160. lp->conf, CONF_proxy_password,
  161. prompt_get_result_ref(
  162. lp->prompts->prompts[lp->password_prompt_index]));
  163. }
  164. free_prompts(lp->prompts);
  165. lp->prompts = NULL;
  166. }
  167. /*
  168. * Now format the command a second time, with the results of
  169. * those prompts written into lp->conf.
  170. */
  171. lp->formatted_cmd = format_telnet_command(
  172. lp->addr, lp->port, lp->conf, NULL);
  173. }
  174. /*
  175. * Log the command, with some changes. Firstly, we regenerate it
  176. * with the password masked; secondly, we escape control
  177. * characters so that the log message is printable.
  178. */
  179. conf_set_str(lp->conf, CONF_proxy_password, "*password*");
  180. {
  181. char *censored_cmd = format_telnet_command(
  182. lp->addr, lp->port, lp->conf, NULL);
  183. strbuf *logmsg = strbuf_new();
  184. put_datapl(logmsg, PTRLEN_LITERAL("Starting local proxy command: "));
  185. put_c_string_literal(logmsg, ptrlen_from_asciz(censored_cmd));
  186. plug_log(lp->plug, lp->socket, PLUGLOG_PROXY_MSG, NULL, 0,
  187. logmsg->s, 0);
  188. strbuf_free(logmsg);
  189. sfree(censored_cmd);
  190. }
  191. /*
  192. * Now we're ready to actually do the platform-specific socket
  193. * setup.
  194. */
  195. char *cmd = lp->formatted_cmd;
  196. lp->formatted_cmd = NULL;
  197. local_proxy_opener_cleanup_interactor(lp);
  198. char *error_msg = platform_setup_local_proxy(lp->socket, cmd);
  199. burnstr(cmd);
  200. if (error_msg) {
  201. plug_closing_error(lp->plug, error_msg);
  202. sfree(error_msg);
  203. } else {
  204. /* If error_msg was NULL, there was no error in setup,
  205. * which means that platform_setup_local_proxy will have
  206. * called back to free us. So return without calling any
  207. * crStop. */
  208. return;
  209. }
  210. crFinishV;
  211. }
  212. DeferredSocketOpener *local_proxy_opener(
  213. SockAddr *addr, int port, Plug *plug, Conf *conf, Interactor *itr)
  214. {
  215. LocalProxyOpener *lp = snew(LocalProxyOpener);
  216. memset(lp, 0, sizeof(*lp));
  217. lp->plug = plug;
  218. lp->opener.vt = &LocalProxyOpener_openervt;
  219. lp->interactor.vt = &LocalProxyOpener_interactorvt;
  220. lp->addr = sk_addr_dup(addr);
  221. lp->port = port;
  222. lp->conf = conf_copy(conf);
  223. if (itr) {
  224. lp->clientitr = itr;
  225. interactor_set_child(lp->clientitr, &lp->interactor);
  226. lp->clientlp = interactor_logpolicy(lp->clientitr);
  227. lp->clientseat = interactor_borrow_seat(lp->clientitr);
  228. }
  229. return &lp->opener;
  230. }
  231. void local_proxy_opener_set_socket(DeferredSocketOpener *opener,
  232. Socket *socket)
  233. {
  234. assert(opener->vt == &LocalProxyOpener_openervt);
  235. LocalProxyOpener *lp = container_of(opener, LocalProxyOpener, opener);
  236. lp->socket = socket;
  237. queue_toplevel_callback(local_proxy_opener_coroutine, lp);
  238. }