summaryrefslogtreecommitdiff
path: root/clientloop.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2004-06-15 10:34:08 +1000
committerDamien Miller <djm@mindrot.org>2004-06-15 10:34:08 +1000
commit0e220dbfbcc9fe252e8f1f4890dbfa415aad35db (patch)
treeb78bab0c628cd5bdb0ec95340f533a1be2fae75f /clientloop.c
parent05202ffe214115afa24bf6e7a6d8c8457e6759bb (diff)
- djm@cvs.openbsd.org 2004/06/13 15:03:02
[channels.c channels.h clientloop.c clientloop.h includes.h readconf.c] [readconf.h scp.1 sftp.1 ssh.1 ssh.c ssh_config.5] implement session multiplexing in the client (the server has supported this since 2.0); ok markus@
Diffstat (limited to 'clientloop.c')
-rw-r--r--clientloop.c327
1 files changed, 304 insertions, 23 deletions
diff --git a/clientloop.c b/clientloop.c
index 31e604180..6401588a9 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -59,7 +59,7 @@
59 */ 59 */
60 60
61#include "includes.h" 61#include "includes.h"
62RCSID("$OpenBSD: clientloop.c,v 1.122 2004/05/22 06:32:12 djm Exp $"); 62RCSID("$OpenBSD: clientloop.c,v 1.123 2004/06/13 15:03:02 djm Exp $");
63 63
64#include "ssh.h" 64#include "ssh.h"
65#include "ssh1.h" 65#include "ssh1.h"
@@ -81,6 +81,9 @@ RCSID("$OpenBSD: clientloop.c,v 1.122 2004/05/22 06:32:12 djm Exp $");
81#include "atomicio.h" 81#include "atomicio.h"
82#include "sshpty.h" 82#include "sshpty.h"
83#include "misc.h" 83#include "misc.h"
84#include "monitor_fdpass.h"
85#include "match.h"
86#include "msg.h"
84 87
85/* import options */ 88/* import options */
86extern Options options; 89extern Options options;
@@ -91,6 +94,9 @@ extern int stdin_null_flag;
91/* Flag indicating that no shell has been requested */ 94/* Flag indicating that no shell has been requested */
92extern int no_shell_flag; 95extern int no_shell_flag;
93 96
97/* Control socket */
98extern int control_fd;
99
94/* 100/*
95 * Name of the host we are connecting to. This is the name given on the 101 * Name of the host we are connecting to. This is the name given on the
96 * command line, or the HostName specified for the user-supplied name in a 102 * command line, or the HostName specified for the user-supplied name in a
@@ -131,9 +137,19 @@ static int server_alive_timeouts = 0;
131static void client_init_dispatch(void); 137static void client_init_dispatch(void);
132int session_ident = -1; 138int session_ident = -1;
133 139
140struct confirm_ctx {
141 int want_tty;
142 int want_subsys;
143 Buffer cmd;
144 char *term;
145 struct termios tio;
146};
147
134/*XXX*/ 148/*XXX*/
135extern Kex *xxx_kex; 149extern Kex *xxx_kex;
136 150
151void ssh_process_session2_setup(int, int, int, Buffer *);
152
137/* Restores stdin to blocking mode. */ 153/* Restores stdin to blocking mode. */
138 154
139static void 155static void
@@ -291,19 +307,13 @@ client_check_window_change(void)
291 /** XXX race */ 307 /** XXX race */
292 received_window_change_signal = 0; 308 received_window_change_signal = 0;
293 309
294 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
295 return;
296
297 debug2("client_check_window_change: changed"); 310 debug2("client_check_window_change: changed");
298 311
299 if (compat20) { 312 if (compat20) {
300 channel_request_start(session_ident, "window-change", 0); 313 channel_send_window_changes();
301 packet_put_int(ws.ws_col);
302 packet_put_int(ws.ws_row);
303 packet_put_int(ws.ws_xpixel);
304 packet_put_int(ws.ws_ypixel);
305 packet_send();
306 } else { 314 } else {
315 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
316 return;
307 packet_start(SSH_CMSG_WINDOW_SIZE); 317 packet_start(SSH_CMSG_WINDOW_SIZE);
308 packet_put_int(ws.ws_row); 318 packet_put_int(ws.ws_row);
309 packet_put_int(ws.ws_col); 319 packet_put_int(ws.ws_col);
@@ -335,7 +345,6 @@ server_alive_check(void)
335 * Waits until the client can do something (some data becomes available on 345 * Waits until the client can do something (some data becomes available on
336 * one of the file descriptors). 346 * one of the file descriptors).
337 */ 347 */
338
339static void 348static void
340client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 349client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
341 int *maxfdp, int *nallocp, int rekeying) 350 int *maxfdp, int *nallocp, int rekeying)
@@ -381,6 +390,9 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
381 if (packet_have_data_to_write()) 390 if (packet_have_data_to_write())
382 FD_SET(connection_out, *writesetp); 391 FD_SET(connection_out, *writesetp);
383 392
393 if (control_fd != -1)
394 FD_SET(control_fd, *readsetp);
395
384 /* 396 /*
385 * Wait for something to happen. This will suspend the process until 397 * Wait for something to happen. This will suspend the process until
386 * some selected descriptor can be read, written, or has some other 398 * some selected descriptor can be read, written, or has some other
@@ -500,6 +512,176 @@ client_process_net_input(fd_set * readset)
500} 512}
501 513
502static void 514static void
515client_subsystem_reply(int type, u_int32_t seq, void *ctxt)
516{
517 int id;
518 Channel *c;
519
520 id = packet_get_int();
521 packet_check_eom();
522
523 if ((c = channel_lookup(id)) == NULL) {
524 error("%s: no channel for id %d", __func__, id);
525 return;
526 }
527
528 if (type == SSH2_MSG_CHANNEL_SUCCESS)
529 debug2("Request suceeded on channel %d", id);
530 else if (type == SSH2_MSG_CHANNEL_FAILURE) {
531 error("Request failed on channel %d", id);
532 channel_free(c);
533 }
534}
535
536static void
537client_extra_session2_setup(int id, void *arg)
538{
539 struct confirm_ctx *cctx = arg;
540 Channel *c;
541
542 if (cctx == NULL)
543 fatal("%s: cctx == NULL", __func__);
544 if ((c = channel_lookup(id)) == NULL)
545 fatal("%s: no channel for id %d", __func__, id);
546
547 client_session2_setup(id, cctx->want_tty, cctx->want_subsys,
548 cctx->term, &cctx->tio, c->rfd, &cctx->cmd,
549 client_subsystem_reply);
550
551 c->confirm_ctx = NULL;
552 buffer_free(&cctx->cmd);
553 free(cctx->term);
554 free(cctx);
555}
556
557static void
558client_process_control(fd_set * readset)
559{
560 Buffer m;
561 Channel *c;
562 int client_fd, new_fd[3], ver;
563 socklen_t addrlen;
564 struct sockaddr_storage addr;
565 struct confirm_ctx *cctx;
566 char *cmd;
567 u_int len;
568 uid_t euid;
569 gid_t egid;
570
571 /*
572 * Accept connection on control socket
573 */
574 if (control_fd == -1 || !FD_ISSET(control_fd, readset))
575 return;
576
577 memset(&addr, 0, sizeof(addr));
578 addrlen = sizeof(addr);
579 if ((client_fd = accept(control_fd,
580 (struct sockaddr*)&addr, &addrlen)) == -1) {
581 error("%s accept: %s", __func__, strerror(errno));
582 return;
583 }
584
585 if (getpeereid(client_fd, &euid, &egid) < 0) {
586 error("%s getpeereid failed: %s", __func__, strerror(errno));
587 close(client_fd);
588 return;
589 }
590 if ((euid != 0) && (getuid() != euid)) {
591 error("control mode uid mismatch: peer euid %u != uid %u",
592 (u_int) euid, (u_int) getuid());
593 close(client_fd);
594 return;
595 }
596 /* XXX: implement use of ssh-askpass to confirm additional channels */
597
598 unset_nonblock(client_fd);
599
600 buffer_init(&m);
601
602 buffer_put_int(&m, getpid());
603 if (ssh_msg_send(client_fd, /* version */0, &m) == -1) {
604 error("%s: client msg_send failed", __func__);
605 close(client_fd);
606 return;
607 }
608 buffer_clear(&m);
609
610 if (ssh_msg_recv(client_fd, &m) == -1) {
611 error("%s: client msg_recv failed", __func__);
612 close(client_fd);
613 return;
614 }
615
616 if ((ver = buffer_get_char(&m)) != 0) {
617 error("%s: wrong client version %d", __func__, ver);
618 buffer_free(&m);
619 close(client_fd);
620 return;
621 }
622
623 cctx = xmalloc(sizeof(*cctx));
624 memset(cctx, 0, sizeof(*cctx));
625
626 cctx->want_tty = buffer_get_int(&m);
627 cctx->want_subsys = buffer_get_int(&m);
628 cctx->term = buffer_get_string(&m, &len);
629
630 cmd = buffer_get_string(&m, &len);
631 buffer_init(&cctx->cmd);
632 buffer_append(&cctx->cmd, cmd, strlen(cmd));
633
634 debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__,
635 cctx->want_tty, cctx->want_subsys, cmd);
636
637 /* Gather fds from client */
638 new_fd[0] = mm_receive_fd(client_fd);
639 new_fd[1] = mm_receive_fd(client_fd);
640 new_fd[2] = mm_receive_fd(client_fd);
641
642 debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__,
643 new_fd[0], new_fd[1], new_fd[2]);
644
645 /* Try to pick up ttymodes from client before it goes raw */
646 if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
647 error("%s: tcgetattr: %s", __func__, strerror(errno));
648
649 buffer_clear(&m);
650 if (ssh_msg_send(client_fd, /* version */0, &m) == -1) {
651 error("%s: client msg_send failed", __func__);
652 close(client_fd);
653 close(new_fd[0]);
654 close(new_fd[1]);
655 close(new_fd[2]);
656 return;
657 }
658 buffer_free(&m);
659
660 /* enable nonblocking unless tty */
661 if (!isatty(new_fd[0]))
662 set_nonblock(new_fd[0]);
663 if (!isatty(new_fd[1]))
664 set_nonblock(new_fd[1]);
665 if (!isatty(new_fd[2]))
666 set_nonblock(new_fd[2]);
667
668 set_nonblock(client_fd);
669
670 c = channel_new("session", SSH_CHANNEL_OPENING,
671 new_fd[0], new_fd[1], new_fd[2],
672 CHAN_SES_WINDOW_DEFAULT, CHAN_SES_PACKET_DEFAULT,
673 CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
674
675 /* XXX */
676 c->ctl_fd = client_fd;
677
678 debug3("%s: channel_new: %d", __func__, c->self);
679
680 channel_send_open(c->self);
681 channel_register_confirm(c->self, client_extra_session2_setup, cctx);
682}
683
684static void
503process_cmdline(void) 685process_cmdline(void)
504{ 686{
505 void (*handler)(int); 687 void (*handler)(int);
@@ -901,9 +1083,6 @@ simple_escape_filter(Channel *c, char *buf, int len)
901static void 1083static void
902client_channel_closed(int id, void *arg) 1084client_channel_closed(int id, void *arg)
903{ 1085{
904 if (id != session_ident)
905 error("client_channel_closed: id %d != session_ident %d",
906 id, session_ident);
907 channel_cancel_cleanup(id); 1086 channel_cancel_cleanup(id);
908 session_closed = 1; 1087 session_closed = 1;
909 leave_raw_mode(); 1088 leave_raw_mode();
@@ -937,6 +1116,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
937 connection_in = packet_get_connection_in(); 1116 connection_in = packet_get_connection_in();
938 connection_out = packet_get_connection_out(); 1117 connection_out = packet_get_connection_out();
939 max_fd = MAX(connection_in, connection_out); 1118 max_fd = MAX(connection_in, connection_out);
1119 if (control_fd != -1)
1120 max_fd = MAX(max_fd, control_fd);
940 1121
941 if (!compat20) { 1122 if (!compat20) {
942 /* enable nonblocking unless tty */ 1123 /* enable nonblocking unless tty */
@@ -1054,6 +1235,9 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1054 /* Buffer input from the connection. */ 1235 /* Buffer input from the connection. */
1055 client_process_net_input(readset); 1236 client_process_net_input(readset);
1056 1237
1238 /* Accept control connections. */
1239 client_process_control(readset);
1240
1057 if (quit_pending) 1241 if (quit_pending)
1058 break; 1242 break;
1059 1243
@@ -1385,7 +1569,7 @@ static void
1385client_input_channel_req(int type, u_int32_t seq, void *ctxt) 1569client_input_channel_req(int type, u_int32_t seq, void *ctxt)
1386{ 1570{
1387 Channel *c = NULL; 1571 Channel *c = NULL;
1388 int id, reply, success = 0; 1572 int exitval, id, reply, success = 0;
1389 char *rtype; 1573 char *rtype;
1390 1574
1391 id = packet_get_int(); 1575 id = packet_get_int();
@@ -1395,18 +1579,21 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt)
1395 debug("client_input_channel_req: channel %d rtype %s reply %d", 1579 debug("client_input_channel_req: channel %d rtype %s reply %d",
1396 id, rtype, reply); 1580 id, rtype, reply);
1397 1581
1398 if (session_ident == -1) {
1399 error("client_input_channel_req: no channel %d", session_ident);
1400 } else if (id != session_ident) {
1401 error("client_input_channel_req: channel %d: wrong channel: %d",
1402 session_ident, id);
1403 }
1404 c = channel_lookup(id); 1582 c = channel_lookup(id);
1405 if (c == NULL) { 1583 if (c == NULL) {
1406 error("client_input_channel_req: channel %d: unknown channel", id); 1584 error("client_input_channel_req: channel %d: unknown channel", id);
1407 } else if (strcmp(rtype, "exit-status") == 0) { 1585 } else if (strcmp(rtype, "exit-status") == 0) {
1408 success = 1; 1586 exitval = packet_get_int();
1409 exit_status = packet_get_int(); 1587 if (id == session_ident) {
1588 success = 1;
1589 exit_status = exitval;
1590 } else if (c->ctl_fd == -1) {
1591 error("client_input_channel_req: unexpected channel %d",
1592 session_ident);
1593 } else {
1594 atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval));
1595 success = 1;
1596 }
1410 packet_check_eom(); 1597 packet_check_eom();
1411 } 1598 }
1412 if (reply) { 1599 if (reply) {
@@ -1437,6 +1624,98 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt)
1437 xfree(rtype); 1624 xfree(rtype);
1438} 1625}
1439 1626
1627void
1628client_session2_setup(int id, int want_tty, int want_subsystem,
1629 const char *term, struct termios *tiop, int in_fd, Buffer *cmd,
1630 dispatch_fn *subsys_repl)
1631{
1632 int len;
1633
1634 debug2("%s: id %d", __func__, id);
1635
1636 if (want_tty) {
1637 struct winsize ws;
1638 struct termios tio;
1639
1640 /* Store window size in the packet. */
1641 if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0)
1642 memset(&ws, 0, sizeof(ws));
1643
1644 channel_request_start(id, "pty-req", 0);
1645 packet_put_cstring(term != NULL ? term : "");
1646 packet_put_int(ws.ws_col);
1647 packet_put_int(ws.ws_row);
1648 packet_put_int(ws.ws_xpixel);
1649 packet_put_int(ws.ws_ypixel);
1650 tio = get_saved_tio();
1651 tty_make_modes(-1, tiop != NULL ? tiop : &tio);
1652 packet_send();
1653 /* XXX wait for reply */
1654 }
1655
1656 /* Transfer any environment variables from client to server */
1657 if (options.num_send_env != 0) {
1658 int i, j, matched;
1659 extern char **environ;
1660 char *name, *val;
1661
1662 debug("Sending environment.");
1663 for (i = 0; environ && environ[i] != NULL; i++) {
1664 /* Split */
1665 name = xstrdup(environ[i]);
1666 if ((val = strchr(name, '=')) == NULL) {
1667 free(name);
1668 continue;
1669 }
1670 *val++ = '\0';
1671
1672 matched = 0;
1673 for (j = 0; j < options.num_send_env; j++) {
1674 if (match_pattern(name, options.send_env[j])) {
1675 matched = 1;
1676 break;
1677 }
1678 }
1679 if (!matched) {
1680 debug3("Ignored env %s", name);
1681 free(name);
1682 continue;
1683 }
1684
1685 debug("Sending env %s = %s", name, val);
1686 channel_request_start(id, "env", 0);
1687 packet_put_cstring(name);
1688 packet_put_cstring(val);
1689 packet_send();
1690 free(name);
1691 }
1692 }
1693
1694 len = buffer_len(cmd);
1695 if (len > 0) {
1696 if (len > 900)
1697 len = 900;
1698 if (want_subsystem) {
1699 debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd));
1700 channel_request_start(id, "subsystem", subsys_repl != NULL);
1701 if (subsys_repl != NULL) {
1702 /* register callback for reply */
1703 /* XXX we assume that client_loop has already been called */
1704 dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl);
1705 dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl);
1706 }
1707 } else {
1708 debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd));
1709 channel_request_start(id, "exec", 0);
1710 }
1711 packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
1712 packet_send();
1713 } else {
1714 channel_request_start(id, "shell", 0);
1715 packet_send();
1716 }
1717}
1718
1440static void 1719static void
1441client_init_dispatch_20(void) 1720client_init_dispatch_20(void)
1442{ 1721{
@@ -1503,5 +1782,7 @@ cleanup_exit(int i)
1503{ 1782{
1504 leave_raw_mode(); 1783 leave_raw_mode();
1505 leave_non_blocking(); 1784 leave_non_blocking();
1785 if (options.control_path != NULL && control_fd != -1)
1786 unlink(options.control_path);
1506 _exit(i); 1787 _exit(i);
1507} 1788}