diff options
author | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
commit | 137d76ba65883aa8143af1fcad83b57e7badef0c (patch) | |
tree | f426e804bb5248ceafedfab7bb78ae6e6752942c /clientloop.c | |
parent | dac7d049dad31f5f84d421d4eb628a7e13f977d7 (diff) | |
parent | ef94e5613d37bcbf880f21ee6094e4b1c7683a4c (diff) |
* New upstream release (closes: #474301). Important changes not previously
backported to 4.7p1:
- 4.9/4.9p1 (http://www.openssh.com/txt/release-4.9):
+ Added chroot(2) support for sshd(8), controlled by a new option
"ChrootDirectory" (closes: #139047, LP: #24777).
+ Linked sftp-server(8) into sshd(8). The internal sftp server is used
when the command "internal-sftp" is specified in a Subsystem or
ForceCommand declaration. When used with ChrootDirectory, the
internal sftp server requires no special configuration of files
inside the chroot environment.
+ Added a protocol extension method "posix-rename@openssh.com" for
sftp-server(8) to perform POSIX atomic rename() operations; sftp(1)
prefers this if available (closes: #308561).
+ Removed the fixed limit of 100 file handles in sftp-server(8).
+ ssh(8) will now skip generation of SSH protocol 1 ephemeral server
keys when in inetd mode and protocol 2 connections are negotiated.
This speeds up protocol 2 connections to inetd-mode servers that
also allow Protocol 1.
+ Accept the PermitRootLogin directive in a sshd_config(5) Match
block. Allows for, e.g. permitting root only from the local network.
+ Reworked sftp(1) argument splitting and escaping to be more
internally consistent (i.e. between sftp commands) and more
consistent with sh(1). Please note that this will change the
interpretation of some quoted strings, especially those with
embedded backslash escape sequences.
+ Support "Banner=none" in sshd_config(5) to disable sending of a
pre-login banner (e.g. in a Match block).
+ ssh(1) ProxyCommands are now executed with $SHELL rather than
/bin/sh.
+ ssh(1)'s ConnectTimeout option is now applied to both the TCP
connection and the SSH banner exchange (previously it just covered
the TCP connection). This allows callers of ssh(1) to better detect
and deal with stuck servers that accept a TCP connection but don't
progress the protocol, and also makes ConnectTimeout useful for
connections via a ProxyCommand.
+ scp(1) incorrectly reported "stalled" on slow copies (closes:
#140828).
+ scp(1) date underflow for timestamps before epoch.
+ ssh(1) used the obsolete SIG DNS RRtype for host keys in DNS,
instead of the current standard RRSIG.
+ Correctly drain ACKs when a sftp(1) upload write fails midway,
avoids a fatal() exit from what should be a recoverable condition.
+ Fixed ssh-keygen(1) selective host key hashing (i.e. "ssh-keygen -HF
hostname") to not include any IP address in the data to be hashed.
+ Make ssh(1) skip listening on the IPv6 wildcard address when a
binding address of 0.0.0.0 is used against an old SSH server that
does not support the RFC4254 syntax for wildcard bind addresses.
+ Enable IPV6_V6ONLY socket option on sshd(8) listen socket, as is
already done for X11/TCP forwarding sockets (closes: #439661).
+ Fix FD leak that could hang a ssh(1) connection multiplexing master.
+ Make ssh(1) -q option documentation consistent with reality.
+ Fixed sshd(8) PAM support not calling pam_session_close(), or
failing to call it with root privileges (closes: #372680).
+ Fix activation of OpenSSL engine support when requested in configure
(LP: #119295).
- 5.1/5.1p1 (http://www.openssh.com/txt/release-5.1):
+ Introduce experimental SSH Fingerprint ASCII Visualisation to ssh(1)
and ssh-keygen(1). Visual fingerprint display is controlled by a new
ssh_config(5) option "VisualHostKey". The intent is to render SSH
host keys in a visual form that is amenable to easy recall and
rejection of changed host keys.
+ sshd_config(5) now supports CIDR address/masklen matching in "Match
address" blocks, with a fallback to classic wildcard matching.
+ sshd(8) now supports CIDR matching in ~/.ssh/authorized_keys
from="..." restrictions, also with a fallback to classic wildcard
matching.
+ Added an extended test mode (-T) to sshd(8) to request that it write
its effective configuration to stdout and exit. Extended test mode
also supports the specification of connection parameters (username,
source address and hostname) to test the application of
sshd_config(5) Match rules.
+ ssh(1) now prints the number of bytes transferred and the overall
connection throughput for SSH protocol 2 sessions when in verbose
mode (previously these statistics were displayed for protocol 1
connections only).
+ sftp-server(8) now supports extension methods statvfs@openssh.com
and fstatvfs@openssh.com that implement statvfs(2)-like operations.
+ sftp(1) now has a "df" command to the sftp client that uses the
statvfs@openssh.com to produce a df(1)-like display of filesystem
space and inode utilisation (requires statvfs@openssh.com support on
the server).
+ Added a MaxSessions option to sshd_config(5) to allow control of the
number of multiplexed sessions supported over a single TCP
connection. This allows increasing the number of allowed sessions
above the previous default of 10, disabling connection multiplexing
(MaxSessions=1) or disallowing login/shell/subsystem sessions
entirely (MaxSessions=0).
+ Added a no-more-sessions@openssh.com global request extension that
is sent from ssh(1) to sshd(8) when the client knows that it will
never request another session (i.e. when session multiplexing is
disabled). This allows a server to disallow further session requests
and terminate the session in cases where the client has been
hijacked.
+ ssh-keygen(1) now supports the use of the -l option in combination
with -F to search for a host in ~/.ssh/known_hosts and display its
fingerprint.
+ ssh-keyscan(1) now defaults to "rsa" (protocol 2) keys, instead of
"rsa1".
+ Added an AllowAgentForwarding option to sshd_config(8) to control
whether authentication agent forwarding is permitted. Note that this
is a loose control, as a client may install their own unofficial
forwarder.
+ ssh(1) and sshd(8): avoid unnecessary malloc/copy/free when
receiving network data, resulting in a ~10% speedup.
+ ssh(1) and sshd(8) will now try additional addresses when connecting
to a port forward destination whose DNS name resolves to more than
one address. The previous behaviour was to try the only first
address and give up if that failed.
+ ssh(1) and sshd(8) now support signalling that channels are
half-closed for writing, through a channel protocol extension
notification "eow@openssh.com". This allows propagation of closed
file descriptors, so that commands such as "ssh -2 localhost od
/bin/ls | true" do not send unnecessary data over the wire.
+ sshd(8): increased the default size of ssh protocol 1 ephemeral keys
from 768 to 1024 bits.
+ When ssh(1) has been requested to fork after authentication ("ssh
-f") with ExitOnForwardFailure enabled, delay the fork until after
replies for any -R forwards have been seen. Allows for robust
detection of -R forward failure when using -f.
+ "Match group" blocks in sshd_config(5) now support negation of
groups. E.g. "Match group staff,!guests".
+ sftp(1) and sftp-server(8) now allow chmod-like operations to set
set[ug]id/sticky bits.
+ The MaxAuthTries option is now permitted in sshd_config(5) match
blocks.
+ Multiplexed ssh(1) sessions now support a subset of the ~ escapes
that are available to a primary connection.
+ ssh(1) connection multiplexing will now fall back to creating a new
connection in most error cases (closes: #352830).
+ Make ssh(1) deal more gracefully with channel requests that fail.
Previously it would optimistically assume that requests would always
succeed, which could cause hangs if they did not (e.g. when the
server runs out of file descriptors).
+ ssh(1) now reports multiplexing errors via the multiplex slave's
stderr where possible (subject to LogLevel in the mux master).
+ Prevent sshd(8) from erroneously applying public key restrictions
leaned from ~/.ssh/authorized_keys to other authentication methods
when public key authentication subsequently fails (LP: #161047).
+ Fixed an UMAC alignment problem that manifested on Itanium
platforms.
Diffstat (limited to 'clientloop.c')
-rw-r--r-- | clientloop.c | 753 |
1 files changed, 345 insertions, 408 deletions
diff --git a/clientloop.c b/clientloop.c index 7037c4192..abe5609de 100644 --- a/clientloop.c +++ b/clientloop.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: clientloop.c,v 1.181 2007/08/15 08:14:46 markus Exp $ */ | 1 | /* $OpenBSD: clientloop.c,v 1.201 2008/07/16 11:51:14 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -86,6 +86,7 @@ | |||
86 | #include <pwd.h> | 86 | #include <pwd.h> |
87 | #include <unistd.h> | 87 | #include <unistd.h> |
88 | 88 | ||
89 | #include "openbsd-compat/sys-queue.h" | ||
89 | #include "xmalloc.h" | 90 | #include "xmalloc.h" |
90 | #include "ssh.h" | 91 | #include "ssh.h" |
91 | #include "ssh1.h" | 92 | #include "ssh1.h" |
@@ -120,7 +121,7 @@ extern int stdin_null_flag; | |||
120 | extern int no_shell_flag; | 121 | extern int no_shell_flag; |
121 | 122 | ||
122 | /* Control socket */ | 123 | /* Control socket */ |
123 | extern int control_fd; | 124 | extern int muxserver_sock; |
124 | 125 | ||
125 | /* | 126 | /* |
126 | * Name of the host we are connecting to. This is the name given on the | 127 | * Name of the host we are connecting to. This is the name given on the |
@@ -143,36 +144,46 @@ static int in_non_blocking_mode = 0; | |||
143 | 144 | ||
144 | /* Common data for the client loop code. */ | 145 | /* Common data for the client loop code. */ |
145 | static volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ | 146 | static volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ |
146 | static int escape_char; /* Escape character. */ | 147 | static int escape_char1; /* Escape character. (proto1 only) */ |
147 | static int escape_pending; /* Last character was the escape character */ | 148 | static int escape_pending1; /* Last character was an escape (proto1 only) */ |
148 | static int last_was_cr; /* Last character was a newline. */ | 149 | static int last_was_cr; /* Last character was a newline. */ |
149 | static int exit_status; /* Used to store the exit status of the command. */ | 150 | static int exit_status; /* Used to store the command exit status. */ |
150 | static int stdin_eof; /* EOF has been encountered on standard error. */ | 151 | static int stdin_eof; /* EOF has been encountered on stderr. */ |
151 | static Buffer stdin_buffer; /* Buffer for stdin data. */ | 152 | static Buffer stdin_buffer; /* Buffer for stdin data. */ |
152 | static Buffer stdout_buffer; /* Buffer for stdout data. */ | 153 | static Buffer stdout_buffer; /* Buffer for stdout data. */ |
153 | static Buffer stderr_buffer; /* Buffer for stderr data. */ | 154 | static Buffer stderr_buffer; /* Buffer for stderr data. */ |
154 | static u_long stdin_bytes, stdout_bytes, stderr_bytes; | ||
155 | static u_int buffer_high;/* Soft max buffer size. */ | 155 | static u_int buffer_high;/* Soft max buffer size. */ |
156 | static int connection_in; /* Connection to server (input). */ | 156 | static int connection_in; /* Connection to server (input). */ |
157 | static int connection_out; /* Connection to server (output). */ | 157 | static int connection_out; /* Connection to server (output). */ |
158 | static int need_rekeying; /* Set to non-zero if rekeying is requested. */ | 158 | static int need_rekeying; /* Set to non-zero if rekeying is requested. */ |
159 | static int session_closed = 0; /* In SSH2: login session closed. */ | 159 | static int session_closed = 0; /* In SSH2: login session closed. */ |
160 | static int server_alive_timeouts = 0; | ||
161 | 160 | ||
162 | static void client_init_dispatch(void); | 161 | static void client_init_dispatch(void); |
163 | int session_ident = -1; | 162 | int session_ident = -1; |
164 | 163 | ||
165 | struct confirm_ctx { | 164 | /* Track escape per proto2 channel */ |
166 | int want_tty; | 165 | struct escape_filter_ctx { |
167 | int want_subsys; | 166 | int escape_pending; |
168 | int want_x_fwd; | 167 | int escape_char; |
169 | int want_agent_fwd; | ||
170 | Buffer cmd; | ||
171 | char *term; | ||
172 | struct termios tio; | ||
173 | char **env; | ||
174 | }; | 168 | }; |
175 | 169 | ||
170 | /* Context for channel confirmation replies */ | ||
171 | struct channel_reply_ctx { | ||
172 | const char *request_type; | ||
173 | int id, do_close; | ||
174 | }; | ||
175 | |||
176 | /* Global request success/failure callbacks */ | ||
177 | struct global_confirm { | ||
178 | TAILQ_ENTRY(global_confirm) entry; | ||
179 | global_confirm_cb *cb; | ||
180 | void *ctx; | ||
181 | int ref_count; | ||
182 | }; | ||
183 | TAILQ_HEAD(global_confirms, global_confirm); | ||
184 | static struct global_confirms global_confirms = | ||
185 | TAILQ_HEAD_INITIALIZER(global_confirms); | ||
186 | |||
176 | /*XXX*/ | 187 | /*XXX*/ |
177 | extern Kex *xxx_kex; | 188 | extern Kex *xxx_kex; |
178 | 189 | ||
@@ -380,7 +391,10 @@ client_check_initial_eof_on_stdin(void) | |||
380 | /* Check for immediate EOF on stdin. */ | 391 | /* Check for immediate EOF on stdin. */ |
381 | len = read(fileno(stdin), buf, 1); | 392 | len = read(fileno(stdin), buf, 1); |
382 | if (len == 0) { | 393 | if (len == 0) { |
383 | /* EOF. Record that we have seen it and send EOF to server. */ | 394 | /* |
395 | * EOF. Record that we have seen it and send | ||
396 | * EOF to server. | ||
397 | */ | ||
384 | debug("Sending eof."); | 398 | debug("Sending eof."); |
385 | stdin_eof = 1; | 399 | stdin_eof = 1; |
386 | packet_start(SSH_CMSG_EOF); | 400 | packet_start(SSH_CMSG_EOF); |
@@ -391,8 +405,8 @@ client_check_initial_eof_on_stdin(void) | |||
391 | * and also process it as an escape character if | 405 | * and also process it as an escape character if |
392 | * appropriate. | 406 | * appropriate. |
393 | */ | 407 | */ |
394 | if ((u_char) buf[0] == escape_char) | 408 | if ((u_char) buf[0] == escape_char1) |
395 | escape_pending = 1; | 409 | escape_pending1 = 1; |
396 | else | 410 | else |
397 | buffer_append(&stdin_buffer, buf, 1); | 411 | buffer_append(&stdin_buffer, buf, 1); |
398 | } | 412 | } |
@@ -422,7 +436,6 @@ client_make_packets_from_stdin_data(void) | |||
422 | packet_put_string(buffer_ptr(&stdin_buffer), len); | 436 | packet_put_string(buffer_ptr(&stdin_buffer), len); |
423 | packet_send(); | 437 | packet_send(); |
424 | buffer_consume(&stdin_buffer, len); | 438 | buffer_consume(&stdin_buffer, len); |
425 | stdin_bytes += len; | ||
426 | /* If we have a pending EOF, send it now. */ | 439 | /* If we have a pending EOF, send it now. */ |
427 | if (stdin_eof && buffer_len(&stdin_buffer) == 0) { | 440 | if (stdin_eof && buffer_len(&stdin_buffer) == 0) { |
428 | packet_start(SSH_CMSG_EOF); | 441 | packet_start(SSH_CMSG_EOF); |
@@ -467,15 +480,26 @@ client_check_window_change(void) | |||
467 | static void | 480 | static void |
468 | client_global_request_reply(int type, u_int32_t seq, void *ctxt) | 481 | client_global_request_reply(int type, u_int32_t seq, void *ctxt) |
469 | { | 482 | { |
470 | server_alive_timeouts = 0; | 483 | struct global_confirm *gc; |
471 | client_global_request_reply_fwd(type, seq, ctxt); | 484 | |
485 | if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) | ||
486 | return; | ||
487 | if (gc->cb != NULL) | ||
488 | gc->cb(type, seq, gc->ctx); | ||
489 | if (--gc->ref_count <= 0) { | ||
490 | TAILQ_REMOVE(&global_confirms, gc, entry); | ||
491 | bzero(gc, sizeof(*gc)); | ||
492 | xfree(gc); | ||
493 | } | ||
494 | |||
495 | keep_alive_timeouts = 0; | ||
472 | } | 496 | } |
473 | 497 | ||
474 | static void | 498 | static void |
475 | server_alive_check(void) | 499 | server_alive_check(void) |
476 | { | 500 | { |
477 | if (compat20) { | 501 | if (compat20) { |
478 | if (++server_alive_timeouts > options.server_alive_count_max) { | 502 | if (++keep_alive_timeouts > options.server_alive_count_max) { |
479 | logit("Timeout, server not responding."); | 503 | logit("Timeout, server not responding."); |
480 | cleanup_exit(255); | 504 | cleanup_exit(255); |
481 | } | 505 | } |
@@ -483,6 +507,8 @@ server_alive_check(void) | |||
483 | packet_put_cstring("keepalive@openssh.com"); | 507 | packet_put_cstring("keepalive@openssh.com"); |
484 | packet_put_char(1); /* boolean: want reply */ | 508 | packet_put_char(1); /* boolean: want reply */ |
485 | packet_send(); | 509 | packet_send(); |
510 | /* Insert an empty placeholder to maintain ordering */ | ||
511 | client_register_global_confirm(NULL, NULL); | ||
486 | } else { | 512 | } else { |
487 | packet_send_ignore(0); | 513 | packet_send_ignore(0); |
488 | packet_send(); | 514 | packet_send(); |
@@ -538,8 +564,8 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, | |||
538 | if (packet_have_data_to_write()) | 564 | if (packet_have_data_to_write()) |
539 | FD_SET(connection_out, *writesetp); | 565 | FD_SET(connection_out, *writesetp); |
540 | 566 | ||
541 | if (control_fd != -1) | 567 | if (muxserver_sock != -1) |
542 | FD_SET(control_fd, *readsetp); | 568 | FD_SET(muxserver_sock, *readsetp); |
543 | 569 | ||
544 | /* | 570 | /* |
545 | * Wait for something to happen. This will suspend the process until | 571 | * Wait for something to happen. This will suspend the process until |
@@ -581,9 +607,11 @@ client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) | |||
581 | { | 607 | { |
582 | /* Flush stdout and stderr buffers. */ | 608 | /* Flush stdout and stderr buffers. */ |
583 | if (buffer_len(bout) > 0) | 609 | if (buffer_len(bout) > 0) |
584 | atomicio(vwrite, fileno(stdout), buffer_ptr(bout), buffer_len(bout)); | 610 | atomicio(vwrite, fileno(stdout), buffer_ptr(bout), |
611 | buffer_len(bout)); | ||
585 | if (buffer_len(berr) > 0) | 612 | if (buffer_len(berr) > 0) |
586 | atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr)); | 613 | atomicio(vwrite, fileno(stderr), buffer_ptr(berr), |
614 | buffer_len(berr)); | ||
587 | 615 | ||
588 | leave_raw_mode(); | 616 | leave_raw_mode(); |
589 | 617 | ||
@@ -623,9 +651,13 @@ client_process_net_input(fd_set *readset) | |||
623 | /* Read as much as possible. */ | 651 | /* Read as much as possible. */ |
624 | len = read(connection_in, buf, sizeof(buf)); | 652 | len = read(connection_in, buf, sizeof(buf)); |
625 | if (len == 0) { | 653 | if (len == 0) { |
626 | /* Received EOF. The remote host has closed the connection. */ | 654 | /* |
627 | snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", | 655 | * Received EOF. The remote host has closed the |
628 | host); | 656 | * connection. |
657 | */ | ||
658 | snprintf(buf, sizeof buf, | ||
659 | "Connection to %.300s closed by remote host.\r\n", | ||
660 | host); | ||
629 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 661 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
630 | quit_pending = 1; | 662 | quit_pending = 1; |
631 | return; | 663 | return; |
@@ -634,13 +666,18 @@ client_process_net_input(fd_set *readset) | |||
634 | * There is a kernel bug on Solaris that causes select to | 666 | * There is a kernel bug on Solaris that causes select to |
635 | * sometimes wake up even though there is no data available. | 667 | * sometimes wake up even though there is no data available. |
636 | */ | 668 | */ |
637 | if (len < 0 && (errno == EAGAIN || errno == EINTR)) | 669 | if (len < 0 && |
670 | (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) | ||
638 | len = 0; | 671 | len = 0; |
639 | 672 | ||
640 | if (len < 0) { | 673 | if (len < 0) { |
641 | /* An error has encountered. Perhaps there is a network problem. */ | 674 | /* |
642 | snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", | 675 | * An error has encountered. Perhaps there is a |
643 | host, strerror(errno)); | 676 | * network problem. |
677 | */ | ||
678 | snprintf(buf, sizeof buf, | ||
679 | "Read from remote host %.300s: %.100s\r\n", | ||
680 | host, strerror(errno)); | ||
644 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 681 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
645 | quit_pending = 1; | 682 | quit_pending = 1; |
646 | return; | 683 | return; |
@@ -650,289 +687,81 @@ client_process_net_input(fd_set *readset) | |||
650 | } | 687 | } |
651 | 688 | ||
652 | static void | 689 | static void |
653 | client_subsystem_reply(int type, u_int32_t seq, void *ctxt) | 690 | client_status_confirm(int type, Channel *c, void *ctx) |
654 | { | 691 | { |
655 | int id; | 692 | struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; |
656 | Channel *c; | 693 | char errmsg[256]; |
657 | 694 | int tochan; | |
658 | id = packet_get_int(); | 695 | |
659 | packet_check_eom(); | 696 | /* XXX supress on mux _client_ quietmode */ |
660 | 697 | tochan = options.log_level >= SYSLOG_LEVEL_ERROR && | |
661 | if ((c = channel_lookup(id)) == NULL) { | 698 | c->ctl_fd != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; |
662 | error("%s: no channel for id %d", __func__, id); | 699 | |
663 | return; | 700 | if (type == SSH2_MSG_CHANNEL_SUCCESS) { |
664 | } | 701 | debug2("%s request accepted on channel %d", |
665 | 702 | cr->request_type, c->self); | |
666 | if (type == SSH2_MSG_CHANNEL_SUCCESS) | 703 | } else if (type == SSH2_MSG_CHANNEL_FAILURE) { |
667 | debug2("Request suceeded on channel %d", id); | 704 | if (tochan) { |
668 | else if (type == SSH2_MSG_CHANNEL_FAILURE) { | 705 | snprintf(errmsg, sizeof(errmsg), |
669 | error("Request failed on channel %d", id); | 706 | "%s request failed\r\n", cr->request_type); |
670 | channel_free(c); | 707 | } else { |
708 | snprintf(errmsg, sizeof(errmsg), | ||
709 | "%s request failed on channel %d", | ||
710 | cr->request_type, c->self); | ||
711 | } | ||
712 | /* If error occurred on primary session channel, then exit */ | ||
713 | if (cr->do_close && c->self == session_ident) | ||
714 | fatal("%s", errmsg); | ||
715 | /* If error occurred on mux client, append to their stderr */ | ||
716 | if (tochan) | ||
717 | buffer_append(&c->extended, errmsg, strlen(errmsg)); | ||
718 | else | ||
719 | error("%s", errmsg); | ||
720 | if (cr->do_close) { | ||
721 | chan_read_failed(c); | ||
722 | chan_write_failed(c); | ||
723 | } | ||
671 | } | 724 | } |
725 | xfree(cr); | ||
672 | } | 726 | } |
673 | 727 | ||
674 | static void | 728 | static void |
675 | client_extra_session2_setup(int id, void *arg) | 729 | client_abandon_status_confirm(Channel *c, void *ctx) |
676 | { | 730 | { |
677 | struct confirm_ctx *cctx = arg; | 731 | xfree(ctx); |
678 | const char *display; | ||
679 | Channel *c; | ||
680 | int i; | ||
681 | |||
682 | if (cctx == NULL) | ||
683 | fatal("%s: cctx == NULL", __func__); | ||
684 | if ((c = channel_lookup(id)) == NULL) | ||
685 | fatal("%s: no channel for id %d", __func__, id); | ||
686 | |||
687 | display = getenv("DISPLAY"); | ||
688 | if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { | ||
689 | char *proto, *data; | ||
690 | /* Get reasonable local authentication information. */ | ||
691 | client_x11_get_proto(display, options.xauth_location, | ||
692 | options.forward_x11_trusted, &proto, &data); | ||
693 | /* Request forwarding with authentication spoofing. */ | ||
694 | debug("Requesting X11 forwarding with authentication spoofing."); | ||
695 | x11_request_forwarding_with_spoofing(id, display, proto, data); | ||
696 | /* XXX wait for reply */ | ||
697 | } | ||
698 | |||
699 | if (cctx->want_agent_fwd && options.forward_agent) { | ||
700 | debug("Requesting authentication agent forwarding."); | ||
701 | channel_request_start(id, "auth-agent-req@openssh.com", 0); | ||
702 | packet_send(); | ||
703 | } | ||
704 | |||
705 | client_session2_setup(id, cctx->want_tty, cctx->want_subsys, | ||
706 | cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env, | ||
707 | client_subsystem_reply); | ||
708 | |||
709 | c->confirm_ctx = NULL; | ||
710 | buffer_free(&cctx->cmd); | ||
711 | xfree(cctx->term); | ||
712 | if (cctx->env != NULL) { | ||
713 | for (i = 0; cctx->env[i] != NULL; i++) | ||
714 | xfree(cctx->env[i]); | ||
715 | xfree(cctx->env); | ||
716 | } | ||
717 | xfree(cctx); | ||
718 | } | 732 | } |
719 | 733 | ||
720 | static void | 734 | static void |
721 | client_process_control(fd_set *readset) | 735 | client_expect_confirm(int id, const char *request, int do_close) |
722 | { | 736 | { |
723 | Buffer m; | 737 | struct channel_reply_ctx *cr = xmalloc(sizeof(*cr)); |
724 | Channel *c; | ||
725 | int client_fd, new_fd[3], ver, allowed, window, packetmax; | ||
726 | socklen_t addrlen; | ||
727 | struct sockaddr_storage addr; | ||
728 | struct confirm_ctx *cctx; | ||
729 | char *cmd; | ||
730 | u_int i, len, env_len, command, flags; | ||
731 | uid_t euid; | ||
732 | gid_t egid; | ||
733 | |||
734 | /* | ||
735 | * Accept connection on control socket | ||
736 | */ | ||
737 | if (control_fd == -1 || !FD_ISSET(control_fd, readset)) | ||
738 | return; | ||
739 | |||
740 | memset(&addr, 0, sizeof(addr)); | ||
741 | addrlen = sizeof(addr); | ||
742 | if ((client_fd = accept(control_fd, | ||
743 | (struct sockaddr*)&addr, &addrlen)) == -1) { | ||
744 | error("%s accept: %s", __func__, strerror(errno)); | ||
745 | return; | ||
746 | } | ||
747 | |||
748 | if (getpeereid(client_fd, &euid, &egid) < 0) { | ||
749 | error("%s getpeereid failed: %s", __func__, strerror(errno)); | ||
750 | close(client_fd); | ||
751 | return; | ||
752 | } | ||
753 | if ((euid != 0) && (getuid() != euid)) { | ||
754 | error("control mode uid mismatch: peer euid %u != uid %u", | ||
755 | (u_int) euid, (u_int) getuid()); | ||
756 | close(client_fd); | ||
757 | return; | ||
758 | } | ||
759 | |||
760 | unset_nonblock(client_fd); | ||
761 | |||
762 | /* Read command */ | ||
763 | buffer_init(&m); | ||
764 | if (ssh_msg_recv(client_fd, &m) == -1) { | ||
765 | error("%s: client msg_recv failed", __func__); | ||
766 | close(client_fd); | ||
767 | buffer_free(&m); | ||
768 | return; | ||
769 | } | ||
770 | if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { | ||
771 | error("%s: wrong client version %d", __func__, ver); | ||
772 | buffer_free(&m); | ||
773 | close(client_fd); | ||
774 | return; | ||
775 | } | ||
776 | |||
777 | allowed = 1; | ||
778 | command = buffer_get_int(&m); | ||
779 | flags = buffer_get_int(&m); | ||
780 | |||
781 | buffer_clear(&m); | ||
782 | |||
783 | switch (command) { | ||
784 | case SSHMUX_COMMAND_OPEN: | ||
785 | if (options.control_master == SSHCTL_MASTER_ASK || | ||
786 | options.control_master == SSHCTL_MASTER_AUTO_ASK) | ||
787 | allowed = ask_permission("Allow shared connection " | ||
788 | "to %s? ", host); | ||
789 | /* continue below */ | ||
790 | break; | ||
791 | case SSHMUX_COMMAND_TERMINATE: | ||
792 | if (options.control_master == SSHCTL_MASTER_ASK || | ||
793 | options.control_master == SSHCTL_MASTER_AUTO_ASK) | ||
794 | allowed = ask_permission("Terminate shared connection " | ||
795 | "to %s? ", host); | ||
796 | if (allowed) | ||
797 | quit_pending = 1; | ||
798 | /* FALLTHROUGH */ | ||
799 | case SSHMUX_COMMAND_ALIVE_CHECK: | ||
800 | /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */ | ||
801 | buffer_clear(&m); | ||
802 | buffer_put_int(&m, allowed); | ||
803 | buffer_put_int(&m, getpid()); | ||
804 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
805 | error("%s: client msg_send failed", __func__); | ||
806 | close(client_fd); | ||
807 | buffer_free(&m); | ||
808 | return; | ||
809 | } | ||
810 | buffer_free(&m); | ||
811 | close(client_fd); | ||
812 | return; | ||
813 | default: | ||
814 | error("Unsupported command %d", command); | ||
815 | buffer_free(&m); | ||
816 | close(client_fd); | ||
817 | return; | ||
818 | } | ||
819 | |||
820 | /* Reply for SSHMUX_COMMAND_OPEN */ | ||
821 | buffer_clear(&m); | ||
822 | buffer_put_int(&m, allowed); | ||
823 | buffer_put_int(&m, getpid()); | ||
824 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
825 | error("%s: client msg_send failed", __func__); | ||
826 | close(client_fd); | ||
827 | buffer_free(&m); | ||
828 | return; | ||
829 | } | ||
830 | |||
831 | if (!allowed) { | ||
832 | error("Refused control connection"); | ||
833 | close(client_fd); | ||
834 | buffer_free(&m); | ||
835 | return; | ||
836 | } | ||
837 | 738 | ||
838 | buffer_clear(&m); | 739 | cr->request_type = request; |
839 | if (ssh_msg_recv(client_fd, &m) == -1) { | 740 | cr->do_close = do_close; |
840 | error("%s: client msg_recv failed", __func__); | ||
841 | close(client_fd); | ||
842 | buffer_free(&m); | ||
843 | return; | ||
844 | } | ||
845 | if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { | ||
846 | error("%s: wrong client version %d", __func__, ver); | ||
847 | buffer_free(&m); | ||
848 | close(client_fd); | ||
849 | return; | ||
850 | } | ||
851 | 741 | ||
852 | cctx = xcalloc(1, sizeof(*cctx)); | 742 | channel_register_status_confirm(id, client_status_confirm, |
853 | cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0; | 743 | client_abandon_status_confirm, cr); |
854 | cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0; | 744 | } |
855 | cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0; | ||
856 | cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0; | ||
857 | cctx->term = buffer_get_string(&m, &len); | ||
858 | |||
859 | cmd = buffer_get_string(&m, &len); | ||
860 | buffer_init(&cctx->cmd); | ||
861 | buffer_append(&cctx->cmd, cmd, strlen(cmd)); | ||
862 | |||
863 | env_len = buffer_get_int(&m); | ||
864 | env_len = MIN(env_len, 4096); | ||
865 | debug3("%s: receiving %d env vars", __func__, env_len); | ||
866 | if (env_len != 0) { | ||
867 | cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env)); | ||
868 | for (i = 0; i < env_len; i++) | ||
869 | cctx->env[i] = buffer_get_string(&m, &len); | ||
870 | cctx->env[i] = NULL; | ||
871 | } | ||
872 | 745 | ||
873 | debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__, | 746 | void |
874 | cctx->want_tty, cctx->want_subsys, cmd); | 747 | client_register_global_confirm(global_confirm_cb *cb, void *ctx) |
875 | xfree(cmd); | 748 | { |
876 | 749 | struct global_confirm *gc, *last_gc; | |
877 | /* Gather fds from client */ | 750 | |
878 | new_fd[0] = mm_receive_fd(client_fd); | 751 | /* Coalesce identical callbacks */ |
879 | new_fd[1] = mm_receive_fd(client_fd); | 752 | last_gc = TAILQ_LAST(&global_confirms, global_confirms); |
880 | new_fd[2] = mm_receive_fd(client_fd); | 753 | if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { |
881 | 754 | if (++last_gc->ref_count >= INT_MAX) | |
882 | debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__, | 755 | fatal("%s: last_gc->ref_count = %d", |
883 | new_fd[0], new_fd[1], new_fd[2]); | 756 | __func__, last_gc->ref_count); |
884 | |||
885 | /* Try to pick up ttymodes from client before it goes raw */ | ||
886 | if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) | ||
887 | error("%s: tcgetattr: %s", __func__, strerror(errno)); | ||
888 | |||
889 | /* This roundtrip is just for synchronisation of ttymodes */ | ||
890 | buffer_clear(&m); | ||
891 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
892 | error("%s: client msg_send failed", __func__); | ||
893 | close(client_fd); | ||
894 | close(new_fd[0]); | ||
895 | close(new_fd[1]); | ||
896 | close(new_fd[2]); | ||
897 | buffer_free(&m); | ||
898 | xfree(cctx->term); | ||
899 | if (env_len != 0) { | ||
900 | for (i = 0; i < env_len; i++) | ||
901 | xfree(cctx->env[i]); | ||
902 | xfree(cctx->env); | ||
903 | } | ||
904 | return; | 757 | return; |
905 | } | 758 | } |
906 | buffer_free(&m); | ||
907 | |||
908 | /* enable nonblocking unless tty */ | ||
909 | if (!isatty(new_fd[0])) | ||
910 | set_nonblock(new_fd[0]); | ||
911 | if (!isatty(new_fd[1])) | ||
912 | set_nonblock(new_fd[1]); | ||
913 | if (!isatty(new_fd[2])) | ||
914 | set_nonblock(new_fd[2]); | ||
915 | |||
916 | set_nonblock(client_fd); | ||
917 | |||
918 | window = CHAN_SES_WINDOW_DEFAULT; | ||
919 | packetmax = CHAN_SES_PACKET_DEFAULT; | ||
920 | if (cctx->want_tty) { | ||
921 | window >>= 1; | ||
922 | packetmax >>= 1; | ||
923 | } | ||
924 | |||
925 | c = channel_new("session", SSH_CHANNEL_OPENING, | ||
926 | new_fd[0], new_fd[1], new_fd[2], window, packetmax, | ||
927 | CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); | ||
928 | 759 | ||
929 | /* XXX */ | 760 | gc = xmalloc(sizeof(*gc)); |
930 | c->ctl_fd = client_fd; | 761 | gc->cb = cb; |
931 | 762 | gc->ctx = ctx; | |
932 | debug3("%s: channel_new: %d", __func__, c->self); | 763 | gc->ref_count = 1; |
933 | 764 | TAILQ_INSERT_TAIL(&global_confirms, gc, entry); | |
934 | channel_send_open(c->self); | ||
935 | channel_register_confirm(c->self, client_extra_session2_setup, cctx); | ||
936 | } | 765 | } |
937 | 766 | ||
938 | static void | 767 | static void |
@@ -945,6 +774,9 @@ process_cmdline(void) | |||
945 | u_short cancel_port; | 774 | u_short cancel_port; |
946 | Forward fwd; | 775 | Forward fwd; |
947 | 776 | ||
777 | bzero(&fwd, sizeof(fwd)); | ||
778 | fwd.listen_host = fwd.connect_host = NULL; | ||
779 | |||
948 | leave_raw_mode(); | 780 | leave_raw_mode(); |
949 | handler = signal(SIGINT, SIG_IGN); | 781 | handler = signal(SIGINT, SIG_IGN); |
950 | cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); | 782 | cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); |
@@ -1044,11 +876,18 @@ out: | |||
1044 | enter_raw_mode(); | 876 | enter_raw_mode(); |
1045 | if (cmd) | 877 | if (cmd) |
1046 | xfree(cmd); | 878 | xfree(cmd); |
879 | if (fwd.listen_host != NULL) | ||
880 | xfree(fwd.listen_host); | ||
881 | if (fwd.connect_host != NULL) | ||
882 | xfree(fwd.connect_host); | ||
1047 | } | 883 | } |
1048 | 884 | ||
1049 | /* process the characters one by one */ | 885 | /* |
886 | * Process the characters one by one, call with c==NULL for proto1 case. | ||
887 | */ | ||
1050 | static int | 888 | static int |
1051 | process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) | 889 | process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, |
890 | char *buf, int len) | ||
1052 | { | 891 | { |
1053 | char string[1024]; | 892 | char string[1024]; |
1054 | pid_t pid; | 893 | pid_t pid; |
@@ -1056,7 +895,20 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) | |||
1056 | u_int i; | 895 | u_int i; |
1057 | u_char ch; | 896 | u_char ch; |
1058 | char *s; | 897 | char *s; |
898 | int *escape_pendingp, escape_char; | ||
899 | struct escape_filter_ctx *efc; | ||
1059 | 900 | ||
901 | if (c == NULL) { | ||
902 | escape_pendingp = &escape_pending1; | ||
903 | escape_char = escape_char1; | ||
904 | } else { | ||
905 | if (c->filter_ctx == NULL) | ||
906 | return 0; | ||
907 | efc = (struct escape_filter_ctx *)c->filter_ctx; | ||
908 | escape_pendingp = &efc->escape_pending; | ||
909 | escape_char = efc->escape_char; | ||
910 | } | ||
911 | |||
1060 | if (len <= 0) | 912 | if (len <= 0) |
1061 | return (0); | 913 | return (0); |
1062 | 914 | ||
@@ -1064,25 +916,42 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) | |||
1064 | /* Get one character at a time. */ | 916 | /* Get one character at a time. */ |
1065 | ch = buf[i]; | 917 | ch = buf[i]; |
1066 | 918 | ||
1067 | if (escape_pending) { | 919 | if (*escape_pendingp) { |
1068 | /* We have previously seen an escape character. */ | 920 | /* We have previously seen an escape character. */ |
1069 | /* Clear the flag now. */ | 921 | /* Clear the flag now. */ |
1070 | escape_pending = 0; | 922 | *escape_pendingp = 0; |
1071 | 923 | ||
1072 | /* Process the escaped character. */ | 924 | /* Process the escaped character. */ |
1073 | switch (ch) { | 925 | switch (ch) { |
1074 | case '.': | 926 | case '.': |
1075 | /* Terminate the connection. */ | 927 | /* Terminate the connection. */ |
1076 | snprintf(string, sizeof string, "%c.\r\n", escape_char); | 928 | snprintf(string, sizeof string, "%c.\r\n", |
929 | escape_char); | ||
1077 | buffer_append(berr, string, strlen(string)); | 930 | buffer_append(berr, string, strlen(string)); |
1078 | 931 | ||
1079 | quit_pending = 1; | 932 | if (c && c->ctl_fd != -1) { |
933 | chan_read_failed(c); | ||
934 | chan_write_failed(c); | ||
935 | return 0; | ||
936 | } else | ||
937 | quit_pending = 1; | ||
1080 | return -1; | 938 | return -1; |
1081 | 939 | ||
1082 | case 'Z' - 64: | 940 | case 'Z' - 64: |
1083 | /* Suspend the program. */ | 941 | /* XXX support this for mux clients */ |
1084 | /* Print a message to that effect to the user. */ | 942 | if (c && c->ctl_fd != -1) { |
1085 | snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char); | 943 | noescape: |
944 | snprintf(string, sizeof string, | ||
945 | "%c%c escape not available to " | ||
946 | "multiplexed sessions\r\n", | ||
947 | escape_char, ch); | ||
948 | buffer_append(berr, string, | ||
949 | strlen(string)); | ||
950 | continue; | ||
951 | } | ||
952 | /* Suspend the program. Inform the user */ | ||
953 | snprintf(string, sizeof string, | ||
954 | "%c^Z [suspend ssh]\r\n", escape_char); | ||
1086 | buffer_append(berr, string, strlen(string)); | 955 | buffer_append(berr, string, strlen(string)); |
1087 | 956 | ||
1088 | /* Restore terminal modes and suspend. */ | 957 | /* Restore terminal modes and suspend. */ |
@@ -1107,16 +976,20 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) | |||
1107 | case 'R': | 976 | case 'R': |
1108 | if (compat20) { | 977 | if (compat20) { |
1109 | if (datafellows & SSH_BUG_NOREKEY) | 978 | if (datafellows & SSH_BUG_NOREKEY) |
1110 | logit("Server does not support re-keying"); | 979 | logit("Server does not " |
980 | "support re-keying"); | ||
1111 | else | 981 | else |
1112 | need_rekeying = 1; | 982 | need_rekeying = 1; |
1113 | } | 983 | } |
1114 | continue; | 984 | continue; |
1115 | 985 | ||
1116 | case '&': | 986 | case '&': |
987 | if (c && c->ctl_fd != -1) | ||
988 | goto noescape; | ||
1117 | /* | 989 | /* |
1118 | * Detach the program (continue to serve connections, | 990 | * Detach the program (continue to serve |
1119 | * but put in background and no more new connections). | 991 | * connections, but put in background and no |
992 | * more new connections). | ||
1120 | */ | 993 | */ |
1121 | /* Restore tty modes. */ | 994 | /* Restore tty modes. */ |
1122 | leave_raw_mode(); | 995 | leave_raw_mode(); |
@@ -1145,9 +1018,9 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) | |||
1145 | return -1; | 1018 | return -1; |
1146 | } else if (!stdin_eof) { | 1019 | } else if (!stdin_eof) { |
1147 | /* | 1020 | /* |
1148 | * Sending SSH_CMSG_EOF alone does not always appear | 1021 | * Sending SSH_CMSG_EOF alone does not |
1149 | * to be enough. So we try to send an EOF character | 1022 | * always appear to be enough. So we |
1150 | * first. | 1023 | * try to send an EOF character first. |
1151 | */ | 1024 | */ |
1152 | packet_start(SSH_CMSG_STDIN_DATA); | 1025 | packet_start(SSH_CMSG_STDIN_DATA); |
1153 | packet_put_string("\004", 1); | 1026 | packet_put_string("\004", 1); |
@@ -1162,27 +1035,50 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) | |||
1162 | continue; | 1035 | continue; |
1163 | 1036 | ||
1164 | case '?': | 1037 | case '?': |
1165 | snprintf(string, sizeof string, | 1038 | if (c && c->ctl_fd != -1) { |
1039 | snprintf(string, sizeof string, | ||
1166 | "%c?\r\n\ | 1040 | "%c?\r\n\ |
1167 | Supported escape sequences:\r\n\ | 1041 | Supported escape sequences:\r\n\ |
1168 | %c. - terminate connection\r\n\ | 1042 | %c. - terminate session\r\n\ |
1169 | %cB - send a BREAK to the remote system\r\n\ | 1043 | %cB - send a BREAK to the remote system\r\n\ |
1170 | %cC - open a command line\r\n\ | 1044 | %cC - open a command line\r\n\ |
1171 | %cR - Request rekey (SSH protocol 2 only)\r\n\ | 1045 | %cR - Request rekey (SSH protocol 2 only)\r\n\ |
1172 | %c^Z - suspend ssh\r\n\ | 1046 | %c# - list forwarded connections\r\n\ |
1173 | %c# - list forwarded connections\r\n\ | 1047 | %c? - this message\r\n\ |
1174 | %c& - background ssh (when waiting for connections to terminate)\r\n\ | 1048 | %c%c - send the escape character by typing it twice\r\n\ |
1175 | %c? - this message\r\n\ | ||
1176 | %c%c - send the escape character by typing it twice\r\n\ | ||
1177 | (Note that escapes are only recognized immediately after newline.)\r\n", | 1049 | (Note that escapes are only recognized immediately after newline.)\r\n", |
1178 | escape_char, escape_char, escape_char, escape_char, | 1050 | escape_char, escape_char, |
1179 | escape_char, escape_char, escape_char, escape_char, | 1051 | escape_char, escape_char, |
1180 | escape_char, escape_char, escape_char); | 1052 | escape_char, escape_char, |
1053 | escape_char, escape_char, | ||
1054 | escape_char); | ||
1055 | } else { | ||
1056 | snprintf(string, sizeof string, | ||
1057 | "%c?\r\n\ | ||
1058 | Supported escape sequences:\r\n\ | ||
1059 | %c. - terminate connection (and any multiplexed sessions)\r\n\ | ||
1060 | %cB - send a BREAK to the remote system\r\n\ | ||
1061 | %cC - open a command line\r\n\ | ||
1062 | %cR - Request rekey (SSH protocol 2 only)\r\n\ | ||
1063 | %c^Z - suspend ssh\r\n\ | ||
1064 | %c# - list forwarded connections\r\n\ | ||
1065 | %c& - background ssh (when waiting for connections to terminate)\r\n\ | ||
1066 | %c? - this message\r\n\ | ||
1067 | %c%c - send the escape character by typing it twice\r\n\ | ||
1068 | (Note that escapes are only recognized immediately after newline.)\r\n", | ||
1069 | escape_char, escape_char, | ||
1070 | escape_char, escape_char, | ||
1071 | escape_char, escape_char, | ||
1072 | escape_char, escape_char, | ||
1073 | escape_char, escape_char, | ||
1074 | escape_char); | ||
1075 | } | ||
1181 | buffer_append(berr, string, strlen(string)); | 1076 | buffer_append(berr, string, strlen(string)); |
1182 | continue; | 1077 | continue; |
1183 | 1078 | ||
1184 | case '#': | 1079 | case '#': |
1185 | snprintf(string, sizeof string, "%c#\r\n", escape_char); | 1080 | snprintf(string, sizeof string, "%c#\r\n", |
1081 | escape_char); | ||
1186 | buffer_append(berr, string, strlen(string)); | 1082 | buffer_append(berr, string, strlen(string)); |
1187 | s = channel_open_message(); | 1083 | s = channel_open_message(); |
1188 | buffer_append(berr, s, strlen(s)); | 1084 | buffer_append(berr, s, strlen(s)); |
@@ -1203,12 +1099,15 @@ Supported escape sequences:\r\n\ | |||
1203 | } | 1099 | } |
1204 | } else { | 1100 | } else { |
1205 | /* | 1101 | /* |
1206 | * The previous character was not an escape char. Check if this | 1102 | * The previous character was not an escape char. |
1207 | * is an escape. | 1103 | * Check if this is an escape. |
1208 | */ | 1104 | */ |
1209 | if (last_was_cr && ch == escape_char) { | 1105 | if (last_was_cr && ch == escape_char) { |
1210 | /* It is. Set the flag and continue to next character. */ | 1106 | /* |
1211 | escape_pending = 1; | 1107 | * It is. Set the flag and continue to |
1108 | * next character. | ||
1109 | */ | ||
1110 | *escape_pendingp = 1; | ||
1212 | continue; | 1111 | continue; |
1213 | } | 1112 | } |
1214 | } | 1113 | } |
@@ -1234,7 +1133,8 @@ client_process_input(fd_set *readset) | |||
1234 | if (FD_ISSET(fileno(stdin), readset)) { | 1133 | if (FD_ISSET(fileno(stdin), readset)) { |
1235 | /* Read as much as possible. */ | 1134 | /* Read as much as possible. */ |
1236 | len = read(fileno(stdin), buf, sizeof(buf)); | 1135 | len = read(fileno(stdin), buf, sizeof(buf)); |
1237 | if (len < 0 && (errno == EAGAIN || errno == EINTR)) | 1136 | if (len < 0 && |
1137 | (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) | ||
1238 | return; /* we'll try again later */ | 1138 | return; /* we'll try again later */ |
1239 | if (len <= 0) { | 1139 | if (len <= 0) { |
1240 | /* | 1140 | /* |
@@ -1243,7 +1143,8 @@ client_process_input(fd_set *readset) | |||
1243 | * if it was an error condition. | 1143 | * if it was an error condition. |
1244 | */ | 1144 | */ |
1245 | if (len < 0) { | 1145 | if (len < 0) { |
1246 | snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); | 1146 | snprintf(buf, sizeof buf, "read: %.100s\r\n", |
1147 | strerror(errno)); | ||
1247 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 1148 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
1248 | } | 1149 | } |
1249 | /* Mark that we have seen EOF. */ | 1150 | /* Mark that we have seen EOF. */ |
@@ -1259,7 +1160,7 @@ client_process_input(fd_set *readset) | |||
1259 | packet_start(SSH_CMSG_EOF); | 1160 | packet_start(SSH_CMSG_EOF); |
1260 | packet_send(); | 1161 | packet_send(); |
1261 | } | 1162 | } |
1262 | } else if (escape_char == SSH_ESCAPECHAR_NONE) { | 1163 | } else if (escape_char1 == SSH_ESCAPECHAR_NONE) { |
1263 | /* | 1164 | /* |
1264 | * Normal successful read, and no escape character. | 1165 | * Normal successful read, and no escape character. |
1265 | * Just append the data to buffer. | 1166 | * Just append the data to buffer. |
@@ -1267,11 +1168,12 @@ client_process_input(fd_set *readset) | |||
1267 | buffer_append(&stdin_buffer, buf, len); | 1168 | buffer_append(&stdin_buffer, buf, len); |
1268 | } else { | 1169 | } else { |
1269 | /* | 1170 | /* |
1270 | * Normal, successful read. But we have an escape character | 1171 | * Normal, successful read. But we have an escape |
1271 | * and have to process the characters one by one. | 1172 | * character and have to process the characters one |
1173 | * by one. | ||
1272 | */ | 1174 | */ |
1273 | if (process_escapes(&stdin_buffer, &stdout_buffer, | 1175 | if (process_escapes(NULL, &stdin_buffer, |
1274 | &stderr_buffer, buf, len) == -1) | 1176 | &stdout_buffer, &stderr_buffer, buf, len) == -1) |
1275 | return; | 1177 | return; |
1276 | } | 1178 | } |
1277 | } | 1179 | } |
@@ -1289,14 +1191,16 @@ client_process_output(fd_set *writeset) | |||
1289 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), | 1191 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), |
1290 | buffer_len(&stdout_buffer)); | 1192 | buffer_len(&stdout_buffer)); |
1291 | if (len <= 0) { | 1193 | if (len <= 0) { |
1292 | if (errno == EINTR || errno == EAGAIN) | 1194 | if (errno == EINTR || errno == EAGAIN || |
1195 | errno == EWOULDBLOCK) | ||
1293 | len = 0; | 1196 | len = 0; |
1294 | else { | 1197 | else { |
1295 | /* | 1198 | /* |
1296 | * An error or EOF was encountered. Put an | 1199 | * An error or EOF was encountered. Put an |
1297 | * error message to stderr buffer. | 1200 | * error message to stderr buffer. |
1298 | */ | 1201 | */ |
1299 | snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); | 1202 | snprintf(buf, sizeof buf, |
1203 | "write stdout: %.50s\r\n", strerror(errno)); | ||
1300 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 1204 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
1301 | quit_pending = 1; | 1205 | quit_pending = 1; |
1302 | return; | 1206 | return; |
@@ -1304,7 +1208,6 @@ client_process_output(fd_set *writeset) | |||
1304 | } | 1208 | } |
1305 | /* Consume printed data from the buffer. */ | 1209 | /* Consume printed data from the buffer. */ |
1306 | buffer_consume(&stdout_buffer, len); | 1210 | buffer_consume(&stdout_buffer, len); |
1307 | stdout_bytes += len; | ||
1308 | } | 1211 | } |
1309 | /* Write buffered output to stderr. */ | 1212 | /* Write buffered output to stderr. */ |
1310 | if (FD_ISSET(fileno(stderr), writeset)) { | 1213 | if (FD_ISSET(fileno(stderr), writeset)) { |
@@ -1312,17 +1215,20 @@ client_process_output(fd_set *writeset) | |||
1312 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), | 1215 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), |
1313 | buffer_len(&stderr_buffer)); | 1216 | buffer_len(&stderr_buffer)); |
1314 | if (len <= 0) { | 1217 | if (len <= 0) { |
1315 | if (errno == EINTR || errno == EAGAIN) | 1218 | if (errno == EINTR || errno == EAGAIN || |
1219 | errno == EWOULDBLOCK) | ||
1316 | len = 0; | 1220 | len = 0; |
1317 | else { | 1221 | else { |
1318 | /* EOF or error, but can't even print error message. */ | 1222 | /* |
1223 | * EOF or error, but can't even print | ||
1224 | * error message. | ||
1225 | */ | ||
1319 | quit_pending = 1; | 1226 | quit_pending = 1; |
1320 | return; | 1227 | return; |
1321 | } | 1228 | } |
1322 | } | 1229 | } |
1323 | /* Consume printed characters from the buffer. */ | 1230 | /* Consume printed characters from the buffer. */ |
1324 | buffer_consume(&stderr_buffer, len); | 1231 | buffer_consume(&stderr_buffer, len); |
1325 | stderr_bytes += len; | ||
1326 | } | 1232 | } |
1327 | } | 1233 | } |
1328 | 1234 | ||
@@ -1341,16 +1247,39 @@ client_process_output(fd_set *writeset) | |||
1341 | static void | 1247 | static void |
1342 | client_process_buffered_input_packets(void) | 1248 | client_process_buffered_input_packets(void) |
1343 | { | 1249 | { |
1344 | dispatch_run(DISPATCH_NONBLOCK, &quit_pending, compat20 ? xxx_kex : NULL); | 1250 | dispatch_run(DISPATCH_NONBLOCK, &quit_pending, |
1251 | compat20 ? xxx_kex : NULL); | ||
1345 | } | 1252 | } |
1346 | 1253 | ||
1347 | /* scan buf[] for '~' before sending data to the peer */ | 1254 | /* scan buf[] for '~' before sending data to the peer */ |
1348 | 1255 | ||
1349 | static int | 1256 | /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ |
1350 | simple_escape_filter(Channel *c, char *buf, int len) | 1257 | void * |
1258 | client_new_escape_filter_ctx(int escape_char) | ||
1259 | { | ||
1260 | struct escape_filter_ctx *ret; | ||
1261 | |||
1262 | ret = xmalloc(sizeof(*ret)); | ||
1263 | ret->escape_pending = 0; | ||
1264 | ret->escape_char = escape_char; | ||
1265 | return (void *)ret; | ||
1266 | } | ||
1267 | |||
1268 | /* Free the escape filter context on channel free */ | ||
1269 | void | ||
1270 | client_filter_cleanup(int cid, void *ctx) | ||
1271 | { | ||
1272 | xfree(ctx); | ||
1273 | } | ||
1274 | |||
1275 | int | ||
1276 | client_simple_escape_filter(Channel *c, char *buf, int len) | ||
1351 | { | 1277 | { |
1352 | /* XXX we assume c->extended is writeable */ | 1278 | if (c->extended_usage != CHAN_EXTENDED_WRITE) |
1353 | return process_escapes(&c->input, &c->output, &c->extended, buf, len); | 1279 | return 0; |
1280 | |||
1281 | return process_escapes(c, &c->input, &c->output, &c->extended, | ||
1282 | buf, len); | ||
1354 | } | 1283 | } |
1355 | 1284 | ||
1356 | static void | 1285 | static void |
@@ -1374,6 +1303,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1374 | fd_set *readset = NULL, *writeset = NULL; | 1303 | fd_set *readset = NULL, *writeset = NULL; |
1375 | double start_time, total_time; | 1304 | double start_time, total_time; |
1376 | int max_fd = 0, max_fd2 = 0, len, rekeying = 0; | 1305 | int max_fd = 0, max_fd2 = 0, len, rekeying = 0; |
1306 | u_int64_t ibytes, obytes; | ||
1377 | u_int nalloc = 0; | 1307 | u_int nalloc = 0; |
1378 | char buf[100]; | 1308 | char buf[100]; |
1379 | 1309 | ||
@@ -1382,7 +1312,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1382 | start_time = get_current_time(); | 1312 | start_time = get_current_time(); |
1383 | 1313 | ||
1384 | /* Initialize variables. */ | 1314 | /* Initialize variables. */ |
1385 | escape_pending = 0; | 1315 | escape_pending1 = 0; |
1386 | last_was_cr = 1; | 1316 | last_was_cr = 1; |
1387 | exit_status = -1; | 1317 | exit_status = -1; |
1388 | stdin_eof = 0; | 1318 | stdin_eof = 0; |
@@ -1390,8 +1320,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1390 | connection_in = packet_get_connection_in(); | 1320 | connection_in = packet_get_connection_in(); |
1391 | connection_out = packet_get_connection_out(); | 1321 | connection_out = packet_get_connection_out(); |
1392 | max_fd = MAX(connection_in, connection_out); | 1322 | max_fd = MAX(connection_in, connection_out); |
1393 | if (control_fd != -1) | 1323 | if (muxserver_sock != -1) |
1394 | max_fd = MAX(max_fd, control_fd); | 1324 | max_fd = MAX(max_fd, muxserver_sock); |
1395 | 1325 | ||
1396 | if (!compat20) { | 1326 | if (!compat20) { |
1397 | /* enable nonblocking unless tty */ | 1327 | /* enable nonblocking unless tty */ |
@@ -1405,11 +1335,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1405 | max_fd = MAX(max_fd, fileno(stdout)); | 1335 | max_fd = MAX(max_fd, fileno(stdout)); |
1406 | max_fd = MAX(max_fd, fileno(stderr)); | 1336 | max_fd = MAX(max_fd, fileno(stderr)); |
1407 | } | 1337 | } |
1408 | stdin_bytes = 0; | ||
1409 | stdout_bytes = 0; | ||
1410 | stderr_bytes = 0; | ||
1411 | quit_pending = 0; | 1338 | quit_pending = 0; |
1412 | escape_char = escape_char_arg; | 1339 | escape_char1 = escape_char_arg; |
1413 | 1340 | ||
1414 | /* Initialize buffers. */ | 1341 | /* Initialize buffers. */ |
1415 | buffer_init(&stdin_buffer); | 1342 | buffer_init(&stdin_buffer); |
@@ -1437,9 +1364,11 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1437 | 1364 | ||
1438 | if (compat20) { | 1365 | if (compat20) { |
1439 | session_ident = ssh2_chan_id; | 1366 | session_ident = ssh2_chan_id; |
1440 | if (escape_char != SSH_ESCAPECHAR_NONE) | 1367 | if (escape_char_arg != SSH_ESCAPECHAR_NONE) |
1441 | channel_register_filter(session_ident, | 1368 | channel_register_filter(session_ident, |
1442 | simple_escape_filter, NULL); | 1369 | client_simple_escape_filter, NULL, |
1370 | client_filter_cleanup, | ||
1371 | client_new_escape_filter_ctx(escape_char_arg)); | ||
1443 | if (session_ident != -1) | 1372 | if (session_ident != -1) |
1444 | channel_register_cleanup(session_ident, | 1373 | channel_register_cleanup(session_ident, |
1445 | client_channel_closed, 0); | 1374 | client_channel_closed, 0); |
@@ -1511,7 +1440,10 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1511 | client_process_net_input(readset); | 1440 | client_process_net_input(readset); |
1512 | 1441 | ||
1513 | /* Accept control connections. */ | 1442 | /* Accept control connections. */ |
1514 | client_process_control(readset); | 1443 | if (muxserver_sock != -1 &&FD_ISSET(muxserver_sock, readset)) { |
1444 | if (muxserver_accept_control()) | ||
1445 | quit_pending = 1; | ||
1446 | } | ||
1515 | 1447 | ||
1516 | if (quit_pending) | 1448 | if (quit_pending) |
1517 | break; | 1449 | break; |
@@ -1526,7 +1458,10 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1526 | client_process_output(writeset); | 1458 | client_process_output(writeset); |
1527 | } | 1459 | } |
1528 | 1460 | ||
1529 | /* Send as much buffered packet data as possible to the sender. */ | 1461 | /* |
1462 | * Send as much buffered packet data as possible to the | ||
1463 | * sender. | ||
1464 | */ | ||
1530 | if (FD_ISSET(connection_out, writeset)) | 1465 | if (FD_ISSET(connection_out, writeset)) |
1531 | packet_write_poll(); | 1466 | packet_write_poll(); |
1532 | } | 1467 | } |
@@ -1573,7 +1508,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1573 | * that the connection has been closed. | 1508 | * that the connection has been closed. |
1574 | */ | 1509 | */ |
1575 | if (have_pty && options.log_level > SYSLOG_LEVEL_QUIET) { | 1510 | if (have_pty && options.log_level > SYSLOG_LEVEL_QUIET) { |
1576 | snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); | 1511 | snprintf(buf, sizeof buf, |
1512 | "Connection to %.64s closed.\r\n", host); | ||
1577 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 1513 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
1578 | } | 1514 | } |
1579 | 1515 | ||
@@ -1586,7 +1522,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1586 | break; | 1522 | break; |
1587 | } | 1523 | } |
1588 | buffer_consume(&stdout_buffer, len); | 1524 | buffer_consume(&stdout_buffer, len); |
1589 | stdout_bytes += len; | ||
1590 | } | 1525 | } |
1591 | 1526 | ||
1592 | /* Output any buffered data for stderr. */ | 1527 | /* Output any buffered data for stderr. */ |
@@ -1598,7 +1533,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1598 | break; | 1533 | break; |
1599 | } | 1534 | } |
1600 | buffer_consume(&stderr_buffer, len); | 1535 | buffer_consume(&stderr_buffer, len); |
1601 | stderr_bytes += len; | ||
1602 | } | 1536 | } |
1603 | 1537 | ||
1604 | /* Clear and free any buffers. */ | 1538 | /* Clear and free any buffers. */ |
@@ -1609,13 +1543,13 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1609 | 1543 | ||
1610 | /* Report bytes transferred, and transfer rates. */ | 1544 | /* Report bytes transferred, and transfer rates. */ |
1611 | total_time = get_current_time() - start_time; | 1545 | total_time = get_current_time() - start_time; |
1612 | debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", | 1546 | packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); |
1613 | stdin_bytes, stdout_bytes, stderr_bytes, total_time); | 1547 | packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); |
1548 | verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", | ||
1549 | obytes, ibytes, total_time); | ||
1614 | if (total_time > 0) | 1550 | if (total_time > 0) |
1615 | debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", | 1551 | verbose("Bytes per second: sent %.1f, received %.1f", |
1616 | stdin_bytes / total_time, stdout_bytes / total_time, | 1552 | obytes / total_time, ibytes / total_time); |
1617 | stderr_bytes / total_time); | ||
1618 | |||
1619 | /* Return the exit status of the program. */ | 1553 | /* Return the exit status of the program. */ |
1620 | debug("Exit status %d", exit_status); | 1554 | debug("Exit status %d", exit_status); |
1621 | return exit_status; | 1555 | return exit_status; |
@@ -1706,7 +1640,6 @@ client_request_forwarded_tcpip(const char *request_type, int rchan) | |||
1706 | Channel *c = NULL; | 1640 | Channel *c = NULL; |
1707 | char *listen_address, *originator_address; | 1641 | char *listen_address, *originator_address; |
1708 | int listen_port, originator_port; | 1642 | int listen_port, originator_port; |
1709 | int sock; | ||
1710 | 1643 | ||
1711 | /* Get rest of the packet */ | 1644 | /* Get rest of the packet */ |
1712 | listen_address = packet_get_string(NULL); | 1645 | listen_address = packet_get_string(NULL); |
@@ -1715,19 +1648,13 @@ client_request_forwarded_tcpip(const char *request_type, int rchan) | |||
1715 | originator_port = packet_get_int(); | 1648 | originator_port = packet_get_int(); |
1716 | packet_check_eom(); | 1649 | packet_check_eom(); |
1717 | 1650 | ||
1718 | debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d", | 1651 | debug("client_request_forwarded_tcpip: listen %s port %d, " |
1719 | listen_address, listen_port, originator_address, originator_port); | 1652 | "originator %s port %d", listen_address, listen_port, |
1653 | originator_address, originator_port); | ||
1654 | |||
1655 | c = channel_connect_by_listen_address(listen_port, | ||
1656 | "forwarded-tcpip", originator_address); | ||
1720 | 1657 | ||
1721 | sock = channel_connect_by_listen_address(listen_port); | ||
1722 | if (sock < 0) { | ||
1723 | xfree(originator_address); | ||
1724 | xfree(listen_address); | ||
1725 | return NULL; | ||
1726 | } | ||
1727 | c = channel_new("forwarded-tcpip", | ||
1728 | SSH_CHANNEL_CONNECTING, sock, sock, -1, | ||
1729 | CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, | ||
1730 | originator_address, 1); | ||
1731 | xfree(originator_address); | 1658 | xfree(originator_address); |
1732 | xfree(listen_address); | 1659 | xfree(listen_address); |
1733 | return c; | 1660 | return c; |
@@ -1743,7 +1670,8 @@ client_request_x11(const char *request_type, int rchan) | |||
1743 | 1670 | ||
1744 | if (!options.forward_x11) { | 1671 | if (!options.forward_x11) { |
1745 | error("Warning: ssh server tried X11 forwarding."); | 1672 | error("Warning: ssh server tried X11 forwarding."); |
1746 | error("Warning: this is probably a break-in attempt by a malicious server."); | 1673 | error("Warning: this is probably a break-in attempt by a " |
1674 | "malicious server."); | ||
1747 | return NULL; | 1675 | return NULL; |
1748 | } | 1676 | } |
1749 | originator = packet_get_string(NULL); | 1677 | originator = packet_get_string(NULL); |
@@ -1776,7 +1704,8 @@ client_request_agent(const char *request_type, int rchan) | |||
1776 | 1704 | ||
1777 | if (!options.forward_agent) { | 1705 | if (!options.forward_agent) { |
1778 | error("Warning: ssh server tried agent forwarding."); | 1706 | error("Warning: ssh server tried agent forwarding."); |
1779 | error("Warning: this is probably a break-in attempt by a malicious server."); | 1707 | error("Warning: this is probably a break-in attempt by a " |
1708 | "malicious server."); | ||
1780 | return NULL; | 1709 | return NULL; |
1781 | } | 1710 | } |
1782 | sock = ssh_get_authentication_socket(); | 1711 | sock = ssh_get_authentication_socket(); |
@@ -1819,7 +1748,7 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) | |||
1819 | #if defined(SSH_TUN_FILTER) | 1748 | #if defined(SSH_TUN_FILTER) |
1820 | if (options.tun_open == SSH_TUNMODE_POINTOPOINT) | 1749 | if (options.tun_open == SSH_TUNMODE_POINTOPOINT) |
1821 | channel_register_filter(c->self, sys_tun_infilter, | 1750 | channel_register_filter(c->self, sys_tun_infilter, |
1822 | sys_tun_outfilter); | 1751 | sys_tun_outfilter, NULL, NULL); |
1823 | #endif | 1752 | #endif |
1824 | 1753 | ||
1825 | packet_start(SSH2_MSG_CHANNEL_OPEN); | 1754 | packet_start(SSH2_MSG_CHANNEL_OPEN); |
@@ -1902,7 +1831,11 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) | |||
1902 | if (id == -1) { | 1831 | if (id == -1) { |
1903 | error("client_input_channel_req: request for channel -1"); | 1832 | error("client_input_channel_req: request for channel -1"); |
1904 | } else if ((c = channel_lookup(id)) == NULL) { | 1833 | } else if ((c = channel_lookup(id)) == NULL) { |
1905 | error("client_input_channel_req: channel %d: unknown channel", id); | 1834 | error("client_input_channel_req: channel %d: " |
1835 | "unknown channel", id); | ||
1836 | } else if (strcmp(rtype, "eow@openssh.com") == 0) { | ||
1837 | packet_check_eom(); | ||
1838 | chan_rcvd_eow(c); | ||
1906 | } else if (strcmp(rtype, "exit-status") == 0) { | 1839 | } else if (strcmp(rtype, "exit-status") == 0) { |
1907 | exitval = packet_get_int(); | 1840 | exitval = packet_get_int(); |
1908 | if (id == session_ident) { | 1841 | if (id == session_ident) { |
@@ -1947,8 +1880,7 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt) | |||
1947 | 1880 | ||
1948 | void | 1881 | void |
1949 | client_session2_setup(int id, int want_tty, int want_subsystem, | 1882 | client_session2_setup(int id, int want_tty, int want_subsystem, |
1950 | const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env, | 1883 | const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) |
1951 | dispatch_fn *subsys_repl) | ||
1952 | { | 1884 | { |
1953 | int len; | 1885 | int len; |
1954 | Channel *c = NULL; | 1886 | Channel *c = NULL; |
@@ -1960,20 +1892,21 @@ client_session2_setup(int id, int want_tty, int want_subsystem, | |||
1960 | 1892 | ||
1961 | if (want_tty) { | 1893 | if (want_tty) { |
1962 | struct winsize ws; | 1894 | struct winsize ws; |
1963 | struct termios tio; | ||
1964 | 1895 | ||
1965 | /* Store window size in the packet. */ | 1896 | /* Store window size in the packet. */ |
1966 | if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) | 1897 | if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) |
1967 | memset(&ws, 0, sizeof(ws)); | 1898 | memset(&ws, 0, sizeof(ws)); |
1968 | 1899 | ||
1969 | channel_request_start(id, "pty-req", 0); | 1900 | channel_request_start(id, "pty-req", 1); |
1901 | client_expect_confirm(id, "PTY allocation", 0); | ||
1970 | packet_put_cstring(term != NULL ? term : ""); | 1902 | packet_put_cstring(term != NULL ? term : ""); |
1971 | packet_put_int((u_int)ws.ws_col); | 1903 | packet_put_int((u_int)ws.ws_col); |
1972 | packet_put_int((u_int)ws.ws_row); | 1904 | packet_put_int((u_int)ws.ws_row); |
1973 | packet_put_int((u_int)ws.ws_xpixel); | 1905 | packet_put_int((u_int)ws.ws_xpixel); |
1974 | packet_put_int((u_int)ws.ws_ypixel); | 1906 | packet_put_int((u_int)ws.ws_ypixel); |
1975 | tio = get_saved_tio(); | 1907 | if (tiop == NULL) |
1976 | tty_make_modes(-1, tiop != NULL ? tiop : &tio); | 1908 | tiop = get_saved_tio(); |
1909 | tty_make_modes(-1, tiop); | ||
1977 | packet_send(); | 1910 | packet_send(); |
1978 | /* XXX wait for reply */ | 1911 | /* XXX wait for reply */ |
1979 | c->client_tty = 1; | 1912 | c->client_tty = 1; |
@@ -2021,22 +1954,21 @@ client_session2_setup(int id, int want_tty, int want_subsystem, | |||
2021 | if (len > 900) | 1954 | if (len > 900) |
2022 | len = 900; | 1955 | len = 900; |
2023 | if (want_subsystem) { | 1956 | if (want_subsystem) { |
2024 | debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd)); | 1957 | debug("Sending subsystem: %.*s", |
2025 | channel_request_start(id, "subsystem", subsys_repl != NULL); | 1958 | len, (u_char*)buffer_ptr(cmd)); |
2026 | if (subsys_repl != NULL) { | 1959 | channel_request_start(id, "subsystem", 1); |
2027 | /* register callback for reply */ | 1960 | client_expect_confirm(id, "subsystem", 1); |
2028 | /* XXX we assume that client_loop has already been called */ | ||
2029 | dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl); | ||
2030 | dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl); | ||
2031 | } | ||
2032 | } else { | 1961 | } else { |
2033 | debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd)); | 1962 | debug("Sending command: %.*s", |
2034 | channel_request_start(id, "exec", 0); | 1963 | len, (u_char*)buffer_ptr(cmd)); |
1964 | channel_request_start(id, "exec", 1); | ||
1965 | client_expect_confirm(id, "exec", 1); | ||
2035 | } | 1966 | } |
2036 | packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); | 1967 | packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); |
2037 | packet_send(); | 1968 | packet_send(); |
2038 | } else { | 1969 | } else { |
2039 | channel_request_start(id, "shell", 0); | 1970 | channel_request_start(id, "shell", 1); |
1971 | client_expect_confirm(id, "shell", 1); | ||
2040 | packet_send(); | 1972 | packet_send(); |
2041 | } | 1973 | } |
2042 | } | 1974 | } |
@@ -2055,6 +1987,8 @@ client_init_dispatch_20(void) | |||
2055 | dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); | 1987 | dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); |
2056 | dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); | 1988 | dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); |
2057 | dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); | 1989 | dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); |
1990 | dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); | ||
1991 | dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); | ||
2058 | dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); | 1992 | dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); |
2059 | 1993 | ||
2060 | /* rekeying */ | 1994 | /* rekeying */ |
@@ -2064,6 +1998,7 @@ client_init_dispatch_20(void) | |||
2064 | dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); | 1998 | dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); |
2065 | dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); | 1999 | dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); |
2066 | } | 2000 | } |
2001 | |||
2067 | static void | 2002 | static void |
2068 | client_init_dispatch_13(void) | 2003 | client_init_dispatch_13(void) |
2069 | { | 2004 | { |
@@ -2083,6 +2018,7 @@ client_init_dispatch_13(void) | |||
2083 | dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? | 2018 | dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? |
2084 | &x11_input_open : &deny_input_open); | 2019 | &x11_input_open : &deny_input_open); |
2085 | } | 2020 | } |
2021 | |||
2086 | static void | 2022 | static void |
2087 | client_init_dispatch_15(void) | 2023 | client_init_dispatch_15(void) |
2088 | { | 2024 | { |
@@ -2090,6 +2026,7 @@ client_init_dispatch_15(void) | |||
2090 | dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); | 2026 | dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); |
2091 | dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); | 2027 | dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); |
2092 | } | 2028 | } |
2029 | |||
2093 | static void | 2030 | static void |
2094 | client_init_dispatch(void) | 2031 | client_init_dispatch(void) |
2095 | { | 2032 | { |
@@ -2107,7 +2044,7 @@ cleanup_exit(int i) | |||
2107 | { | 2044 | { |
2108 | leave_raw_mode(); | 2045 | leave_raw_mode(); |
2109 | leave_non_blocking(); | 2046 | leave_non_blocking(); |
2110 | if (options.control_path != NULL && control_fd != -1) | 2047 | if (options.control_path != NULL && muxserver_sock != -1) |
2111 | unlink(options.control_path); | 2048 | unlink(options.control_path); |
2112 | _exit(i); | 2049 | _exit(i); |
2113 | } | 2050 | } |