tmate-ssh-client.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. #include <sys/socket.h>
  2. #include <netinet/tcp.h>
  3. #include <stdio.h>
  4. #include <event.h>
  5. #include <assert.h>
  6. #include "tmate.h"
  7. #include "window-copy.h"
  8. static void on_ssh_client_event(struct tmate_ssh_client *client);
  9. static void __on_ssh_client_event(evutil_socket_t fd, short what, void *arg);
  10. static void printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client,
  11. const char *fmt, ...);
  12. static void printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client,
  13. const char *fmt, ...);
  14. static void read_channel(struct tmate_ssh_client *client)
  15. {
  16. struct tmate_decoder *decoder = &client->tmate_session->decoder;
  17. char *buf;
  18. ssize_t len;
  19. for (;;) {
  20. tmate_decoder_get_buffer(decoder, &buf, &len);
  21. len = ssh_channel_read_nonblocking(client->channel, buf, len, 0);
  22. if (len < 0) {
  23. kill_ssh_client(client, "Error reading from channel: %s",
  24. ssh_get_error(client->session));
  25. break;
  26. }
  27. if (len == 0)
  28. break;
  29. tmate_decoder_commit(decoder, len);
  30. }
  31. }
  32. static void on_decoder_read(void *userdata, struct tmate_unpacker *uk)
  33. {
  34. struct tmate_ssh_client *client = userdata;
  35. tmate_dispatch_slave_message(client->tmate_session, uk);
  36. }
  37. static void on_encoder_write(void *userdata, struct evbuffer *buffer)
  38. {
  39. struct tmate_ssh_client *client = userdata;
  40. ssize_t len, written;
  41. unsigned char *buf;
  42. if (!client->channel)
  43. return;
  44. for(;;) {
  45. len = evbuffer_get_length(buffer);
  46. if (!len)
  47. break;
  48. buf = evbuffer_pullup(buffer, -1);
  49. written = ssh_channel_write(client->channel, buf, len);
  50. if (written < 0) {
  51. kill_ssh_client(client, "Error writing to channel: %s",
  52. ssh_get_error(client->session));
  53. break;
  54. }
  55. evbuffer_drain(buffer, written);
  56. }
  57. }
  58. static void on_ssh_auth_server_complete(struct tmate_ssh_client *connected_client)
  59. {
  60. /*
  61. * The first ssh connection succeeded. Hopefully this one offers the
  62. * best latency. We can now kill the other ssh clients that are trying
  63. * to connect.
  64. */
  65. struct tmate_session *session = connected_client->tmate_session;
  66. struct tmate_ssh_client *client, *tmp_client;
  67. TAILQ_FOREACH_SAFE(client, &session->clients, node, tmp_client) {
  68. if (client == connected_client)
  69. continue;
  70. assert(!client->has_encoder);
  71. kill_ssh_client(client, NULL);
  72. }
  73. }
  74. static char *get_identity(void)
  75. {
  76. char *identity;
  77. identity = options_get_string(global_options, "tmate-identity");
  78. if (!strlen(identity))
  79. return NULL;
  80. if (strchr(identity, '/'))
  81. identity = xstrdup(identity);
  82. else
  83. xasprintf(&identity, "%%d/%s", identity);
  84. return identity;
  85. }
  86. static int passphrase_callback(__unused const char *prompt, char *buf, size_t len,
  87. __unused int echo, __unused int verify, void *userdata)
  88. {
  89. struct tmate_ssh_client *client = userdata;
  90. client->tmate_session->need_passphrase = 1;
  91. if (client->tmate_session->passphrase)
  92. strncpy(buf, client->tmate_session->passphrase, len);
  93. else
  94. strcpy(buf, "");
  95. return 0;
  96. }
  97. static void on_passphrase_read(const char *passphrase, void *private)
  98. {
  99. struct tmate_ssh_client *client = private;
  100. client->tmate_session->passphrase = xstrdup(passphrase);
  101. on_ssh_client_event(client);
  102. }
  103. static void request_passphrase(struct tmate_ssh_client *client)
  104. {
  105. struct window_pane *wp;
  106. struct window_copy_mode_data *data;
  107. /*
  108. * We'll display the prompt on the first pane.
  109. * It doesn't make much sense, but it's simpler to reuse the copy mode
  110. * and its key parsing logic compared to rolling something on our own.
  111. */
  112. wp = RB_MIN(window_pane_tree, &all_window_panes);
  113. if (wp->mode) {
  114. data = wp->modedata;
  115. if (data->inputtype == WINDOW_COPY_PASSWORD) {
  116. /* We are already requesting the passphrase */
  117. return;
  118. }
  119. window_pane_reset_mode(wp);
  120. }
  121. window_pane_set_mode(wp, &window_copy_mode);
  122. window_copy_init_from_pane(wp, 0);
  123. data = wp->modedata;
  124. data->inputtype = WINDOW_COPY_PASSWORD;
  125. data->inputprompt = "SSH key passphrase";
  126. mode_key_init(&data->mdata, &mode_key_tree_vi_edit);
  127. window_copy_update_selection(wp, 1);
  128. window_copy_redraw_screen(wp);
  129. data->password_cb = on_passphrase_read;
  130. data->password_cb_private = client;
  131. }
  132. static void init_conn_fd(struct tmate_ssh_client *client)
  133. {
  134. if (client->has_init_conn_fd)
  135. return;
  136. if (ssh_get_fd(client->session) < 0)
  137. return;
  138. {
  139. int flag = 1;
  140. setsockopt(ssh_get_fd(client->session), IPPROTO_TCP,
  141. TCP_NODELAY, &flag, sizeof(flag));
  142. }
  143. event_set(&client->ev_ssh, ssh_get_fd(client->session),
  144. EV_READ | EV_PERSIST, __on_ssh_client_event, client);
  145. event_add(&client->ev_ssh, NULL);
  146. client->has_init_conn_fd = true;
  147. }
  148. static void on_ssh_client_event(struct tmate_ssh_client *client)
  149. {
  150. char *identity;
  151. ssh_key pubkey;
  152. int key_type;
  153. unsigned char *hash;
  154. ssize_t hash_len;
  155. char *hash_str;
  156. const char *server_hash_str;
  157. int match;
  158. int verbosity = SSH_LOG_NOLOG + log_get_level();
  159. int port = options_get_number(global_options, "tmate-server-port");
  160. ssh_session session = client->session;
  161. ssh_channel channel = client->channel;
  162. switch (client->state) {
  163. case SSH_INIT:
  164. client->session = session = ssh_new();
  165. if (!session) {
  166. tmate_fatal("cannot initialize");
  167. return;
  168. }
  169. ssh_set_callbacks(session, &client->ssh_callbacks);
  170. client->channel = channel = ssh_channel_new(session);
  171. if (!channel) {
  172. tmate_fatal("cannot initialize");
  173. return;
  174. }
  175. ssh_set_blocking(session, 0);
  176. ssh_options_set(session, SSH_OPTIONS_HOST, client->server_ip);
  177. ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
  178. ssh_options_set(session, SSH_OPTIONS_PORT, &port);
  179. ssh_options_set(session, SSH_OPTIONS_USER, "tmate");
  180. ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
  181. if ((identity = get_identity())) {
  182. /*
  183. * FIXME libssh will continue with the next set of
  184. * keys if the identity has a passphrase and the
  185. * regular one doesn't.
  186. */
  187. ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity);
  188. free(identity);
  189. }
  190. client->state = SSH_CONNECT;
  191. /* fall through */
  192. case SSH_CONNECT:
  193. switch (ssh_connect(session)) {
  194. case SSH_AGAIN:
  195. init_conn_fd(client);
  196. return;
  197. case SSH_ERROR:
  198. kill_ssh_client(client, "Error connecting: %s",
  199. ssh_get_error(session));
  200. return;
  201. case SSH_OK:
  202. init_conn_fd(client);
  203. tmate_debug("Establishing connection to %s", client->server_ip);
  204. client->state = SSH_AUTH_SERVER;
  205. /* fall through */
  206. }
  207. case SSH_AUTH_SERVER:
  208. if (ssh_get_publickey(session, &pubkey) < 0)
  209. tmate_fatal("ssh_get_publickey");
  210. if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hash_len) < 0) {
  211. kill_ssh_client(client, "Cannot authenticate server");
  212. return;
  213. }
  214. hash_str = ssh_get_hexa(hash, hash_len);
  215. if (!hash_str)
  216. tmate_fatal("malloc failed");
  217. key_type = ssh_key_type(pubkey);
  218. switch (key_type) {
  219. case SSH_KEYTYPE_RSA:
  220. server_hash_str = options_get_string(global_options,
  221. "tmate-server-rsa-fingerprint");
  222. break;
  223. case SSH_KEYTYPE_ECDSA:
  224. server_hash_str = options_get_string(global_options,
  225. "tmate-server-ecdsa-fingerprint");
  226. break;
  227. default:
  228. server_hash_str = "";
  229. }
  230. match = !strcmp(hash_str, server_hash_str);
  231. ssh_key_free(pubkey);
  232. ssh_clean_pubkey_hash(&hash);
  233. free(hash_str);
  234. if (!match) {
  235. kill_ssh_client(client, "Cannot authenticate server");
  236. return;
  237. }
  238. /*
  239. * At this point, we abort other connection attempts to the
  240. * other tmate servers, since we have reached the fastest one.
  241. * We need to do it before we ask the user its passphrase,
  242. * otherwise the speed test would be biased.
  243. */
  244. tmate_debug("Connected to %s", client->server_ip);
  245. on_ssh_auth_server_complete(client);
  246. client->state = SSH_AUTH_CLIENT;
  247. /* fall through */
  248. case SSH_AUTH_CLIENT:
  249. client->tried_passphrase = client->tmate_session->passphrase;
  250. switch (ssh_userauth_autopubkey(session, client->tried_passphrase)) {
  251. case SSH_AUTH_AGAIN:
  252. return;
  253. case SSH_AUTH_PARTIAL:
  254. case SSH_AUTH_INFO:
  255. case SSH_AUTH_DENIED:
  256. if (client->tmate_session->need_passphrase) {
  257. request_passphrase(client);
  258. } else {
  259. kill_ssh_client(client, "SSH keys not found."
  260. " Run 'ssh-keygen' to create keys and try again.");
  261. return;
  262. }
  263. if (client->tried_passphrase)
  264. tmate_status_message("Can't load SSH key."
  265. " Try typing passphrase again in case of typo. ctrl-c to abort.");
  266. return;
  267. case SSH_AUTH_ERROR:
  268. kill_ssh_client(client, "Auth error: %s", ssh_get_error(session));
  269. return;
  270. case SSH_AUTH_SUCCESS:
  271. tmate_debug("Auth successful");
  272. client->state = SSH_OPEN_CHANNEL;
  273. /* fall through */
  274. }
  275. case SSH_OPEN_CHANNEL:
  276. switch (ssh_channel_open_session(channel)) {
  277. case SSH_AGAIN:
  278. return;
  279. case SSH_ERROR:
  280. kill_ssh_client(client, "Error opening channel: %s",
  281. ssh_get_error(session));
  282. return;
  283. case SSH_OK:
  284. tmate_debug("Session opened, initalizing tmate");
  285. client->state = SSH_BOOTSTRAP;
  286. /* fall through */
  287. }
  288. case SSH_BOOTSTRAP:
  289. switch (ssh_channel_request_subsystem(channel, "tmate")) {
  290. case SSH_AGAIN:
  291. return;
  292. case SSH_ERROR:
  293. kill_ssh_client(client, "Error initializing tmate: %s",
  294. ssh_get_error(session));
  295. return;
  296. case SSH_OK:
  297. tmate_debug("Ready");
  298. /* Writes are now performed in a blocking fashion */
  299. ssh_set_blocking(session, 1);
  300. client->state = SSH_READY;
  301. if (client->tmate_session->reconnected)
  302. tmate_send_reconnection_state(client->tmate_session);
  303. tmate_encoder_set_ready_callback(&client->tmate_session->encoder,
  304. on_encoder_write, client);
  305. tmate_decoder_init(&client->tmate_session->decoder,
  306. on_decoder_read, client);
  307. free(client->tmate_session->last_server_ip);
  308. client->tmate_session->last_server_ip = xstrdup(client->server_ip);
  309. /* fall through */
  310. }
  311. case SSH_READY:
  312. read_channel(client);
  313. }
  314. }
  315. static void __on_ssh_client_event(__unused evutil_socket_t fd, __unused short what, void *arg)
  316. {
  317. on_ssh_client_event(arg);
  318. }
  319. static void kill_ssh_client(struct tmate_ssh_client *client,
  320. const char *fmt, ...)
  321. {
  322. bool last_client;
  323. va_list ap;
  324. char *message = NULL;
  325. TAILQ_REMOVE(&client->tmate_session->clients, client, node);
  326. last_client = TAILQ_EMPTY(&client->tmate_session->clients);
  327. if (fmt && last_client) {
  328. va_start(ap, fmt);
  329. xvasprintf(&message, fmt, ap);
  330. va_end(ap);
  331. tmate_status_message("%s", message);
  332. }
  333. tmate_debug("SSH client killed (%s)", client->server_ip);
  334. if (client->has_init_conn_fd) {
  335. event_del(&client->ev_ssh);
  336. client->has_init_conn_fd = false;
  337. }
  338. if (client->state == SSH_READY) {
  339. tmate_encoder_set_ready_callback(&client->tmate_session->encoder, NULL, NULL);
  340. tmate_decoder_destroy(&client->tmate_session->decoder);
  341. client->tmate_session->min_sx = -1;
  342. client->tmate_session->min_sy = -1;
  343. recalculate_sizes();
  344. }
  345. if (client->session) {
  346. /* ssh_free() also frees the associated channels. */
  347. ssh_free(client->session);
  348. client->session = NULL;
  349. client->channel = NULL;
  350. }
  351. if (last_client)
  352. tmate_reconnect_session(client->tmate_session, message);
  353. free(client->server_ip);
  354. free(client);
  355. }
  356. static void connect_ssh_client(struct tmate_ssh_client *client)
  357. {
  358. if (!client->session) {
  359. client->state = SSH_INIT;
  360. on_ssh_client_event(client);
  361. }
  362. }
  363. static void ssh_log_function(int priority, const char *function,
  364. const char *buffer, __unused void *userdata)
  365. {
  366. tmate_debug("[%d] [%s] %s", priority, function, buffer);
  367. }
  368. struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session,
  369. const char *server_ip)
  370. {
  371. struct tmate_ssh_client *client;
  372. client = xmalloc(sizeof(*client));
  373. ssh_set_log_callback(ssh_log_function);
  374. memset(&client->ssh_callbacks, 0, sizeof(client->ssh_callbacks));
  375. ssh_callbacks_init(&client->ssh_callbacks);
  376. client->ssh_callbacks.userdata = client;
  377. client->ssh_callbacks.auth_function = passphrase_callback;
  378. client->tmate_session = session;
  379. TAILQ_INSERT_TAIL(&session->clients, client, node);
  380. client->server_ip = xstrdup(server_ip);
  381. client->state = SSH_NONE;
  382. client->session = NULL;
  383. client->channel = NULL;
  384. client->has_encoder = 0;
  385. client->has_init_conn_fd = false;
  386. connect_ssh_client(client);
  387. return client;
  388. }