mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
c1d9da67a2
This enables plug_log to run query methods on the socket in order to find out useful information to log. I don't expect it's sensible to do anything else with it.
274 lines
8.9 KiB
C
274 lines
8.9 KiB
C
/*
|
|
* 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);
|
|
}
|