diff options
Diffstat (limited to 'tmate-ssh-client.c')
-rw-r--r-- | tmate-ssh-client.c | 122 |
1 files changed, 98 insertions, 24 deletions
diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 50842d97..a930e28c 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -10,10 +10,11 @@ static void consume_channel(struct tmate_ssh_client *client); static void flush_input_stream(struct tmate_ssh_client *client); static void __flush_input_stream(evutil_socket_t fd, short what, void *arg); static void __on_session_event(evutil_socket_t fd, short what, void *arg); -static void printflike2 disconnect_session(struct tmate_ssh_client *client, - const char *fmt, ...); +static void printflike2 kill_session(struct tmate_ssh_client *client, + const char *fmt, ...); static void printflike2 reconnect_session(struct tmate_ssh_client *client, const char *fmt, ...); +static void on_session_event(struct tmate_ssh_client *client); static void log_function(ssh_session session, int priority, const char *message, void *userdata) @@ -79,7 +80,7 @@ static void connection_complete(struct tmate_ssh_client *connected_client) continue; assert(!client->has_encoder); - tmate_ssh_client_free(client); + kill_session(client, NULL); } } @@ -99,6 +100,66 @@ static char *get_identity(void) return identity; } +static int passphrase_callback(const char *prompt, char *buf, size_t len, + int echo, int verify, void *userdata) +{ + struct tmate_ssh_client *client = userdata; + + client->tmate_session->need_passphrase = 1; + + if (client->tmate_session->passphrase) + strncpy(buf, client->tmate_session->passphrase, len); + else + strcpy(buf, ""); + + return 0; +} + +static void on_passphrase_read(const char *passphrase, void *private) +{ + struct tmate_ssh_client *client = private; + + client->tmate_session->passphrase = xstrdup(passphrase); + on_session_event(client); +} + +static void request_passphrase(struct tmate_ssh_client *client) +{ + struct window_pane *wp; + struct window_copy_mode_data *data; + + /* + * We'll display the prompt on the first pane. + * It doesn't make much sense, but it's simpler to reuse the copy mode + * and its key parsing logic compared to rolling something on our own. + */ + wp = RB_MIN(window_pane_tree, &all_window_panes); + + if (wp->mode) { + data = wp->modedata; + if (data->inputtype == WINDOW_COPY_PASSWORD) { + /* We are already requesting the passphrase */ + return; + } + window_pane_reset_mode(wp); + } + + window_pane_set_mode(wp, &window_copy_mode); + window_copy_init_from_pane(wp); + data = wp->modedata; + + data->inputtype = WINDOW_COPY_PASSWORD; + data->inputprompt = "SSH key passphrase"; + + mode_key_init(&data->mdata, &mode_key_tree_vi_edit); + + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + + data->password_cb = on_passphrase_read; + data->password_cb_private = client; +} + static void on_session_event(struct tmate_ssh_client *client) { char *identity; @@ -139,6 +200,11 @@ static void on_session_event(struct tmate_ssh_client *client) ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); if ((identity = get_identity())) { + /* + * FIXME libssh will continue with the next set of + * keys if the identity has a passphrase and the + * regular one doesn't. + */ ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity); free(identity); } @@ -164,7 +230,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AUTH_SERVER: if ((hash_len = ssh_get_pubkey_hash(session, &hash)) < 0) { - disconnect_session(client, "Cannot authenticate server"); + kill_session(client, "Cannot authenticate server"); return; } @@ -199,7 +265,7 @@ static void on_session_event(struct tmate_ssh_client *client) free(hash_str); if (!match) { - disconnect_session(client, "Cannot authenticate server"); + kill_session(client, "Cannot authenticate server"); return; } @@ -216,14 +282,18 @@ static void on_session_event(struct tmate_ssh_client *client) /* fall through */ case SSH_AUTH_CLIENT: - switch (ssh_userauth_autopubkey(session, NULL)) { + client->tried_passphrase = client->tmate_session->passphrase; + switch (ssh_userauth_autopubkey(session, client->tried_passphrase)) { case SSH_AUTH_AGAIN: return; case SSH_AUTH_PARTIAL: case SSH_AUTH_INFO: case SSH_AUTH_DENIED: - disconnect_session(client, "Access denied. Check your SSH keys " - "(passphrases are not supported yet)."); + if (client->tmate_session->need_passphrase && + !client->tried_passphrase) + request_passphrase(client); + else + kill_session(client, "Access denied. Check your SSH keys."); return; case SSH_AUTH_ERROR: reconnect_session(client, "Auth error: %s", @@ -316,12 +386,12 @@ static void __on_session_event(evutil_socket_t fd, short what, void *arg) on_session_event(arg); } -static void __disconnect_session(struct tmate_ssh_client *client, - const char *fmt, va_list va) +static void __kill_session(struct tmate_ssh_client *client, + const char *fmt, va_list va) { struct tmate_encoder *encoder; - if (fmt) + if (fmt && TAILQ_EMPTY(&client->tmate_session->clients)) __tmate_status_message(fmt, va); else tmate_debug("Disconnecting %s", client->server_ip); @@ -348,14 +418,19 @@ static void __disconnect_session(struct tmate_ssh_client *client, client->state = SSH_NONE; } -static void printflike2 disconnect_session(struct tmate_ssh_client *client, - const char *fmt, ...) +static void printflike2 kill_session(struct tmate_ssh_client *client, + const char *fmt, ...) { va_list ap; + TAILQ_REMOVE(&client->tmate_session->clients, client, node); + va_start(ap, fmt); - __disconnect_session(client, fmt, ap); + __kill_session(client, fmt, ap); va_end(ap); + + free(client->server_ip); + free(client); } static void connect_session(struct tmate_ssh_client *client) @@ -377,8 +452,12 @@ static void printflike2 reconnect_session(struct tmate_ssh_client *client, struct timeval tv; va_list ap; +#if 1 + TAILQ_REMOVE(&client->tmate_session->clients, client, node); +#endif + va_start(ap, fmt); - __disconnect_session(client, fmt, ap); + __kill_session(client, fmt, ap); va_end(ap); /* Not yet implemented... */ @@ -389,16 +468,17 @@ static void printflike2 reconnect_session(struct tmate_ssh_client *client, #endif } - struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, const char *server_ip) { struct tmate_ssh_client *client; client = xmalloc(sizeof(*client)); + memset(&client->ssh_callbacks, 0, sizeof(client->ssh_callbacks)); ssh_callbacks_init(&client->ssh_callbacks); client->ssh_callbacks.log_function = log_function; client->ssh_callbacks.userdata = client; + client->ssh_callbacks.auth_function = passphrase_callback; client->tmate_session = session; TAILQ_INSERT_TAIL(&session->clients, client, node); @@ -409,6 +489,8 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, client->channel = NULL; client->has_encoder = 0; + client->ev_ssh.ev_flags = 0; + evtimer_assign(&client->ev_ssh_reconnect, ev_base, on_reconnect_timer, client); @@ -416,11 +498,3 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, return client; } - -void tmate_ssh_client_free(struct tmate_ssh_client *client) -{ - disconnect_session(client, NULL); - TAILQ_REMOVE(&client->tmate_session->clients, client, node); - free(client->server_ip); - free(client); -} |