diff options
Diffstat (limited to 'clientloop.c')
-rw-r--r-- | clientloop.c | 478 |
1 files changed, 420 insertions, 58 deletions
diff --git a/clientloop.c b/clientloop.c index a13d06ddf..1bcf4392f 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.117 2003/12/16 15:49:51 markus Exp $"); | 62 | RCSID("$OpenBSD: clientloop.c,v 1.130 2004/08/11 21:43:04 avsm Exp $"); |
63 | 63 | ||
64 | #include "ssh.h" | 64 | #include "ssh.h" |
65 | #include "ssh1.h" | 65 | #include "ssh1.h" |
@@ -79,9 +79,11 @@ RCSID("$OpenBSD: clientloop.c,v 1.117 2003/12/16 15:49:51 markus Exp $"); | |||
79 | #include "clientloop.h" | 79 | #include "clientloop.h" |
80 | #include "authfd.h" | 80 | #include "authfd.h" |
81 | #include "atomicio.h" | 81 | #include "atomicio.h" |
82 | #include "sshtty.h" | 82 | #include "sshpty.h" |
83 | #include "misc.h" | 83 | #include "misc.h" |
84 | #include "readpass.h" | 84 | #include "monitor_fdpass.h" |
85 | #include "match.h" | ||
86 | #include "msg.h" | ||
85 | 87 | ||
86 | /* import options */ | 88 | /* import options */ |
87 | extern Options options; | 89 | extern Options options; |
@@ -92,6 +94,9 @@ extern int stdin_null_flag; | |||
92 | /* Flag indicating that no shell has been requested */ | 94 | /* Flag indicating that no shell has been requested */ |
93 | extern int no_shell_flag; | 95 | extern int no_shell_flag; |
94 | 96 | ||
97 | /* Control socket */ | ||
98 | extern int control_fd; | ||
99 | |||
95 | /* | 100 | /* |
96 | * 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 |
97 | * 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 |
@@ -132,16 +137,27 @@ static int server_alive_timeouts = 0; | |||
132 | static void client_init_dispatch(void); | 137 | static void client_init_dispatch(void); |
133 | int session_ident = -1; | 138 | int session_ident = -1; |
134 | 139 | ||
140 | struct confirm_ctx { | ||
141 | int want_tty; | ||
142 | int want_subsys; | ||
143 | Buffer cmd; | ||
144 | char *term; | ||
145 | struct termios tio; | ||
146 | char **env; | ||
147 | }; | ||
148 | |||
135 | /*XXX*/ | 149 | /*XXX*/ |
136 | extern Kex *xxx_kex; | 150 | extern Kex *xxx_kex; |
137 | 151 | ||
152 | void ssh_process_session2_setup(int, int, int, Buffer *); | ||
153 | |||
138 | /* Restores stdin to blocking mode. */ | 154 | /* Restores stdin to blocking mode. */ |
139 | 155 | ||
140 | static void | 156 | static void |
141 | leave_non_blocking(void) | 157 | leave_non_blocking(void) |
142 | { | 158 | { |
143 | if (in_non_blocking_mode) { | 159 | if (in_non_blocking_mode) { |
144 | (void) fcntl(fileno(stdin), F_SETFL, 0); | 160 | unset_nonblock(fileno(stdin)); |
145 | in_non_blocking_mode = 0; | 161 | in_non_blocking_mode = 0; |
146 | } | 162 | } |
147 | } | 163 | } |
@@ -152,7 +168,7 @@ static void | |||
152 | enter_non_blocking(void) | 168 | enter_non_blocking(void) |
153 | { | 169 | { |
154 | in_non_blocking_mode = 1; | 170 | in_non_blocking_mode = 1; |
155 | (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); | 171 | set_nonblock(fileno(stdin)); |
156 | } | 172 | } |
157 | 173 | ||
158 | /* | 174 | /* |
@@ -292,19 +308,13 @@ client_check_window_change(void) | |||
292 | /** XXX race */ | 308 | /** XXX race */ |
293 | received_window_change_signal = 0; | 309 | received_window_change_signal = 0; |
294 | 310 | ||
295 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) | ||
296 | return; | ||
297 | |||
298 | debug2("client_check_window_change: changed"); | 311 | debug2("client_check_window_change: changed"); |
299 | 312 | ||
300 | if (compat20) { | 313 | if (compat20) { |
301 | channel_request_start(session_ident, "window-change", 0); | 314 | channel_send_window_changes(); |
302 | packet_put_int(ws.ws_col); | ||
303 | packet_put_int(ws.ws_row); | ||
304 | packet_put_int(ws.ws_xpixel); | ||
305 | packet_put_int(ws.ws_ypixel); | ||
306 | packet_send(); | ||
307 | } else { | 315 | } else { |
316 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) | ||
317 | return; | ||
308 | packet_start(SSH_CMSG_WINDOW_SIZE); | 318 | packet_start(SSH_CMSG_WINDOW_SIZE); |
309 | packet_put_int(ws.ws_row); | 319 | packet_put_int(ws.ws_row); |
310 | packet_put_int(ws.ws_col); | 320 | packet_put_int(ws.ws_col); |
@@ -341,10 +351,9 @@ server_alive_check(void) | |||
341 | * Waits until the client can do something (some data becomes available on | 351 | * Waits until the client can do something (some data becomes available on |
342 | * one of the file descriptors). | 352 | * one of the file descriptors). |
343 | */ | 353 | */ |
344 | |||
345 | static void | 354 | static void |
346 | client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, | 355 | client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, |
347 | int *maxfdp, int *nallocp, int rekeying) | 356 | int *maxfdp, u_int *nallocp, int rekeying) |
348 | { | 357 | { |
349 | struct timeval tv, *tvp; | 358 | struct timeval tv, *tvp; |
350 | int ret; | 359 | int ret; |
@@ -387,6 +396,9 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, | |||
387 | if (packet_have_data_to_write()) | 396 | if (packet_have_data_to_write()) |
388 | FD_SET(connection_out, *writesetp); | 397 | FD_SET(connection_out, *writesetp); |
389 | 398 | ||
399 | if (control_fd != -1) | ||
400 | FD_SET(control_fd, *readsetp); | ||
401 | |||
390 | /* | 402 | /* |
391 | * Wait for something to happen. This will suspend the process until | 403 | * Wait for something to happen. This will suspend the process until |
392 | * some selected descriptor can be read, written, or has some other | 404 | * some selected descriptor can be read, written, or has some other |
@@ -395,7 +407,7 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, | |||
395 | 407 | ||
396 | if (options.server_alive_interval == 0) | 408 | if (options.server_alive_interval == 0) |
397 | tvp = NULL; | 409 | tvp = NULL; |
398 | else { | 410 | else { |
399 | tv.tv_sec = options.server_alive_interval; | 411 | tv.tv_sec = options.server_alive_interval; |
400 | tv.tv_usec = 0; | 412 | tv.tv_usec = 0; |
401 | tvp = &tv; | 413 | tvp = &tv; |
@@ -506,12 +518,229 @@ client_process_net_input(fd_set * readset) | |||
506 | } | 518 | } |
507 | 519 | ||
508 | static void | 520 | static void |
521 | client_subsystem_reply(int type, u_int32_t seq, void *ctxt) | ||
522 | { | ||
523 | int id; | ||
524 | Channel *c; | ||
525 | |||
526 | id = packet_get_int(); | ||
527 | packet_check_eom(); | ||
528 | |||
529 | if ((c = channel_lookup(id)) == NULL) { | ||
530 | error("%s: no channel for id %d", __func__, id); | ||
531 | return; | ||
532 | } | ||
533 | |||
534 | if (type == SSH2_MSG_CHANNEL_SUCCESS) | ||
535 | debug2("Request suceeded on channel %d", id); | ||
536 | else if (type == SSH2_MSG_CHANNEL_FAILURE) { | ||
537 | error("Request failed on channel %d", id); | ||
538 | channel_free(c); | ||
539 | } | ||
540 | } | ||
541 | |||
542 | static void | ||
543 | client_extra_session2_setup(int id, void *arg) | ||
544 | { | ||
545 | struct confirm_ctx *cctx = arg; | ||
546 | Channel *c; | ||
547 | int i; | ||
548 | |||
549 | if (cctx == NULL) | ||
550 | fatal("%s: cctx == NULL", __func__); | ||
551 | if ((c = channel_lookup(id)) == NULL) | ||
552 | fatal("%s: no channel for id %d", __func__, id); | ||
553 | |||
554 | client_session2_setup(id, cctx->want_tty, cctx->want_subsys, | ||
555 | cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env, | ||
556 | client_subsystem_reply); | ||
557 | |||
558 | c->confirm_ctx = NULL; | ||
559 | buffer_free(&cctx->cmd); | ||
560 | xfree(cctx->term); | ||
561 | if (cctx->env != NULL) { | ||
562 | for (i = 0; cctx->env[i] != NULL; i++) | ||
563 | xfree(cctx->env[i]); | ||
564 | xfree(cctx->env); | ||
565 | } | ||
566 | xfree(cctx); | ||
567 | } | ||
568 | |||
569 | static void | ||
570 | client_process_control(fd_set * readset) | ||
571 | { | ||
572 | Buffer m; | ||
573 | Channel *c; | ||
574 | int client_fd, new_fd[3], ver, i, allowed; | ||
575 | socklen_t addrlen; | ||
576 | struct sockaddr_storage addr; | ||
577 | struct confirm_ctx *cctx; | ||
578 | char *cmd; | ||
579 | u_int len, env_len; | ||
580 | uid_t euid; | ||
581 | gid_t egid; | ||
582 | |||
583 | /* | ||
584 | * Accept connection on control socket | ||
585 | */ | ||
586 | if (control_fd == -1 || !FD_ISSET(control_fd, readset)) | ||
587 | return; | ||
588 | |||
589 | memset(&addr, 0, sizeof(addr)); | ||
590 | addrlen = sizeof(addr); | ||
591 | if ((client_fd = accept(control_fd, | ||
592 | (struct sockaddr*)&addr, &addrlen)) == -1) { | ||
593 | error("%s accept: %s", __func__, strerror(errno)); | ||
594 | return; | ||
595 | } | ||
596 | |||
597 | if (getpeereid(client_fd, &euid, &egid) < 0) { | ||
598 | error("%s getpeereid failed: %s", __func__, strerror(errno)); | ||
599 | close(client_fd); | ||
600 | return; | ||
601 | } | ||
602 | if ((euid != 0) && (getuid() != euid)) { | ||
603 | error("control mode uid mismatch: peer euid %u != uid %u", | ||
604 | (u_int) euid, (u_int) getuid()); | ||
605 | close(client_fd); | ||
606 | return; | ||
607 | } | ||
608 | |||
609 | allowed = 1; | ||
610 | if (options.control_master == 2) { | ||
611 | char *p, prompt[1024]; | ||
612 | |||
613 | allowed = 0; | ||
614 | snprintf(prompt, sizeof(prompt), | ||
615 | "Allow shared connection to %s? ", host); | ||
616 | p = read_passphrase(prompt, RP_USE_ASKPASS|RP_ALLOW_EOF); | ||
617 | if (p != NULL) { | ||
618 | /* | ||
619 | * Accept empty responses and responses consisting | ||
620 | * of the word "yes" as affirmative. | ||
621 | */ | ||
622 | if (*p == '\0' || *p == '\n' || | ||
623 | strcasecmp(p, "yes") == 0) | ||
624 | allowed = 1; | ||
625 | xfree(p); | ||
626 | } | ||
627 | } | ||
628 | |||
629 | unset_nonblock(client_fd); | ||
630 | |||
631 | buffer_init(&m); | ||
632 | |||
633 | buffer_put_int(&m, allowed); | ||
634 | buffer_put_int(&m, getpid()); | ||
635 | if (ssh_msg_send(client_fd, /* version */0, &m) == -1) { | ||
636 | error("%s: client msg_send failed", __func__); | ||
637 | close(client_fd); | ||
638 | buffer_free(&m); | ||
639 | return; | ||
640 | } | ||
641 | buffer_clear(&m); | ||
642 | |||
643 | if (!allowed) { | ||
644 | error("Refused control connection"); | ||
645 | close(client_fd); | ||
646 | buffer_free(&m); | ||
647 | return; | ||
648 | } | ||
649 | |||
650 | if (ssh_msg_recv(client_fd, &m) == -1) { | ||
651 | error("%s: client msg_recv failed", __func__); | ||
652 | close(client_fd); | ||
653 | buffer_free(&m); | ||
654 | return; | ||
655 | } | ||
656 | |||
657 | if ((ver = buffer_get_char(&m)) != 0) { | ||
658 | error("%s: wrong client version %d", __func__, ver); | ||
659 | buffer_free(&m); | ||
660 | close(client_fd); | ||
661 | return; | ||
662 | } | ||
663 | |||
664 | cctx = xmalloc(sizeof(*cctx)); | ||
665 | memset(cctx, 0, sizeof(*cctx)); | ||
666 | |||
667 | cctx->want_tty = buffer_get_int(&m); | ||
668 | cctx->want_subsys = buffer_get_int(&m); | ||
669 | cctx->term = buffer_get_string(&m, &len); | ||
670 | |||
671 | cmd = buffer_get_string(&m, &len); | ||
672 | buffer_init(&cctx->cmd); | ||
673 | buffer_append(&cctx->cmd, cmd, strlen(cmd)); | ||
674 | |||
675 | env_len = buffer_get_int(&m); | ||
676 | env_len = MIN(env_len, 4096); | ||
677 | debug3("%s: receiving %d env vars", __func__, env_len); | ||
678 | if (env_len != 0) { | ||
679 | cctx->env = xmalloc(sizeof(*cctx->env) * (env_len + 1)); | ||
680 | for (i = 0; i < env_len; i++) | ||
681 | cctx->env[i] = buffer_get_string(&m, &len); | ||
682 | cctx->env[i] = NULL; | ||
683 | } | ||
684 | |||
685 | debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__, | ||
686 | cctx->want_tty, cctx->want_subsys, cmd); | ||
687 | |||
688 | /* Gather fds from client */ | ||
689 | new_fd[0] = mm_receive_fd(client_fd); | ||
690 | new_fd[1] = mm_receive_fd(client_fd); | ||
691 | new_fd[2] = mm_receive_fd(client_fd); | ||
692 | |||
693 | debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__, | ||
694 | new_fd[0], new_fd[1], new_fd[2]); | ||
695 | |||
696 | /* Try to pick up ttymodes from client before it goes raw */ | ||
697 | if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) | ||
698 | error("%s: tcgetattr: %s", __func__, strerror(errno)); | ||
699 | |||
700 | buffer_clear(&m); | ||
701 | if (ssh_msg_send(client_fd, /* version */0, &m) == -1) { | ||
702 | error("%s: client msg_send failed", __func__); | ||
703 | close(client_fd); | ||
704 | close(new_fd[0]); | ||
705 | close(new_fd[1]); | ||
706 | close(new_fd[2]); | ||
707 | buffer_free(&m); | ||
708 | return; | ||
709 | } | ||
710 | buffer_free(&m); | ||
711 | |||
712 | /* enable nonblocking unless tty */ | ||
713 | if (!isatty(new_fd[0])) | ||
714 | set_nonblock(new_fd[0]); | ||
715 | if (!isatty(new_fd[1])) | ||
716 | set_nonblock(new_fd[1]); | ||
717 | if (!isatty(new_fd[2])) | ||
718 | set_nonblock(new_fd[2]); | ||
719 | |||
720 | set_nonblock(client_fd); | ||
721 | |||
722 | c = channel_new("session", SSH_CHANNEL_OPENING, | ||
723 | new_fd[0], new_fd[1], new_fd[2], | ||
724 | CHAN_SES_WINDOW_DEFAULT, CHAN_SES_PACKET_DEFAULT, | ||
725 | CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); | ||
726 | |||
727 | /* XXX */ | ||
728 | c->ctl_fd = client_fd; | ||
729 | |||
730 | debug3("%s: channel_new: %d", __func__, c->self); | ||
731 | |||
732 | channel_send_open(c->self); | ||
733 | channel_register_confirm(c->self, client_extra_session2_setup, cctx); | ||
734 | } | ||
735 | |||
736 | static void | ||
509 | process_cmdline(void) | 737 | process_cmdline(void) |
510 | { | 738 | { |
511 | void (*handler)(int); | 739 | void (*handler)(int); |
512 | char *s, *cmd; | 740 | char *s, *cmd; |
513 | u_short fwd_port, fwd_host_port; | 741 | u_short fwd_port, fwd_host_port; |
514 | char buf[1024], sfwd_port[6], sfwd_host_port[6]; | 742 | char buf[1024], sfwd_port[6], sfwd_host_port[6]; |
743 | int delete = 0; | ||
515 | int local = 0; | 744 | int local = 0; |
516 | 745 | ||
517 | leave_raw_mode(); | 746 | leave_raw_mode(); |
@@ -521,44 +750,77 @@ process_cmdline(void) | |||
521 | goto out; | 750 | goto out; |
522 | while (*s && isspace(*s)) | 751 | while (*s && isspace(*s)) |
523 | s++; | 752 | s++; |
524 | if (*s == 0) | 753 | if (*s == '-') |
754 | s++; /* Skip cmdline '-', if any */ | ||
755 | if (*s == '\0') | ||
756 | goto out; | ||
757 | |||
758 | if (*s == 'h' || *s == 'H' || *s == '?') { | ||
759 | logit("Commands:"); | ||
760 | logit(" -Lport:host:hostport Request local forward"); | ||
761 | logit(" -Rport:host:hostport Request remote forward"); | ||
762 | logit(" -KRhostport Cancel remote forward"); | ||
525 | goto out; | 763 | goto out; |
526 | if (strlen(s) < 2 || s[0] != '-' || !(s[1] == 'L' || s[1] == 'R')) { | 764 | } |
765 | |||
766 | if (*s == 'K') { | ||
767 | delete = 1; | ||
768 | s++; | ||
769 | } | ||
770 | if (*s != 'L' && *s != 'R') { | ||
527 | logit("Invalid command."); | 771 | logit("Invalid command."); |
528 | goto out; | 772 | goto out; |
529 | } | 773 | } |
530 | if (s[1] == 'L') | 774 | if (*s == 'L') |
531 | local = 1; | 775 | local = 1; |
532 | if (!local && !compat20) { | 776 | if (local && delete) { |
777 | logit("Not supported."); | ||
778 | goto out; | ||
779 | } | ||
780 | if ((!local || delete) && !compat20) { | ||
533 | logit("Not supported for SSH protocol version 1."); | 781 | logit("Not supported for SSH protocol version 1."); |
534 | goto out; | 782 | goto out; |
535 | } | 783 | } |
536 | s += 2; | 784 | |
785 | s++; | ||
537 | while (*s && isspace(*s)) | 786 | while (*s && isspace(*s)) |
538 | s++; | 787 | s++; |
539 | 788 | ||
540 | if (sscanf(s, "%5[0-9]:%255[^:]:%5[0-9]", | 789 | if (delete) { |
541 | sfwd_port, buf, sfwd_host_port) != 3 && | 790 | if (sscanf(s, "%5[0-9]", sfwd_host_port) != 1) { |
542 | sscanf(s, "%5[0-9]/%255[^/]/%5[0-9]", | 791 | logit("Bad forwarding specification."); |
543 | sfwd_port, buf, sfwd_host_port) != 3) { | 792 | goto out; |
544 | logit("Bad forwarding specification."); | 793 | } |
545 | goto out; | 794 | if ((fwd_host_port = a2port(sfwd_host_port)) == 0) { |
546 | } | 795 | logit("Bad forwarding port(s)."); |
547 | if ((fwd_port = a2port(sfwd_port)) == 0 || | 796 | goto out; |
548 | (fwd_host_port = a2port(sfwd_host_port)) == 0) { | 797 | } |
549 | logit("Bad forwarding port(s)."); | 798 | channel_request_rforward_cancel(fwd_host_port); |
550 | goto out; | 799 | } else { |
551 | } | 800 | if (sscanf(s, "%5[0-9]:%255[^:]:%5[0-9]", |
552 | if (local) { | 801 | sfwd_port, buf, sfwd_host_port) != 3 && |
553 | if (channel_setup_local_fwd_listener(fwd_port, buf, | 802 | sscanf(s, "%5[0-9]/%255[^/]/%5[0-9]", |
554 | fwd_host_port, options.gateway_ports) < 0) { | 803 | sfwd_port, buf, sfwd_host_port) != 3) { |
555 | logit("Port forwarding failed."); | 804 | logit("Bad forwarding specification."); |
805 | goto out; | ||
806 | } | ||
807 | if ((fwd_port = a2port(sfwd_port)) == 0 || | ||
808 | (fwd_host_port = a2port(sfwd_host_port)) == 0) { | ||
809 | logit("Bad forwarding port(s)."); | ||
556 | goto out; | 810 | goto out; |
557 | } | 811 | } |
558 | } else | 812 | if (local) { |
559 | channel_request_remote_forwarding(fwd_port, buf, | 813 | if (channel_setup_local_fwd_listener(fwd_port, buf, |
560 | fwd_host_port); | 814 | fwd_host_port, options.gateway_ports) < 0) { |
561 | logit("Forwarding port."); | 815 | logit("Port forwarding failed."); |
816 | goto out; | ||
817 | } | ||
818 | } else | ||
819 | channel_request_remote_forwarding(fwd_port, buf, | ||
820 | fwd_host_port); | ||
821 | logit("Forwarding port."); | ||
822 | } | ||
823 | |||
562 | out: | 824 | out: |
563 | signal(SIGINT, handler); | 825 | signal(SIGINT, handler); |
564 | enter_raw_mode(); | 826 | enter_raw_mode(); |
@@ -873,9 +1135,6 @@ simple_escape_filter(Channel *c, char *buf, int len) | |||
873 | static void | 1135 | static void |
874 | client_channel_closed(int id, void *arg) | 1136 | client_channel_closed(int id, void *arg) |
875 | { | 1137 | { |
876 | if (id != session_ident) | ||
877 | error("client_channel_closed: id %d != session_ident %d", | ||
878 | id, session_ident); | ||
879 | channel_cancel_cleanup(id); | 1138 | channel_cancel_cleanup(id); |
880 | session_closed = 1; | 1139 | session_closed = 1; |
881 | leave_raw_mode(); | 1140 | leave_raw_mode(); |
@@ -893,7 +1152,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
893 | { | 1152 | { |
894 | fd_set *readset = NULL, *writeset = NULL; | 1153 | fd_set *readset = NULL, *writeset = NULL; |
895 | double start_time, total_time; | 1154 | double start_time, total_time; |
896 | int max_fd = 0, max_fd2 = 0, len, rekeying = 0, nalloc = 0; | 1155 | int max_fd = 0, max_fd2 = 0, len, rekeying = 0; |
1156 | u_int nalloc = 0; | ||
897 | char buf[100]; | 1157 | char buf[100]; |
898 | 1158 | ||
899 | debug("Entering interactive session."); | 1159 | debug("Entering interactive session."); |
@@ -909,6 +1169,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
909 | connection_in = packet_get_connection_in(); | 1169 | connection_in = packet_get_connection_in(); |
910 | connection_out = packet_get_connection_out(); | 1170 | connection_out = packet_get_connection_out(); |
911 | max_fd = MAX(connection_in, connection_out); | 1171 | max_fd = MAX(connection_in, connection_out); |
1172 | if (control_fd != -1) | ||
1173 | max_fd = MAX(max_fd, control_fd); | ||
912 | 1174 | ||
913 | if (!compat20) { | 1175 | if (!compat20) { |
914 | /* enable nonblocking unless tty */ | 1176 | /* enable nonblocking unless tty */ |
@@ -1026,6 +1288,9 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1026 | /* Buffer input from the connection. */ | 1288 | /* Buffer input from the connection. */ |
1027 | client_process_net_input(readset); | 1289 | client_process_net_input(readset); |
1028 | 1290 | ||
1291 | /* Accept control connections. */ | ||
1292 | client_process_control(readset); | ||
1293 | |||
1029 | if (quit_pending) | 1294 | if (quit_pending) |
1030 | break; | 1295 | break; |
1031 | 1296 | ||
@@ -1357,7 +1622,7 @@ static void | |||
1357 | client_input_channel_req(int type, u_int32_t seq, void *ctxt) | 1622 | client_input_channel_req(int type, u_int32_t seq, void *ctxt) |
1358 | { | 1623 | { |
1359 | Channel *c = NULL; | 1624 | Channel *c = NULL; |
1360 | int id, reply, success = 0; | 1625 | int exitval, id, reply, success = 0; |
1361 | char *rtype; | 1626 | char *rtype; |
1362 | 1627 | ||
1363 | id = packet_get_int(); | 1628 | id = packet_get_int(); |
@@ -1367,24 +1632,28 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) | |||
1367 | debug("client_input_channel_req: channel %d rtype %s reply %d", | 1632 | debug("client_input_channel_req: channel %d rtype %s reply %d", |
1368 | id, rtype, reply); | 1633 | id, rtype, reply); |
1369 | 1634 | ||
1370 | if (session_ident == -1) { | 1635 | if (id == -1) { |
1371 | error("client_input_channel_req: no channel %d", session_ident); | 1636 | error("client_input_channel_req: request for channel -1"); |
1372 | } else if (id != session_ident) { | 1637 | } else if ((c = channel_lookup(id)) == NULL) { |
1373 | error("client_input_channel_req: channel %d: wrong channel: %d", | ||
1374 | session_ident, id); | ||
1375 | } | ||
1376 | c = channel_lookup(id); | ||
1377 | if (c == NULL) { | ||
1378 | error("client_input_channel_req: channel %d: unknown channel", id); | 1638 | error("client_input_channel_req: channel %d: unknown channel", id); |
1379 | } else if (strcmp(rtype, "exit-status") == 0) { | 1639 | } else if (strcmp(rtype, "exit-status") == 0) { |
1380 | success = 1; | 1640 | exitval = packet_get_int(); |
1381 | exit_status = packet_get_int(); | 1641 | if (id == session_ident) { |
1642 | success = 1; | ||
1643 | exit_status = exitval; | ||
1644 | } else if (c->ctl_fd == -1) { | ||
1645 | error("client_input_channel_req: unexpected channel %d", | ||
1646 | session_ident); | ||
1647 | } else { | ||
1648 | atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval)); | ||
1649 | success = 1; | ||
1650 | } | ||
1382 | packet_check_eom(); | 1651 | packet_check_eom(); |
1383 | } | 1652 | } |
1384 | if (reply) { | 1653 | if (reply) { |
1385 | packet_start(success ? | 1654 | packet_start(success ? |
1386 | SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); | 1655 | SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); |
1387 | packet_put_int(c->remote_id); | 1656 | packet_put_int(id); |
1388 | packet_send(); | 1657 | packet_send(); |
1389 | } | 1658 | } |
1390 | xfree(rtype); | 1659 | xfree(rtype); |
@@ -1409,6 +1678,97 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt) | |||
1409 | xfree(rtype); | 1678 | xfree(rtype); |
1410 | } | 1679 | } |
1411 | 1680 | ||
1681 | void | ||
1682 | client_session2_setup(int id, int want_tty, int want_subsystem, | ||
1683 | const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env, | ||
1684 | dispatch_fn *subsys_repl) | ||
1685 | { | ||
1686 | int len; | ||
1687 | |||
1688 | debug2("%s: id %d", __func__, id); | ||
1689 | |||
1690 | if (want_tty) { | ||
1691 | struct winsize ws; | ||
1692 | struct termios tio; | ||
1693 | |||
1694 | /* Store window size in the packet. */ | ||
1695 | if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) | ||
1696 | memset(&ws, 0, sizeof(ws)); | ||
1697 | |||
1698 | channel_request_start(id, "pty-req", 0); | ||
1699 | packet_put_cstring(term != NULL ? term : ""); | ||
1700 | packet_put_int(ws.ws_col); | ||
1701 | packet_put_int(ws.ws_row); | ||
1702 | packet_put_int(ws.ws_xpixel); | ||
1703 | packet_put_int(ws.ws_ypixel); | ||
1704 | tio = get_saved_tio(); | ||
1705 | tty_make_modes(-1, tiop != NULL ? tiop : &tio); | ||
1706 | packet_send(); | ||
1707 | /* XXX wait for reply */ | ||
1708 | } | ||
1709 | |||
1710 | /* Transfer any environment variables from client to server */ | ||
1711 | if (options.num_send_env != 0 && env != NULL) { | ||
1712 | int i, j, matched; | ||
1713 | char *name, *val; | ||
1714 | |||
1715 | debug("Sending environment."); | ||
1716 | for (i = 0; env[i] != NULL; i++) { | ||
1717 | /* Split */ | ||
1718 | name = xstrdup(env[i]); | ||
1719 | if ((val = strchr(name, '=')) == NULL) { | ||
1720 | free(name); | ||
1721 | continue; | ||
1722 | } | ||
1723 | *val++ = '\0'; | ||
1724 | |||
1725 | matched = 0; | ||
1726 | for (j = 0; j < options.num_send_env; j++) { | ||
1727 | if (match_pattern(name, options.send_env[j])) { | ||
1728 | matched = 1; | ||
1729 | break; | ||
1730 | } | ||
1731 | } | ||
1732 | if (!matched) { | ||
1733 | debug3("Ignored env %s", name); | ||
1734 | free(name); | ||
1735 | continue; | ||
1736 | } | ||
1737 | |||
1738 | debug("Sending env %s = %s", name, val); | ||
1739 | channel_request_start(id, "env", 0); | ||
1740 | packet_put_cstring(name); | ||
1741 | packet_put_cstring(val); | ||
1742 | packet_send(); | ||
1743 | free(name); | ||
1744 | } | ||
1745 | } | ||
1746 | |||
1747 | len = buffer_len(cmd); | ||
1748 | if (len > 0) { | ||
1749 | if (len > 900) | ||
1750 | len = 900; | ||
1751 | if (want_subsystem) { | ||
1752 | debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd)); | ||
1753 | channel_request_start(id, "subsystem", subsys_repl != NULL); | ||
1754 | if (subsys_repl != NULL) { | ||
1755 | /* register callback for reply */ | ||
1756 | /* XXX we assume that client_loop has already been called */ | ||
1757 | dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl); | ||
1758 | dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl); | ||
1759 | } | ||
1760 | } else { | ||
1761 | debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd)); | ||
1762 | channel_request_start(id, "exec", 0); | ||
1763 | } | ||
1764 | packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); | ||
1765 | packet_send(); | ||
1766 | } else { | ||
1767 | channel_request_start(id, "shell", 0); | ||
1768 | packet_send(); | ||
1769 | } | ||
1770 | } | ||
1771 | |||
1412 | static void | 1772 | static void |
1413 | client_init_dispatch_20(void) | 1773 | client_init_dispatch_20(void) |
1414 | { | 1774 | { |
@@ -1475,5 +1835,7 @@ cleanup_exit(int i) | |||
1475 | { | 1835 | { |
1476 | leave_raw_mode(); | 1836 | leave_raw_mode(); |
1477 | leave_non_blocking(); | 1837 | leave_non_blocking(); |
1838 | if (options.control_path != NULL && control_fd != -1) | ||
1839 | unlink(options.control_path); | ||
1478 | _exit(i); | 1840 | _exit(i); |
1479 | } | 1841 | } |