mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 01:18:00 +00:00
Allow new_connection to take an optional Seat. (NFC)
This is working towards allowing the subsidiary SSH connection in an SshProxy to share the main user-facing Seat, so as to be able to pass through interactive prompts. This is more difficult than the similar change with LogPolicy, because Seats are stateful. In particular, the trust-sigil status will need to be controlled by the SshProxy until it's ready to pass over control to the main SSH (or whatever) connection. To make this work, I've introduced a thing called a TempSeat, which is (yet) another Seat implementation. When a backend hands its Seat to new_connection(), it does it in a way that allows new_connection() to borrow it completely, and replace it in the main backend structure with a TempSeat, which acts as a temporary placeholder. If the main backend tries to do things like changing trust status or sending output, the TempSeat will buffer them; later on, when the connection is established, TempSeat will replay the changes into the real Seat. So, in each backend, I've made the following changes: - pass &foo->seat to new_connection, which may overwrite it with a TempSeat. - if it has done so (which we can tell via the is_tempseat() query function), then we have to free the TempSeat and reinstate our main Seat. The signal that we can do so is the PLUGLOG_CONNECT_SUCCESS notification, which indicates that SshProxy has finished all its connection setup work. - we also have to remember to free the TempSeat if our backend is disposed of without that having happened (e.g. because the connection _doesn't_ succeed). - in backends which have no local auth phase to worry about, ensure we don't call seat_set_trust_status on the main Seat _before_ it gets potentially replaced with a TempSeat. Moved some calls of seat_set_trust_status to just after new_connection(), so that now the initial trust status setup will go into the TempSeat (if appropriate) and be buffered until that seat is relinquished. In all other uses of new_connection, where we don't have a Seat available at all, we just pass NULL. This is NFC, because neither new_connection() nor any of its delegates will _actually_ do this replacement yet. We're just setting up the framework to enable it to do so in the next commit.
This commit is contained in:
parent
a08f953bd6
commit
6d272ee007
19
network.h
19
network.h
@ -121,11 +121,28 @@ struct PlugVtable {
|
|||||||
* host proxy). If you don't have one, all proxy types are required to
|
* host proxy). If you don't have one, all proxy types are required to
|
||||||
* be able to manage without (and will just degrade their logging
|
* be able to manage without (and will just degrade their logging
|
||||||
* control).
|
* control).
|
||||||
|
*
|
||||||
|
* If calling this from a backend with a Seat, you can also give it a
|
||||||
|
* pointer to your 'Seat *'. In that situation, it might replace the
|
||||||
|
* 'Seat *' with a temporary seat of its own, and give the real Seat
|
||||||
|
* to the proxy system so that it can ask for passwords (and, in the
|
||||||
|
* case of SSH proxying, other prompts like host key checks). If that
|
||||||
|
* happens, then the resulting 'temp seat' is the backend's property,
|
||||||
|
* and it will have to remember to free it when cleaning up, or after
|
||||||
|
* flushing it back into the real seat when the network connection
|
||||||
|
* attempt completes.
|
||||||
|
*
|
||||||
|
* You can free your TempSeat and resume using the real Seat when one
|
||||||
|
* of two things happens: either your Plug's closing() method is
|
||||||
|
* called (indicating failure to connect), or its log() method is
|
||||||
|
* called with PLUGLOG_CONNECT_SUCCESS. In the latter case, you'll
|
||||||
|
* probably want to flush the TempSeat's contents into the real Seat,
|
||||||
|
* of course.
|
||||||
*/
|
*/
|
||||||
Socket *new_connection(SockAddr *addr, const char *hostname,
|
Socket *new_connection(SockAddr *addr, const char *hostname,
|
||||||
int port, bool privport,
|
int port, bool privport,
|
||||||
bool oobinline, bool nodelay, bool keepalive,
|
bool oobinline, bool nodelay, bool keepalive,
|
||||||
Plug *plug, Conf *conf, LogPolicy *lp);
|
Plug *plug, Conf *conf, LogPolicy *lp, Seat **seat);
|
||||||
Socket *new_listener(const char *srcaddr, int port, Plug *plug,
|
Socket *new_listener(const char *srcaddr, int port, Plug *plug,
|
||||||
bool local_host_only, Conf *conf, int addressfamily);
|
bool local_host_only, Conf *conf, int addressfamily);
|
||||||
SockAddr *name_lookup(const char *host, int port, char **canonicalname,
|
SockAddr *name_lookup(const char *host, int port, char **canonicalname,
|
||||||
|
@ -20,7 +20,7 @@ SockAddr *name_lookup(const char *host, int port, char **canonicalname,
|
|||||||
Socket *new_connection(SockAddr *addr, const char *hostname,
|
Socket *new_connection(SockAddr *addr, const char *hostname,
|
||||||
int port, bool privport,
|
int port, bool privport,
|
||||||
bool oobinline, bool nodelay, bool keepalive,
|
bool oobinline, bool nodelay, bool keepalive,
|
||||||
Plug *plug, Conf *conf, LogPolicy *lp)
|
Plug *plug, Conf *conf, LogPolicy *lp, Seat **seat)
|
||||||
{
|
{
|
||||||
return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);
|
return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,15 @@ static void raw_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
|
|||||||
Raw *raw = container_of(plug, Raw, plug);
|
Raw *raw = container_of(plug, Raw, plug);
|
||||||
backend_socket_log(raw->seat, raw->logctx, type, addr, port, error_msg,
|
backend_socket_log(raw->seat, raw->logctx, type, addr, port, error_msg,
|
||||||
error_code, raw->conf, raw->socket_connected);
|
error_code, raw->conf, raw->socket_connected);
|
||||||
if (type == PLUGLOG_CONNECT_SUCCESS)
|
if (type == PLUGLOG_CONNECT_SUCCESS) {
|
||||||
raw->socket_connected = true;
|
raw->socket_connected = true;
|
||||||
|
if (is_tempseat(raw->seat)) {
|
||||||
|
Seat *ts = raw->seat;
|
||||||
|
tempseat_flush(ts);
|
||||||
|
raw->seat = tempseat_get_real(ts);
|
||||||
|
tempseat_free(ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void raw_check_close(Raw *raw)
|
static void raw_check_close(Raw *raw)
|
||||||
@ -132,9 +139,6 @@ static char *raw_init(const BackendVtable *vt, Seat *seat,
|
|||||||
int addressfamily;
|
int addressfamily;
|
||||||
char *loghost;
|
char *loghost;
|
||||||
|
|
||||||
/* No local authentication phase in this protocol */
|
|
||||||
seat_set_trust_status(seat, false);
|
|
||||||
|
|
||||||
raw = snew(Raw);
|
raw = snew(Raw);
|
||||||
raw->plug.vt = &Raw_plugvt;
|
raw->plug.vt = &Raw_plugvt;
|
||||||
raw->backend.vt = vt;
|
raw->backend.vt = vt;
|
||||||
@ -168,10 +172,13 @@ static char *raw_init(const BackendVtable *vt, Seat *seat,
|
|||||||
*/
|
*/
|
||||||
raw->s = new_connection(addr, *realhost, port, false, true, nodelay,
|
raw->s = new_connection(addr, *realhost, port, false, true, nodelay,
|
||||||
keepalive, &raw->plug, conf,
|
keepalive, &raw->plug, conf,
|
||||||
log_get_policy(logctx));
|
log_get_policy(logctx), &raw->seat);
|
||||||
if ((err = sk_socket_error(raw->s)) != NULL)
|
if ((err = sk_socket_error(raw->s)) != NULL)
|
||||||
return dupstr(err);
|
return dupstr(err);
|
||||||
|
|
||||||
|
/* No local authentication phase in this protocol */
|
||||||
|
seat_set_trust_status(raw->seat, false);
|
||||||
|
|
||||||
loghost = conf_get_str(conf, CONF_loghost);
|
loghost = conf_get_str(conf, CONF_loghost);
|
||||||
if (*loghost) {
|
if (*loghost) {
|
||||||
char *colon;
|
char *colon;
|
||||||
@ -191,6 +198,8 @@ static void raw_free(Backend *be)
|
|||||||
{
|
{
|
||||||
Raw *raw = container_of(be, Raw, backend);
|
Raw *raw = container_of(be, Raw, backend);
|
||||||
|
|
||||||
|
if (is_tempseat(raw->seat))
|
||||||
|
tempseat_free(raw->seat);
|
||||||
if (raw->s)
|
if (raw->s)
|
||||||
sk_close(raw->s);
|
sk_close(raw->s);
|
||||||
conf_free(raw->conf);
|
conf_free(raw->conf);
|
||||||
|
@ -45,8 +45,15 @@ static void rlogin_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
|
|||||||
backend_socket_log(rlogin->seat, rlogin->logctx, type, addr, port,
|
backend_socket_log(rlogin->seat, rlogin->logctx, type, addr, port,
|
||||||
error_msg, error_code,
|
error_msg, error_code,
|
||||||
rlogin->conf, rlogin->socket_connected);
|
rlogin->conf, rlogin->socket_connected);
|
||||||
if (type == PLUGLOG_CONNECT_SUCCESS)
|
if (type == PLUGLOG_CONNECT_SUCCESS) {
|
||||||
rlogin->socket_connected = true;
|
rlogin->socket_connected = true;
|
||||||
|
if (is_tempseat(rlogin->seat)) {
|
||||||
|
Seat *ts = rlogin->seat;
|
||||||
|
tempseat_flush(ts);
|
||||||
|
rlogin->seat = tempseat_get_real(ts);
|
||||||
|
tempseat_free(ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rlogin_closing(Plug *plug, const char *error_msg, int error_code,
|
static void rlogin_closing(Plug *plug, const char *error_msg, int error_code,
|
||||||
@ -205,7 +212,7 @@ static char *rlogin_init(const BackendVtable *vt, Seat *seat,
|
|||||||
*/
|
*/
|
||||||
rlogin->s = new_connection(addr, *realhost, port, true, false,
|
rlogin->s = new_connection(addr, *realhost, port, true, false,
|
||||||
nodelay, keepalive, &rlogin->plug, conf,
|
nodelay, keepalive, &rlogin->plug, conf,
|
||||||
log_get_policy(logctx));
|
log_get_policy(logctx), &rlogin->seat);
|
||||||
if ((err = sk_socket_error(rlogin->s)) != NULL)
|
if ((err = sk_socket_error(rlogin->s)) != NULL)
|
||||||
return dupstr(err);
|
return dupstr(err);
|
||||||
|
|
||||||
@ -256,6 +263,8 @@ static void rlogin_free(Backend *be)
|
|||||||
{
|
{
|
||||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||||
|
|
||||||
|
if (is_tempseat(rlogin->seat))
|
||||||
|
tempseat_free(rlogin->seat);
|
||||||
if (rlogin->prompt)
|
if (rlogin->prompt)
|
||||||
free_prompts(rlogin->prompt);
|
free_prompts(rlogin->prompt);
|
||||||
if (rlogin->s)
|
if (rlogin->s)
|
||||||
|
@ -563,8 +563,15 @@ static void supdup_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
|
|||||||
backend_socket_log(supdup->seat, supdup->logctx, type, addr, port,
|
backend_socket_log(supdup->seat, supdup->logctx, type, addr, port,
|
||||||
error_msg, error_code,
|
error_msg, error_code,
|
||||||
supdup->conf, supdup->socket_connected);
|
supdup->conf, supdup->socket_connected);
|
||||||
if (type == PLUGLOG_CONNECT_SUCCESS)
|
if (type == PLUGLOG_CONNECT_SUCCESS) {
|
||||||
supdup->socket_connected = true;
|
supdup->socket_connected = true;
|
||||||
|
if (is_tempseat(supdup->seat)) {
|
||||||
|
Seat *ts = supdup->seat;
|
||||||
|
tempseat_flush(ts);
|
||||||
|
supdup->seat = tempseat_get_real(ts);
|
||||||
|
tempseat_free(ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void supdup_closing(Plug *plug, const char *error_msg, int error_code,
|
static void supdup_closing(Plug *plug, const char *error_msg, int error_code,
|
||||||
@ -713,7 +720,7 @@ static char *supdup_init(const BackendVtable *x, Seat *seat,
|
|||||||
*/
|
*/
|
||||||
supdup->s = new_connection(addr, *realhost, port, false, true,
|
supdup->s = new_connection(addr, *realhost, port, false, true,
|
||||||
nodelay, keepalive, &supdup->plug, supdup->conf,
|
nodelay, keepalive, &supdup->plug, supdup->conf,
|
||||||
log_get_policy(logctx));
|
log_get_policy(logctx), &supdup->seat);
|
||||||
if ((err = sk_socket_error(supdup->s)) != NULL)
|
if ((err = sk_socket_error(supdup->s)) != NULL)
|
||||||
return dupstr(err);
|
return dupstr(err);
|
||||||
|
|
||||||
@ -783,6 +790,8 @@ static void supdup_free(Backend *be)
|
|||||||
{
|
{
|
||||||
Supdup *supdup = container_of(be, Supdup, backend);
|
Supdup *supdup = container_of(be, Supdup, backend);
|
||||||
|
|
||||||
|
if (is_tempseat(supdup->seat))
|
||||||
|
tempseat_free(supdup->seat);
|
||||||
if (supdup->s)
|
if (supdup->s)
|
||||||
sk_close(supdup->s);
|
sk_close(supdup->s);
|
||||||
if (supdup->pinger)
|
if (supdup->pinger)
|
||||||
|
@ -626,8 +626,15 @@ static void telnet_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
|
|||||||
backend_socket_log(telnet->seat, telnet->logctx, type, addr, port,
|
backend_socket_log(telnet->seat, telnet->logctx, type, addr, port,
|
||||||
error_msg, error_code, telnet->conf,
|
error_msg, error_code, telnet->conf,
|
||||||
telnet->socket_connected);
|
telnet->socket_connected);
|
||||||
if (type == PLUGLOG_CONNECT_SUCCESS)
|
if (type == PLUGLOG_CONNECT_SUCCESS) {
|
||||||
telnet->socket_connected = true;
|
telnet->socket_connected = true;
|
||||||
|
if (is_tempseat(telnet->seat)) {
|
||||||
|
Seat *ts = telnet->seat;
|
||||||
|
tempseat_flush(ts);
|
||||||
|
telnet->seat = tempseat_get_real(ts);
|
||||||
|
tempseat_free(ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void telnet_closing(Plug *plug, const char *error_msg, int error_code,
|
static void telnet_closing(Plug *plug, const char *error_msg, int error_code,
|
||||||
@ -698,9 +705,6 @@ static char *telnet_init(const BackendVtable *vt, Seat *seat,
|
|||||||
char *loghost;
|
char *loghost;
|
||||||
int addressfamily;
|
int addressfamily;
|
||||||
|
|
||||||
/* No local authentication phase in this protocol */
|
|
||||||
seat_set_trust_status(seat, false);
|
|
||||||
|
|
||||||
telnet = snew(Telnet);
|
telnet = snew(Telnet);
|
||||||
telnet->plug.vt = &Telnet_plugvt;
|
telnet->plug.vt = &Telnet_plugvt;
|
||||||
telnet->backend.vt = vt;
|
telnet->backend.vt = vt;
|
||||||
@ -740,10 +744,13 @@ static char *telnet_init(const BackendVtable *vt, Seat *seat,
|
|||||||
*/
|
*/
|
||||||
telnet->s = new_connection(addr, *realhost, port, false, true, nodelay,
|
telnet->s = new_connection(addr, *realhost, port, false, true, nodelay,
|
||||||
keepalive, &telnet->plug, telnet->conf,
|
keepalive, &telnet->plug, telnet->conf,
|
||||||
log_get_policy(logctx));
|
log_get_policy(logctx), &telnet->seat);
|
||||||
if ((err = sk_socket_error(telnet->s)) != NULL)
|
if ((err = sk_socket_error(telnet->s)) != NULL)
|
||||||
return dupstr(err);
|
return dupstr(err);
|
||||||
|
|
||||||
|
/* No local authentication phase in this protocol */
|
||||||
|
seat_set_trust_status(telnet->seat, false);
|
||||||
|
|
||||||
telnet->pinger = pinger_new(telnet->conf, &telnet->backend);
|
telnet->pinger = pinger_new(telnet->conf, &telnet->backend);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -797,6 +804,8 @@ static void telnet_free(Backend *be)
|
|||||||
{
|
{
|
||||||
Telnet *telnet = container_of(be, Telnet, backend);
|
Telnet *telnet = container_of(be, Telnet, backend);
|
||||||
|
|
||||||
|
if (is_tempseat(telnet->seat))
|
||||||
|
tempseat_free(telnet->seat);
|
||||||
strbuf_free(telnet->sb_buf);
|
strbuf_free(telnet->sb_buf);
|
||||||
if (telnet->s)
|
if (telnet->s)
|
||||||
sk_close(telnet->s);
|
sk_close(telnet->s);
|
||||||
|
25
putty.h
25
putty.h
@ -1295,6 +1295,31 @@ bool console_can_set_trust_status(Seat *seat);
|
|||||||
int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input);
|
int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input);
|
||||||
bool cmdline_seat_verbose(Seat *seat);
|
bool cmdline_seat_verbose(Seat *seat);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TempSeat: a seat implementation that can be given to a backend
|
||||||
|
* temporarily while network proxy setup is using the real seat.
|
||||||
|
* Buffers output and trust-status changes until the real seat is
|
||||||
|
* available again.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Called by the proxy code to make a TempSeat. */
|
||||||
|
Seat *tempseat_new(Seat *real);
|
||||||
|
|
||||||
|
/* Query functions to tell if a Seat _is_ temporary, and if so, to
|
||||||
|
* return the underlying real Seat. */
|
||||||
|
bool is_tempseat(Seat *seat);
|
||||||
|
Seat *tempseat_get_real(Seat *seat);
|
||||||
|
|
||||||
|
/* Called by the backend once the proxy connection has finished
|
||||||
|
* setting up (or failed), to pass on any buffered stuff to the real
|
||||||
|
* seat. */
|
||||||
|
void tempseat_flush(Seat *ts);
|
||||||
|
|
||||||
|
/* Frees a TempSeat, without flushing anything it has buffered. (Call
|
||||||
|
* this after tempseat_flush, or alternatively, when you were going to
|
||||||
|
* abandon the whole connection anyway.) */
|
||||||
|
void tempseat_free(Seat *ts);
|
||||||
|
|
||||||
typedef struct rgb {
|
typedef struct rgb {
|
||||||
uint8_t r, g, b;
|
uint8_t r, g, b;
|
||||||
} rgb;
|
} rgb;
|
||||||
|
@ -1161,7 +1161,7 @@ char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
|
|||||||
|
|
||||||
pf->s = new_connection(addr, dummy_realhost, port,
|
pf->s = new_connection(addr, dummy_realhost, port,
|
||||||
false, true, false, false, &pf->plug, mgr->conf,
|
false, true, false, false, &pf->plug, mgr->conf,
|
||||||
NULL);
|
NULL, NULL);
|
||||||
sfree(dummy_realhost);
|
sfree(dummy_realhost);
|
||||||
if ((err = sk_socket_error(pf->s)) != NULL) {
|
if ((err = sk_socket_error(pf->s)) != NULL) {
|
||||||
char *err_ret = dupstr(err);
|
char *err_ret = dupstr(err);
|
||||||
|
14
ssh/ssh.c
14
ssh/ssh.c
@ -598,6 +598,15 @@ static void ssh_socket_log(Plug *plug, PlugLogType type, SockAddr *addr,
|
|||||||
backend_socket_log(ssh->seat, ssh->logctx, type, addr, port,
|
backend_socket_log(ssh->seat, ssh->logctx, type, addr, port,
|
||||||
error_msg, error_code, ssh->conf,
|
error_msg, error_code, ssh->conf,
|
||||||
ssh->session_started);
|
ssh->session_started);
|
||||||
|
|
||||||
|
if (type == PLUGLOG_CONNECT_SUCCESS) {
|
||||||
|
if (is_tempseat(ssh->seat)) {
|
||||||
|
Seat *ts = ssh->seat;
|
||||||
|
tempseat_flush(ts);
|
||||||
|
ssh->seat = tempseat_get_real(ts);
|
||||||
|
tempseat_free(ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssh_closing(Plug *plug, const char *error_msg, int error_code,
|
static void ssh_closing(Plug *plug, const char *error_msg, int error_code,
|
||||||
@ -790,7 +799,7 @@ static char *connect_to_host(
|
|||||||
ssh->s = new_connection(addr, *realhost, port,
|
ssh->s = new_connection(addr, *realhost, port,
|
||||||
false, true, nodelay, keepalive,
|
false, true, nodelay, keepalive,
|
||||||
&ssh->plug, ssh->conf,
|
&ssh->plug, ssh->conf,
|
||||||
log_get_policy(ssh->logctx));
|
log_get_policy(ssh->logctx), &ssh->seat);
|
||||||
if ((err = sk_socket_error(ssh->s)) != NULL) {
|
if ((err = sk_socket_error(ssh->s)) != NULL) {
|
||||||
ssh->s = NULL;
|
ssh->s = NULL;
|
||||||
seat_notify_remote_exit(ssh->seat);
|
seat_notify_remote_exit(ssh->seat);
|
||||||
@ -955,6 +964,9 @@ static void ssh_free(Backend *be)
|
|||||||
|
|
||||||
ssh_shutdown(ssh);
|
ssh_shutdown(ssh);
|
||||||
|
|
||||||
|
if (is_tempseat(ssh->seat))
|
||||||
|
tempseat_free(ssh->seat);
|
||||||
|
|
||||||
conf_free(ssh->conf);
|
conf_free(ssh->conf);
|
||||||
if (ssh->connshare)
|
if (ssh->connshare)
|
||||||
sharestate_free(ssh->connshare);
|
sharestate_free(ssh->connshare);
|
||||||
|
@ -564,7 +564,7 @@ static size_t x11_send(
|
|||||||
xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),
|
xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),
|
||||||
xconn->disp->realhost, xconn->disp->port,
|
xconn->disp->realhost, xconn->disp->port,
|
||||||
false, true, false, false, &xconn->plug,
|
false, true, false, false, &xconn->plug,
|
||||||
sshfwd_get_conf(xconn->c), NULL);
|
sshfwd_get_conf(xconn->c), NULL, NULL);
|
||||||
if ((err = sk_socket_error(xconn->s)) != NULL) {
|
if ((err = sk_socket_error(xconn->s)) != NULL) {
|
||||||
char *err_message = dupprintf("unable to connect to"
|
char *err_message = dupprintf("unable to connect to"
|
||||||
" forwarded X server: %s", err);
|
" forwarded X server: %s", err);
|
||||||
|
@ -1190,7 +1190,7 @@ void run_agent(FILE *logfp, const char *symlink_path)
|
|||||||
s = new_connection(sk_addr_dup(disp->addr),
|
s = new_connection(sk_addr_dup(disp->addr),
|
||||||
disp->realhost, disp->port,
|
disp->realhost, disp->port,
|
||||||
false, true, false, false, &conn->plug, conf,
|
false, true, false, false, &conn->plug, conf,
|
||||||
NULL);
|
NULL, NULL);
|
||||||
if ((err = sk_socket_error(s)) != NULL) {
|
if ((err = sk_socket_error(s)) != NULL) {
|
||||||
fprintf(stderr, "pageant: unable to connect to X server: %s", err);
|
fprintf(stderr, "pageant: unable to connect to X server: %s", err);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -297,7 +297,7 @@ int platform_ssh_share(const char *pi_name, Conf *conf,
|
|||||||
if (can_downstream) {
|
if (can_downstream) {
|
||||||
retsock = new_connection(unix_sock_addr(sockname),
|
retsock = new_connection(unix_sock_addr(sockname),
|
||||||
"", 0, false, true, false, false,
|
"", 0, false, true, false, false,
|
||||||
downplug, conf, NULL);
|
downplug, conf, NULL, NULL);
|
||||||
if (sk_socket_error(retsock) == NULL) {
|
if (sk_socket_error(retsock) == NULL) {
|
||||||
sfree(*logtext);
|
sfree(*logtext);
|
||||||
*logtext = sockname;
|
*logtext = sockname;
|
||||||
|
@ -42,6 +42,7 @@ add_sources_from_current_dir(utils
|
|||||||
strbuf.c
|
strbuf.c
|
||||||
string_length_for_printf.c
|
string_length_for_printf.c
|
||||||
stripctrl.c
|
stripctrl.c
|
||||||
|
tempseat.c
|
||||||
tree234.c
|
tree234.c
|
||||||
validate_manual_hostkey.c
|
validate_manual_hostkey.c
|
||||||
version.c
|
version.c
|
||||||
|
353
utils/tempseat.c
Normal file
353
utils/tempseat.c
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of the Seat trait that buffers output and other
|
||||||
|
* events until it can give them back to a real Seat.
|
||||||
|
*
|
||||||
|
* This is used by the SSH proxying code, which temporarily takes over
|
||||||
|
* the real user-facing Seat so that it can issue host key warnings,
|
||||||
|
* password prompts etc for the proxy SSH connection. While it's got
|
||||||
|
* the real Seat, it gives the primary connection's backend one of
|
||||||
|
* these temporary Seats in the interim, so that if the backend wants
|
||||||
|
* to send some kind of initial output, or start by reconfiguring the
|
||||||
|
* trust status, or what have you, then it can do that without having
|
||||||
|
* to keep careful track of the fact that its Seat is out on loan.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
typedef struct TempSeat TempSeat;
|
||||||
|
struct TempSeat {
|
||||||
|
Seat *realseat;
|
||||||
|
bufchain outputs[2]; /* stdout, stderr */
|
||||||
|
bool seen_session_started;
|
||||||
|
bool seen_remote_exit;
|
||||||
|
bool seen_remote_disconnect;
|
||||||
|
bool seen_update_specials_menu;
|
||||||
|
bool seen_echoedit_update, echoing, editing;
|
||||||
|
bool seen_trust_status, trusted;
|
||||||
|
|
||||||
|
Seat seat;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* Methods we can usefully buffer, and pass their results on to the
|
||||||
|
* real Seat in tempseat_flush().
|
||||||
|
*/
|
||||||
|
|
||||||
|
static size_t tempseat_output(Seat *seat, bool is_stderr, const void *data,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
bufchain_add(&ts->outputs[is_stderr], data, len);
|
||||||
|
return bufchain_size(&ts->outputs[0]) + bufchain_size(&ts->outputs[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tempseat_notify_session_started(Seat *seat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
ts->seen_session_started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tempseat_notify_remote_exit(Seat *seat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
ts->seen_remote_exit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tempseat_notify_remote_disconnect(Seat *seat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
ts->seen_remote_disconnect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tempseat_update_specials_menu(Seat *seat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
ts->seen_update_specials_menu = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tempseat_echoedit_update(Seat *seat, bool echoing, bool editing)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
ts->seen_echoedit_update = true;
|
||||||
|
ts->echoing = echoing;
|
||||||
|
ts->editing = editing;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tempseat_set_trust_status(Seat *seat, bool trusted)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
ts->seen_trust_status = true;
|
||||||
|
ts->trusted = trusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* Methods we can safely pass straight on to the real Seat, usually
|
||||||
|
* (but not in every case) because they're read-only queries.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *tempseat_get_ttymode(Seat *seat, const char *mode)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_get_ttymode(ts->realseat, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tempseat_set_busy_status(Seat *seat, BusyStatus status)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
/*
|
||||||
|
* set_busy_status is generally called when something is about to
|
||||||
|
* do some single-threaded, event-loop blocking computation. This
|
||||||
|
* _shouldn't_ happen in a backend while it's waiting for a
|
||||||
|
* network connection to be made, but if for some reason it were
|
||||||
|
* to, there's no reason we can't just pass this straight to the
|
||||||
|
* real seat, because we expect that it will mark itself busy,
|
||||||
|
* compute, and mark itself unbusy, all between yields to the
|
||||||
|
* event loop that might give whatever else is using the real Seat
|
||||||
|
* an opportunity to do anything.
|
||||||
|
*/
|
||||||
|
seat_set_busy_status(ts->realseat, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tempseat_is_utf8(Seat *seat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_is_utf8(ts->realseat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *tempseat_get_x_display(Seat *seat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_get_x_display(ts->realseat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tempseat_get_windowid(Seat *seat, long *id_out)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_get_windowid(ts->realseat, id_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tempseat_get_window_pixel_size(Seat *seat, int *width, int *height)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_get_window_pixel_size(ts->realseat, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static StripCtrlChars *tempseat_stripctrl_new(
|
||||||
|
Seat *seat, BinarySink *bs_out, SeatInteractionContext sic)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_stripctrl_new(ts->realseat, bs_out, sic);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tempseat_verbose(Seat *seat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_verbose(ts->realseat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tempseat_interactive(Seat *seat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_interactive(ts->realseat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tempseat_get_cursor_position(Seat *seat, int *x, int *y)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_get_cursor_position(ts->realseat, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tempseat_can_set_trust_status(Seat *seat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return seat_can_set_trust_status(ts->realseat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* Methods that should never be called on a TempSeat, so we can put an
|
||||||
|
* unreachable() in them.
|
||||||
|
*
|
||||||
|
* A backend in possession of a TempSeat ought to be sitting and
|
||||||
|
* patiently waiting for a network connection attempt to either
|
||||||
|
* succeed or fail. And it should be aware of the possibility that the
|
||||||
|
* proxy setup code to which it has lent the real Seat might need to
|
||||||
|
* present interactive prompts - that's the whole point of lending out
|
||||||
|
* the Seat in the first place - so it absolutely shouldn't get any
|
||||||
|
* ideas about issuing some kind of prompt of its own while it waits
|
||||||
|
* for the network connection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int tempseat_get_userpass_input(Seat *seat, prompts_t *p,
|
||||||
|
bufchain *input)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Interactive prompts of this nature are a thing that a backend
|
||||||
|
* MUST NOT do while not in possession of the real Seat, because
|
||||||
|
* the whole point of temporarily lending the real Seat to
|
||||||
|
* something else is that so it can have a clear field to do
|
||||||
|
* interactive stuff of its own while making a network connection.
|
||||||
|
*/
|
||||||
|
unreachable("get_userpass_input should never be called on TempSeat");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tempseat_verify_ssh_host_key(
|
||||||
|
Seat *seat, const char *host, int port, const char *keytype,
|
||||||
|
char *keystr, const char *keydisp, char **key_fingerprints,
|
||||||
|
void (*callback)(void *ctx, int result), void *ctx)
|
||||||
|
{
|
||||||
|
unreachable("verify_ssh_host_key should never be called on TempSeat");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tempseat_confirm_weak_crypto_primitive(
|
||||||
|
Seat *seat, const char *algtype, const char *algname,
|
||||||
|
void (*callback)(void *ctx, int result), void *ctx)
|
||||||
|
{
|
||||||
|
unreachable("confirm_weak_crypto_primitive "
|
||||||
|
"should never be called on TempSeat");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tempseat_confirm_weak_cached_hostkey(
|
||||||
|
Seat *seat, const char *algname, const char *betteralgs,
|
||||||
|
void (*callback)(void *ctx, int result), void *ctx)
|
||||||
|
{
|
||||||
|
unreachable("confirm_weak_cached_hostkey "
|
||||||
|
"should never be called on TempSeat");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tempseat_connection_fatal(Seat *seat, const char *message)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Fatal errors are another thing a backend should not have any
|
||||||
|
* reason to encounter while waiting to hear back about its
|
||||||
|
* network connection setup.
|
||||||
|
*
|
||||||
|
* Also, if a backend _did_ call this, it would be hellish to
|
||||||
|
* unpick all the error handling. Just passing on the fatal error
|
||||||
|
* to the real Seat wouldn't be good enough: what about freeing
|
||||||
|
* all the various things that are confusingly holding pointers to
|
||||||
|
* each other? Better to leave this as an assertion-failure level
|
||||||
|
* issue, so that if it does ever happen by accident, we'll know
|
||||||
|
* it's a bug.
|
||||||
|
*/
|
||||||
|
unreachable("connection_fatal should never be called on TempSeat");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tempseat_eof(Seat *seat)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* EOF is _very nearly_ something that we could buffer, and pass
|
||||||
|
* on to the real Seat at flush time. The only difficulty is that
|
||||||
|
* sometimes the front end wants to respond to an incoming EOF by
|
||||||
|
* instructing the back end to send an outgoing one, which it does
|
||||||
|
* by returning a bool from its eof method.
|
||||||
|
*
|
||||||
|
* So we'd have to arrange that tempseat_flush caught that return
|
||||||
|
* value and passed it on to the calling backend. And then every
|
||||||
|
* backend would have to deal with tempseat_flush maybe returning
|
||||||
|
* it an 'actually, please start closing down now' indication,
|
||||||
|
* which could only happen _in theory_, if it had for some reason
|
||||||
|
* called seat_eof on the TempSeat.
|
||||||
|
*
|
||||||
|
* But in fact, we don't expect back ends to call seat_eof on the
|
||||||
|
* TempSeat in the first place, so all of that effort would be a
|
||||||
|
* total waste. Hence, we'll put EOF in the category of things we
|
||||||
|
* expect backends never to do while the real Seat is out on loan.
|
||||||
|
*/
|
||||||
|
unreachable("eof should never be called on TempSeat");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* Done with the TempSeat methods. Here's the vtable definition and
|
||||||
|
* the main setup/teardown code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const struct SeatVtable tempseat_vt = {
|
||||||
|
.output = tempseat_output,
|
||||||
|
.eof = tempseat_eof,
|
||||||
|
.sent = nullseat_sent,
|
||||||
|
.get_userpass_input = tempseat_get_userpass_input,
|
||||||
|
.notify_session_started = tempseat_notify_session_started,
|
||||||
|
.notify_remote_exit = tempseat_notify_remote_exit,
|
||||||
|
.notify_remote_disconnect = tempseat_notify_remote_disconnect,
|
||||||
|
.connection_fatal = tempseat_connection_fatal,
|
||||||
|
.update_specials_menu = tempseat_update_specials_menu,
|
||||||
|
.get_ttymode = tempseat_get_ttymode,
|
||||||
|
.set_busy_status = tempseat_set_busy_status,
|
||||||
|
.verify_ssh_host_key = tempseat_verify_ssh_host_key,
|
||||||
|
.confirm_weak_crypto_primitive = tempseat_confirm_weak_crypto_primitive,
|
||||||
|
.confirm_weak_cached_hostkey = tempseat_confirm_weak_cached_hostkey,
|
||||||
|
.is_utf8 = tempseat_is_utf8,
|
||||||
|
.echoedit_update = tempseat_echoedit_update,
|
||||||
|
.get_x_display = tempseat_get_x_display,
|
||||||
|
.get_windowid = tempseat_get_windowid,
|
||||||
|
.get_window_pixel_size = tempseat_get_window_pixel_size,
|
||||||
|
.stripctrl_new = tempseat_stripctrl_new,
|
||||||
|
.set_trust_status = tempseat_set_trust_status,
|
||||||
|
.can_set_trust_status = tempseat_can_set_trust_status,
|
||||||
|
.verbose = tempseat_verbose,
|
||||||
|
.interactive = tempseat_interactive,
|
||||||
|
.get_cursor_position = tempseat_get_cursor_position,
|
||||||
|
};
|
||||||
|
|
||||||
|
Seat *tempseat_new(Seat *realseat)
|
||||||
|
{
|
||||||
|
TempSeat *ts = snew(TempSeat);
|
||||||
|
memset(ts, 0, sizeof(*ts));
|
||||||
|
ts->seat.vt = &tempseat_vt;
|
||||||
|
|
||||||
|
ts->realseat = realseat;
|
||||||
|
for (unsigned i = 0; i < 2; i++)
|
||||||
|
bufchain_init(&ts->outputs[i]);
|
||||||
|
|
||||||
|
return &ts->seat;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_tempseat(Seat *seat)
|
||||||
|
{
|
||||||
|
return seat->vt == &tempseat_vt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Seat *tempseat_get_real(Seat *seat)
|
||||||
|
{
|
||||||
|
assert(seat->vt == &tempseat_vt);
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
return ts->realseat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tempseat_free(Seat *seat)
|
||||||
|
{
|
||||||
|
assert(seat->vt == &tempseat_vt);
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
for (unsigned i = 0; i < 2; i++)
|
||||||
|
bufchain_clear(&ts->outputs[i]);
|
||||||
|
sfree(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tempseat_flush(Seat *seat)
|
||||||
|
{
|
||||||
|
assert(seat->vt == &tempseat_vt);
|
||||||
|
TempSeat *ts = container_of(seat, TempSeat, seat);
|
||||||
|
|
||||||
|
/* Empty the stdout/stderr bufchains into the real seat */
|
||||||
|
for (unsigned i = 0; i < 2; i++) {
|
||||||
|
while (bufchain_size(&ts->outputs[i])) {
|
||||||
|
ptrlen pl = bufchain_prefix(&ts->outputs[i]);
|
||||||
|
seat_output(ts->realseat, i, pl.ptr, pl.len);
|
||||||
|
bufchain_consume(&ts->outputs[i], pl.len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pass on any other kinds of event we've buffered */
|
||||||
|
if (ts->seen_session_started)
|
||||||
|
seat_notify_session_started(ts->realseat);
|
||||||
|
if (ts->seen_remote_exit)
|
||||||
|
seat_notify_remote_exit(ts->realseat);
|
||||||
|
if (ts->seen_remote_disconnect)
|
||||||
|
seat_notify_remote_disconnect(ts->realseat);
|
||||||
|
if (ts->seen_update_specials_menu)
|
||||||
|
seat_update_specials_menu(ts->realseat);
|
||||||
|
if (ts->seen_echoedit_update)
|
||||||
|
seat_echoedit_update(ts->realseat, ts->echoing, ts->editing);
|
||||||
|
if (ts->seen_trust_status)
|
||||||
|
seat_set_trust_status(ts->realseat, ts->trusted);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user