123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- /*
- * Implement LocalProxyOpener, a centralised system for setting up the
- * command string to be run by platform-specific local-subprocess
- * proxy types.
- *
- * The platform-specific local proxy code is expected to use this
- * system by calling local_proxy_opener() from
- * platform_new_connection(); then using the resulting
- * DeferredSocketOpener to make a deferred version of whatever local
- * socket type is used for talking to subcommands (Unix FdSocket,
- * Windows HandleSocket); then passing the 'Socket *' back to us via
- * local_proxy_opener_set_socket().
- *
- * The LocalProxyOpener object implemented by this code will set
- * itself up as an Interactor if possible, so that it can prompt for
- * the proxy username and/or password if they're referred to in the
- * command string but not given in the config (exactly as the Telnet
- * proxy does). Once it knows the exact command it wants to run -
- * whether that was done immediately or after user interaction - it
- * calls back to platform_setup_local_proxy() with the full command,
- * which is expected to actually start the subprocess and fill in the
- * missing details in the deferred socket, freeing the
- * LocalProxyOpener as a side effect.
- */
- #include "tree234.h"
- #include "putty.h"
- #include "network.h"
- #include "sshcr.h"
- #include "proxy/proxy.h"
- typedef struct LocalProxyOpener {
- int crLine;
- Socket *socket;
- char *formatted_cmd;
- Plug *plug;
- SockAddr *addr;
- int port;
- Conf *conf;
- Interactor *clientitr;
- LogPolicy *clientlp;
- Seat *clientseat;
- prompts_t *prompts;
- int username_prompt_index, password_prompt_index;
- Interactor interactor;
- DeferredSocketOpener opener;
- } LocalProxyOpener;
- static void local_proxy_opener_free(DeferredSocketOpener *opener)
- {
- LocalProxyOpener *lp = container_of(opener, LocalProxyOpener, opener);
- burnstr(lp->formatted_cmd);
- if (lp->prompts)
- free_prompts(lp->prompts);
- sk_addr_free(lp->addr);
- conf_free(lp->conf);
- sfree(lp);
- }
- static const DeferredSocketOpenerVtable LocalProxyOpener_openervt = {
- .free = local_proxy_opener_free,
- };
- static char *local_proxy_opener_description(Interactor *itr)
- {
- return dupstr("connection via local command");
- }
- static LogPolicy *local_proxy_opener_logpolicy(Interactor *itr)
- {
- LocalProxyOpener *lp = container_of(itr, LocalProxyOpener, interactor);
- return lp->clientlp;
- }
- static Seat *local_proxy_opener_get_seat(Interactor *itr)
- {
- LocalProxyOpener *lp = container_of(itr, LocalProxyOpener, interactor);
- return lp->clientseat;
- }
- static void local_proxy_opener_set_seat(Interactor *itr, Seat *seat)
- {
- LocalProxyOpener *lp = container_of(itr, LocalProxyOpener, interactor);
- lp->clientseat = seat;
- }
- static const InteractorVtable LocalProxyOpener_interactorvt = {
- .description = local_proxy_opener_description,
- .logpolicy = local_proxy_opener_logpolicy,
- .get_seat = local_proxy_opener_get_seat,
- .set_seat = local_proxy_opener_set_seat,
- };
- static void local_proxy_opener_cleanup_interactor(LocalProxyOpener *lp)
- {
- if (lp->clientseat) {
- interactor_return_seat(lp->clientitr);
- lp->clientitr = NULL;
- lp->clientseat = NULL;
- }
- }
- static void local_proxy_opener_coroutine(void *vctx)
- {
- LocalProxyOpener *lp = (LocalProxyOpener *)vctx;
- crBegin(lp->crLine);
- /*
- * Make an initial attempt to figure out the command we want, and
- * see if it tried to include a username or password that we don't
- * have.
- */
- {
- unsigned flags;
- lp->formatted_cmd = format_telnet_command(
- lp->addr, lp->port, lp->conf, &flags);
- if (lp->clientseat && (flags & (TELNET_CMD_MISSING_USERNAME |
- TELNET_CMD_MISSING_PASSWORD))) {
- burnstr(lp->formatted_cmd);
- lp->formatted_cmd = NULL;
- /*
- * We're missing at least one of the two parts, and we
- * have an Interactor we can use to prompt for them, so
- * try it.
- */
- lp->prompts = new_prompts();
- lp->prompts->callback = local_proxy_opener_coroutine;
- lp->prompts->callback_ctx = lp;
- lp->prompts->to_server = true;
- lp->prompts->from_server = false;
- lp->prompts->name = dupstr("Local proxy authentication");
- if (flags & TELNET_CMD_MISSING_USERNAME) {
- lp->username_prompt_index = lp->prompts->n_prompts;
- add_prompt(lp->prompts, dupstr("Proxy username: "), true);
- } else {
- lp->username_prompt_index = -1;
- }
- if (flags & TELNET_CMD_MISSING_PASSWORD) {
- lp->password_prompt_index = lp->prompts->n_prompts;
- add_prompt(lp->prompts, dupstr("Proxy password: "), false);
- } else {
- lp->password_prompt_index = -1;
- }
- while (true) {
- SeatPromptResult spr = seat_get_userpass_input(
- interactor_announce(&lp->interactor), lp->prompts);
- if (spr.kind == SPRK_OK) {
- break;
- } else if (spr.kind == SPRK_USER_ABORT) {
- local_proxy_opener_cleanup_interactor(lp);
- plug_closing_user_abort(lp->plug);
- /* That will have freed us, so we must just return
- * without calling any crStop */
- return;
- } else if (spr.kind == SPRK_SW_ABORT) {
- local_proxy_opener_cleanup_interactor(lp);
- char *err = spr_get_error_message(spr);
- plug_closing_error(lp->plug, err);
- sfree(err);
- return; /* without crStop, as above */
- }
- crReturnV;
- }
- if (lp->username_prompt_index != -1) {
- conf_set_str(
- lp->conf, CONF_proxy_username,
- prompt_get_result_ref(
- lp->prompts->prompts[lp->username_prompt_index]));
- }
- if (lp->password_prompt_index != -1) {
- conf_set_str(
- lp->conf, CONF_proxy_password,
- prompt_get_result_ref(
- lp->prompts->prompts[lp->password_prompt_index]));
- }
- free_prompts(lp->prompts);
- lp->prompts = NULL;
- }
- /*
- * Now format the command a second time, with the results of
- * those prompts written into lp->conf.
- */
- lp->formatted_cmd = format_telnet_command(
- lp->addr, lp->port, lp->conf, NULL);
- }
- /*
- * Log the command, with some changes. Firstly, we regenerate it
- * with the password masked; secondly, we escape control
- * characters so that the log message is printable.
- */
- conf_set_str(lp->conf, CONF_proxy_password, "*password*");
- {
- char *censored_cmd = format_telnet_command(
- lp->addr, lp->port, lp->conf, NULL);
- strbuf *logmsg = strbuf_new();
- put_datapl(logmsg, PTRLEN_LITERAL("Starting local proxy command: "));
- put_c_string_literal(logmsg, ptrlen_from_asciz(censored_cmd));
- plug_log(lp->plug, lp->socket, PLUGLOG_PROXY_MSG, NULL, 0,
- logmsg->s, 0);
- strbuf_free(logmsg);
- sfree(censored_cmd);
- }
- /*
- * Now we're ready to actually do the platform-specific socket
- * setup.
- */
- char *cmd = lp->formatted_cmd;
- lp->formatted_cmd = NULL;
- local_proxy_opener_cleanup_interactor(lp);
- char *error_msg = platform_setup_local_proxy(lp->socket, cmd);
- burnstr(cmd);
- if (error_msg) {
- plug_closing_error(lp->plug, error_msg);
- sfree(error_msg);
- } else {
- /* If error_msg was NULL, there was no error in setup,
- * which means that platform_setup_local_proxy will have
- * called back to free us. So return without calling any
- * crStop. */
- return;
- }
- crFinishV;
- }
- DeferredSocketOpener *local_proxy_opener(
- SockAddr *addr, int port, Plug *plug, Conf *conf, Interactor *itr)
- {
- LocalProxyOpener *lp = snew(LocalProxyOpener);
- memset(lp, 0, sizeof(*lp));
- lp->plug = plug;
- lp->opener.vt = &LocalProxyOpener_openervt;
- lp->interactor.vt = &LocalProxyOpener_interactorvt;
- lp->addr = sk_addr_dup(addr);
- lp->port = port;
- lp->conf = conf_copy(conf);
- if (itr) {
- lp->clientitr = itr;
- interactor_set_child(lp->clientitr, &lp->interactor);
- lp->clientlp = interactor_logpolicy(lp->clientitr);
- lp->clientseat = interactor_borrow_seat(lp->clientitr);
- }
- return &lp->opener;
- }
- void local_proxy_opener_set_socket(DeferredSocketOpener *opener,
- Socket *socket)
- {
- assert(opener->vt == &LocalProxyOpener_openervt);
- LocalProxyOpener *lp = container_of(opener, LocalProxyOpener, opener);
- lp->socket = socket;
- queue_toplevel_callback(local_proxy_opener_coroutine, lp);
- }
|