diff options
author | Damien Miller <djm@mindrot.org> | 2004-06-15 10:34:08 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2004-06-15 10:34:08 +1000 |
commit | 0e220dbfbcc9fe252e8f1f4890dbfa415aad35db (patch) | |
tree | b78bab0c628cd5bdb0ec95340f533a1be2fae75f /clientloop.c | |
parent | 05202ffe214115afa24bf6e7a6d8c8457e6759bb (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.c | 327 |
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" |
62 | RCSID("$OpenBSD: clientloop.c,v 1.122 2004/05/22 06:32:12 djm Exp $"); | 62 | RCSID("$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 */ |
86 | extern Options options; | 89 | extern 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 */ |
92 | extern int no_shell_flag; | 95 | extern int no_shell_flag; |
93 | 96 | ||
97 | /* Control socket */ | ||
98 | extern 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; | |||
131 | static void client_init_dispatch(void); | 137 | static void client_init_dispatch(void); |
132 | int session_ident = -1; | 138 | int session_ident = -1; |
133 | 139 | ||
140 | struct 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*/ |
135 | extern Kex *xxx_kex; | 149 | extern Kex *xxx_kex; |
136 | 150 | ||
151 | void ssh_process_session2_setup(int, int, int, Buffer *); | ||
152 | |||
137 | /* Restores stdin to blocking mode. */ | 153 | /* Restores stdin to blocking mode. */ |
138 | 154 | ||
139 | static void | 155 | static 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 | |||
339 | static void | 348 | static void |
340 | client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, | 349 | client_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 | ||
502 | static void | 514 | static void |
515 | client_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 | |||
536 | static void | ||
537 | client_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 | |||
557 | static void | ||
558 | client_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 | |||
684 | static void | ||
503 | process_cmdline(void) | 685 | process_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) | |||
901 | static void | 1083 | static void |
902 | client_channel_closed(int id, void *arg) | 1084 | client_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 | |||
1385 | client_input_channel_req(int type, u_int32_t seq, void *ctxt) | 1569 | client_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 | ||
1627 | void | ||
1628 | client_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 | |||
1440 | static void | 1719 | static void |
1441 | client_init_dispatch_20(void) | 1720 | client_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 | } |