Vendor import of OpenSSH 9.5p1

This commit is contained in:
Ed Maste 2023-10-04 08:06:41 -04:00
parent 78f30535bc
commit b3ced7f268
57 changed files with 1712 additions and 1634 deletions

View File

@ -6,6 +6,10 @@ master :
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh)
[![Coverity Status](https://scan.coverity.com/projects/21341/badge.svg)](https://scan.coverity.com/projects/openssh-portable) [![Coverity Status](https://scan.coverity.com/projects/21341/badge.svg)](https://scan.coverity.com/projects/openssh-portable)
9.4 :
[![C/C++ CI](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml/badge.svg?branch=V_9_4)](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml?query=branch:V_9_4)
[![C/C++ CI self-hosted](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml/badge.svg?branch=V_9_4)](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml?query=branch:V_9_4)
9.3 : 9.3 :
[![C/C++ CI](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml/badge.svg?branch=V_9_3)](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml?query=branch:V_9_3) [![C/C++ CI](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml/badge.svg?branch=V_9_3)](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml?query=branch:V_9_3)
[![C/C++ CI self-hosted](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml/badge.svg?branch=V_9_3)](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml?query=branch:V_9_3) [![C/C++ CI self-hosted](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml/badge.svg?branch=V_9_3)](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml?query=branch:V_9_3)

11
.github/configs vendored
View File

@ -30,6 +30,13 @@ case "$config" in
default|sol64) default|sol64)
;; ;;
c89) c89)
# If we don't have LLONG_MAX, configure will figure out that it can
# get it by setting -std=gnu99, at which point we won't be testing
# C89 any more. To avoid this, feed it in via CFLAGS.
llong_max=`gcc -E -dM - </dev/null | \
awk '$2=="__LONG_LONG_MAX__"{print $3}'`
CPPFLAGS="-DLLONG_MAX=${llong_max}"
CC="gcc" CC="gcc"
CFLAGS="-Wall -std=c89 -pedantic -Werror=vla" CFLAGS="-Wall -std=c89 -pedantic -Werror=vla"
CONFIGFLAGS="--without-zlib" CONFIGFLAGS="--without-zlib"
@ -205,6 +212,10 @@ case "$config" in
;; ;;
esac esac
;; ;;
zlib-develop)
INSTALL_ZLIB=develop
CONFIGFLAGS="--with-zlib=/opt/zlib --with-rpath=-Wl,-rpath,"
;;
*) *)
echo "Unknown configuration $config" echo "Unknown configuration $config"
exit 1 exit 1

8
.github/setup_ci.sh vendored
View File

@ -133,6 +133,8 @@ for TARGET in $TARGETS; do
valgrind*) valgrind*)
PACKAGES="$PACKAGES valgrind" PACKAGES="$PACKAGES valgrind"
;; ;;
zlib-*)
;;
*) echo "Invalid option '${TARGET}'" *) echo "Invalid option '${TARGET}'"
exit 1 exit 1
;; ;;
@ -214,3 +216,9 @@ if [ ! -z "${INSTALL_BORINGSSL}" ]; then
cp ${HOME}/boringssl/build/crypto/libcrypto.a /opt/boringssl/lib && cp ${HOME}/boringssl/build/crypto/libcrypto.a /opt/boringssl/lib &&
cp -r ${HOME}/boringssl/include /opt/boringssl) cp -r ${HOME}/boringssl/include /opt/boringssl)
fi fi
if [ ! -z "${INSTALL_ZLIB}" ]; then
(cd ${HOME} && git clone https://github.com/madler/zlib.git &&
cd ${HOME}/zlib && ./configure && make &&
sudo make install prefix=/opt/zlib)
fi

View File

@ -73,6 +73,7 @@ jobs:
- { target: ubuntu-latest, config: openssl-3.1.0 } - { target: ubuntu-latest, config: openssl-3.1.0 }
- { target: ubuntu-latest, config: openssl-1.1.1_stable } - { target: ubuntu-latest, config: openssl-1.1.1_stable }
- { target: ubuntu-latest, config: openssl-3.0 } # stable branch - { target: ubuntu-latest, config: openssl-3.0 } # stable branch
- { target: ubuntu-latest, config: zlib-develop }
- { target: ubuntu-22.04, config: pam } - { target: ubuntu-22.04, config: pam }
- { target: ubuntu-22.04, config: krb5 } - { target: ubuntu-22.04, config: krb5 }
- { target: ubuntu-22.04, config: heimdal } - { target: ubuntu-22.04, config: heimdal }

View File

@ -40,6 +40,8 @@ jobs:
- obsd67 - obsd67
- obsd69 - obsd69
- obsd70 - obsd70
- obsd72
- obsd73
- obsdsnap - obsdsnap
- obsdsnap-i386 - obsdsnap-i386
- openindiana - openindiana
@ -76,6 +78,7 @@ jobs:
- { target: ARM64, config: default, host: ARM64 } - { target: ARM64, config: default, host: ARM64 }
- { target: ARM64, config: pam, host: ARM64 } - { target: ARM64, config: pam, host: ARM64 }
- { target: debian-riscv64, config: default, host: debian-riscv64 } - { target: debian-riscv64, config: default, host: debian-riscv64 }
- { target: obsd-arm64, config: default, host: obsd-arm64 }
- { target: openwrt-mips, config: default, host: openwrt-mips } - { target: openwrt-mips, config: default, host: openwrt-mips }
- { target: openwrt-mipsel, config: default, host: openwrt-mipsel } - { target: openwrt-mipsel, config: default, host: openwrt-mipsel }
steps: steps:

1611
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@ -104,6 +104,39 @@ http://git.libssh.org/users/aris/libssh.git/plain/doc/curve25519-sha256@libssh.o
This is identical to curve25519-sha256 as later published in RFC8731. This is identical to curve25519-sha256 as later published in RFC8731.
1.9 transport: ping facility
OpenSSH implements a transport level ping message SSH2_MSG_PING
and a corresponding SSH2_MSG_PONG reply.
#define SSH2_MSG_PING 192
#define SSH2_MSG_PONG 193
The ping message is simply:
byte SSH_MSG_PING
string data
The reply copies the data (which may be the empty string) from the
ping:
byte SSH_MSG_PONG
string data
Replies are sent in order. They are sent immediately except when rekeying
is in progress, in which case they are queued until rekeying completes.
The server advertises support for these messages using the
SSH2_MSG_EXT_INFO mechanism (RFC8308), with the following message:
string "ping@openssh.com"
string "0" (version)
The ping/reply message is implemented at the transport layer rather
than as a named global or channel request to allow pings with very
short packet lengths, which would not be possible with other
approaches.
2. Connection protocol changes 2. Connection protocol changes
2.1. connection: Channel write close extension "eow@openssh.com" 2.1. connection: Channel write close extension "eow@openssh.com"
@ -712,4 +745,4 @@ master instance and later clients.
OpenSSH extends the usual agent protocol. These changes are documented OpenSSH extends the usual agent protocol. These changes are documented
in the PROTOCOL.agent file. in the PROTOCOL.agent file.
$OpenBSD: PROTOCOL,v 1.48 2022/11/07 01:53:01 dtucker Exp $ $OpenBSD: PROTOCOL,v 1.49 2023/08/28 03:28:43 djm Exp $

View File

@ -1,5 +1,5 @@
The SSH agent protocol is described in The SSH agent protocol is described in
https://tools.ietf.org/html/draft-miller-ssh-agent-04 https://tools.ietf.org/html/draft-miller-ssh-agent
This file documents OpenSSH's extensions to the agent protocol. This file documents OpenSSH's extensions to the agent protocol.
@ -81,4 +81,4 @@ the constraint is:
This option is only valid for XMSS keys. This option is only valid for XMSS keys.
$OpenBSD: PROTOCOL.agent,v 1.19 2023/04/12 08:53:54 jsg Exp $ $OpenBSD: PROTOCOL.agent,v 1.20 2023/10/03 23:56:10 djm Exp $

2
README
View File

@ -1,4 +1,4 @@
See https://www.openssh.com/releasenotes.html#9.4p1 for the release See https://www.openssh.com/releasenotes.html#9.5p1 for the release
notes. notes.
Please read https://www.openssh.com/report.html for bug reporting Please read https://www.openssh.com/report.html for bug reporting

11
auth2.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2.c,v 1.166 2023/03/08 04:43:12 guenther Exp $ */ /* $OpenBSD: auth2.c,v 1.167 2023/08/28 09:48:11 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* *
@ -218,6 +218,7 @@ input_service_request(int type, u_int32_t seq, struct ssh *ssh)
} }
#define MIN_FAIL_DELAY_SECONDS 0.005 #define MIN_FAIL_DELAY_SECONDS 0.005
#define MAX_FAIL_DELAY_SECONDS 5.0
static double static double
user_specific_delay(const char *user) user_specific_delay(const char *user)
{ {
@ -243,6 +244,12 @@ ensure_minimum_time_since(double start, double seconds)
struct timespec ts; struct timespec ts;
double elapsed = monotime_double() - start, req = seconds, remain; double elapsed = monotime_double() - start, req = seconds, remain;
if (elapsed > MAX_FAIL_DELAY_SECONDS) {
debug3_f("elapsed %0.3lfms exceeded the max delay "
"requested %0.3lfms)", elapsed*1000, req*1000);
return;
}
/* if we've already passed the requested time, scale up */ /* if we've already passed the requested time, scale up */
while ((remain = seconds - elapsed) < 0.0) while ((remain = seconds - elapsed) < 0.0)
seconds *= 2; seconds *= 2;
@ -334,7 +341,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
debug2("input_userauth_request: try method %s", method); debug2("input_userauth_request: try method %s", method);
authenticated = m->userauth(ssh, method); authenticated = m->userauth(ssh, method);
} }
if (!authctxt->authenticated) if (!authctxt->authenticated && strcmp(method, "none") != 0)
ensure_minimum_time_since(tstart, ensure_minimum_time_since(tstart,
user_specific_delay(authctxt->user)); user_specific_delay(authctxt->user));
userauth_finish(ssh, authenticated, method, NULL); userauth_finish(ssh, authenticated, method, NULL);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: channels.c,v 1.432 2023/07/04 03:59:21 dlg Exp $ */ /* $OpenBSD: channels.c,v 1.433 2023/09/04 00:01:46 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -2890,8 +2890,9 @@ channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd)
/* /*
* Enqueue data for channels with open or draining c->input. * Enqueue data for channels with open or draining c->input.
* Returns non-zero if a packet was enqueued.
*/ */
static void static int
channel_output_poll_input_open(struct ssh *ssh, Channel *c) channel_output_poll_input_open(struct ssh *ssh, Channel *c)
{ {
size_t len, plen; size_t len, plen;
@ -2914,7 +2915,7 @@ channel_output_poll_input_open(struct ssh *ssh, Channel *c)
else else
chan_ibuf_empty(ssh, c); chan_ibuf_empty(ssh, c);
} }
return; return 0;
} }
if (!c->have_remote_id) if (!c->have_remote_id)
@ -2931,7 +2932,7 @@ channel_output_poll_input_open(struct ssh *ssh, Channel *c)
*/ */
if (plen > c->remote_window || plen > c->remote_maxpacket) { if (plen > c->remote_window || plen > c->remote_maxpacket) {
debug("channel %d: datagram too big", c->self); debug("channel %d: datagram too big", c->self);
return; return 0;
} }
/* Enqueue it */ /* Enqueue it */
if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
@ -2940,7 +2941,7 @@ channel_output_poll_input_open(struct ssh *ssh, Channel *c)
(r = sshpkt_send(ssh)) != 0) (r = sshpkt_send(ssh)) != 0)
fatal_fr(r, "channel %i: send datagram", c->self); fatal_fr(r, "channel %i: send datagram", c->self);
c->remote_window -= plen; c->remote_window -= plen;
return; return 1;
} }
/* Enqueue packet for buffered data. */ /* Enqueue packet for buffered data. */
@ -2949,7 +2950,7 @@ channel_output_poll_input_open(struct ssh *ssh, Channel *c)
if (len > c->remote_maxpacket) if (len > c->remote_maxpacket)
len = c->remote_maxpacket; len = c->remote_maxpacket;
if (len == 0) if (len == 0)
return; return 0;
if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
(r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
(r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 || (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 ||
@ -2958,19 +2959,21 @@ channel_output_poll_input_open(struct ssh *ssh, Channel *c)
if ((r = sshbuf_consume(c->input, len)) != 0) if ((r = sshbuf_consume(c->input, len)) != 0)
fatal_fr(r, "channel %i: consume", c->self); fatal_fr(r, "channel %i: consume", c->self);
c->remote_window -= len; c->remote_window -= len;
return 1;
} }
/* /*
* Enqueue data for channels with open c->extended in read mode. * Enqueue data for channels with open c->extended in read mode.
* Returns non-zero if a packet was enqueued.
*/ */
static void static int
channel_output_poll_extended_read(struct ssh *ssh, Channel *c) channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
{ {
size_t len; size_t len;
int r; int r;
if ((len = sshbuf_len(c->extended)) == 0) if ((len = sshbuf_len(c->extended)) == 0)
return; return 0;
debug2("channel %d: rwin %u elen %zu euse %d", c->self, debug2("channel %d: rwin %u elen %zu euse %d", c->self,
c->remote_window, sshbuf_len(c->extended), c->extended_usage); c->remote_window, sshbuf_len(c->extended), c->extended_usage);
@ -2979,7 +2982,7 @@ channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
if (len > c->remote_maxpacket) if (len > c->remote_maxpacket)
len = c->remote_maxpacket; len = c->remote_maxpacket;
if (len == 0) if (len == 0)
return; return 0;
if (!c->have_remote_id) if (!c->have_remote_id)
fatal_f("channel %d: no remote id", c->self); fatal_f("channel %d: no remote id", c->self);
if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 || if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 ||
@ -2992,15 +2995,20 @@ channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
fatal_fr(r, "channel %i: consume", c->self); fatal_fr(r, "channel %i: consume", c->self);
c->remote_window -= len; c->remote_window -= len;
debug2("channel %d: sent ext data %zu", c->self, len); debug2("channel %d: sent ext data %zu", c->self, len);
return 1;
} }
/* If there is data to send to the connection, enqueue some of it now. */ /*
void * If there is data to send to the connection, enqueue some of it now.
* Returns non-zero if data was enqueued.
*/
int
channel_output_poll(struct ssh *ssh) channel_output_poll(struct ssh *ssh)
{ {
struct ssh_channels *sc = ssh->chanctxt; struct ssh_channels *sc = ssh->chanctxt;
Channel *c; Channel *c;
u_int i; u_int i;
int ret = 0;
for (i = 0; i < sc->channels_alloc; i++) { for (i = 0; i < sc->channels_alloc; i++) {
c = sc->channels[i]; c = sc->channels[i];
@ -3023,12 +3031,13 @@ channel_output_poll(struct ssh *ssh)
/* Get the amount of buffered data for this channel. */ /* Get the amount of buffered data for this channel. */
if (c->istate == CHAN_INPUT_OPEN || if (c->istate == CHAN_INPUT_OPEN ||
c->istate == CHAN_INPUT_WAIT_DRAIN) c->istate == CHAN_INPUT_WAIT_DRAIN)
channel_output_poll_input_open(ssh, c); ret |= channel_output_poll_input_open(ssh, c);
/* Send extended data, i.e. stderr */ /* Send extended data, i.e. stderr */
if (!(c->flags & CHAN_EOF_SENT) && if (!(c->flags & CHAN_EOF_SENT) &&
c->extended_usage == CHAN_EXTENDED_READ) c->extended_usage == CHAN_EXTENDED_READ)
channel_output_poll_extended_read(ssh, c); ret |= channel_output_poll_extended_read(ssh, c);
} }
return ret;
} }
/* -- mux proxy support */ /* -- mux proxy support */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: channels.h,v 1.151 2023/07/04 03:59:21 dlg Exp $ */ /* $OpenBSD: channels.h,v 1.152 2023/09/04 00:01:46 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -335,7 +335,7 @@ struct timespec;
void channel_prepare_poll(struct ssh *, struct pollfd **, void channel_prepare_poll(struct ssh *, struct pollfd **,
u_int *, u_int *, u_int, struct timespec *); u_int *, u_int *, u_int, struct timespec *);
void channel_after_poll(struct ssh *, struct pollfd *, u_int); void channel_after_poll(struct ssh *, struct pollfd *, u_int);
void channel_output_poll(struct ssh *); int channel_output_poll(struct ssh *);
int channel_not_very_much_buffered_data(struct ssh *); int channel_not_very_much_buffered_data(struct ssh *);
void channel_close_all(struct ssh *); void channel_close_all(struct ssh *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: clientloop.c,v 1.392 2023/04/03 08:10:54 dtucker Exp $ */ /* $OpenBSD: clientloop.c,v 1.398 2023/09/10 03:51:55 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -118,6 +118,9 @@
/* Permitted RSA signature algorithms for UpdateHostkeys proofs */ /* Permitted RSA signature algorithms for UpdateHostkeys proofs */
#define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256" #define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256"
/* Uncertainty (in percent) of keystroke timing intervals */
#define SSH_KEYSTROKE_TIMING_FUZZ 10
/* import options */ /* import options */
extern Options options; extern Options options;
@ -507,17 +510,181 @@ server_alive_check(struct ssh *ssh)
schedule_server_alive_check(); schedule_server_alive_check();
} }
/* Try to send a dummy keystroke */
static int
send_chaff(struct ssh *ssh)
{
int r;
if ((ssh->kex->flags & KEX_HAS_PING) == 0)
return 0;
/* XXX probabilistically send chaff? */
/*
* a SSH2_MSG_CHANNEL_DATA payload is 9 bytes:
* 4 bytes channel ID + 4 bytes string length + 1 byte string data
* simulate that here.
*/
if ((r = sshpkt_start(ssh, SSH2_MSG_PING)) != 0 ||
(r = sshpkt_put_cstring(ssh, "PING!")) != 0 ||
(r = sshpkt_send(ssh)) != 0)
fatal_fr(r, "send packet");
return 1;
}
/* Sets the next interval to send a keystroke or chaff packet */
static void
set_next_interval(const struct timespec *now, struct timespec *next_interval,
u_int interval_ms, int starting)
{
struct timespec tmp;
long long interval_ns, fuzz_ns;
static long long rate_fuzz;
interval_ns = interval_ms * (1000LL * 1000);
fuzz_ns = (interval_ns * SSH_KEYSTROKE_TIMING_FUZZ) / 100;
/* Center fuzz around requested interval */
if (fuzz_ns > INT_MAX)
fuzz_ns = INT_MAX;
if (fuzz_ns > interval_ns) {
/* Shouldn't happen */
fatal_f("internal error: fuzz %u%% %lldns > interval %lldns",
SSH_KEYSTROKE_TIMING_FUZZ, fuzz_ns, interval_ns);
}
/*
* Randomise the keystroke/chaff intervals in two ways:
* 1. Each interval has some random jitter applied to make the
* interval-to-interval time unpredictable.
* 2. The overall interval rate is also randomly perturbed for each
* chaffing session to make the average rate unpredictable.
*/
if (starting)
rate_fuzz = arc4random_uniform(fuzz_ns);
interval_ns -= fuzz_ns;
interval_ns += arc4random_uniform(fuzz_ns) + rate_fuzz;
tmp.tv_sec = interval_ns / (1000 * 1000 * 1000);
tmp.tv_nsec = interval_ns % (1000 * 1000 * 1000);
timespecadd(now, &tmp, next_interval);
}
/*
* Performs keystroke timing obfuscation. Returns non-zero if the
* output fd should be polled.
*/
static int
obfuscate_keystroke_timing(struct ssh *ssh, struct timespec *timeout,
int channel_did_enqueue)
{
static int active;
static struct timespec next_interval, chaff_until;
struct timespec now, tmp;
int just_started = 0, had_keystroke = 0;
static unsigned long long nchaff;
char *stop_reason = NULL;
long long n;
monotime_ts(&now);
if (options.obscure_keystroke_timing_interval <= 0)
return 1; /* disabled in config */
if (!channel_still_open(ssh) || quit_pending) {
/* Stop if no channels left of we're waiting for one to close */
stop_reason = "no active channels";
} else if (ssh_packet_is_rekeying(ssh)) {
/* Stop if we're rekeying */
stop_reason = "rekeying started";
} else if (!ssh_packet_interactive_data_to_write(ssh) &&
ssh_packet_have_data_to_write(ssh)) {
/* Stop if the output buffer has more than a few keystrokes */
stop_reason = "output buffer filling";
} else if (active && channel_did_enqueue &&
ssh_packet_have_data_to_write(ssh)) {
/* Still in active mode and have a keystroke queued. */
had_keystroke = 1;
} else if (active) {
if (timespeccmp(&now, &chaff_until, >=)) {
/* Stop if there have been no keystrokes for a while */
stop_reason = "chaff time expired";
} else if (timespeccmp(&now, &next_interval, >=)) {
/* Otherwise if we were due to send, then send chaff */
if (send_chaff(ssh))
nchaff++;
}
}
if (stop_reason != NULL) {
if (active) {
debug3_f("stopping: %s (%llu chaff packets sent)",
stop_reason, nchaff);
active = 0;
}
return 1;
}
/*
* If we're in interactive mode, and only have a small amount
* of outbound data, then we assume that the user is typing
* interactively. In this case, start quantising outbound packets to
* fixed time intervals to hide inter-keystroke timing.
*/
if (!active && ssh_packet_interactive_data_to_write(ssh) &&
channel_did_enqueue && ssh_packet_have_data_to_write(ssh)) {
debug3_f("starting: interval ~%dms",
options.obscure_keystroke_timing_interval);
just_started = had_keystroke = active = 1;
nchaff = 0;
set_next_interval(&now, &next_interval,
options.obscure_keystroke_timing_interval, 1);
}
/* Don't hold off if obfuscation inactive */
if (!active)
return 1;
if (had_keystroke) {
/*
* Arrange to send chaff packets for a random interval after
* the last keystroke was sent.
*/
ms_to_timespec(&tmp, SSH_KEYSTROKE_CHAFF_MIN_MS +
arc4random_uniform(SSH_KEYSTROKE_CHAFF_RNG_MS));
timespecadd(&now, &tmp, &chaff_until);
}
ptimeout_deadline_monotime_tsp(timeout, &next_interval);
if (just_started)
return 1;
/* Don't arm output fd for poll until the timing interval has elapsed */
if (timespeccmp(&now, &next_interval, <))
return 0;
/* Calculate number of intervals missed since the last check */
n = (now.tv_sec - next_interval.tv_sec) * 1000LL * 1000 * 1000;
n += now.tv_nsec - next_interval.tv_nsec;
n /= options.obscure_keystroke_timing_interval * 1000LL * 1000;
n = (n < 0) ? 1 : n + 1;
/* Advance to the next interval */
set_next_interval(&now, &next_interval,
options.obscure_keystroke_timing_interval * n, 0);
return 1;
}
/* /*
* Waits until the client can do something (some data becomes available on * Waits until the client can do something (some data becomes available on
* one of the file descriptors). * one of the file descriptors).
*/ */
static void static void
client_wait_until_can_do_something(struct ssh *ssh, struct pollfd **pfdp, client_wait_until_can_do_something(struct ssh *ssh, struct pollfd **pfdp,
u_int *npfd_allocp, u_int *npfd_activep, int rekeying, u_int *npfd_allocp, u_int *npfd_activep, int channel_did_enqueue,
int *conn_in_readyp, int *conn_out_readyp) int *conn_in_readyp, int *conn_out_readyp)
{ {
struct timespec timeout; struct timespec timeout;
int ret; int ret, oready;
u_int p; u_int p;
*conn_in_readyp = *conn_out_readyp = 0; *conn_in_readyp = *conn_out_readyp = 0;
@ -537,11 +704,14 @@ client_wait_until_can_do_something(struct ssh *ssh, struct pollfd **pfdp,
return; return;
} }
oready = obfuscate_keystroke_timing(ssh, &timeout, channel_did_enqueue);
/* Monitor server connection on reserved pollfd entries */ /* Monitor server connection on reserved pollfd entries */
(*pfdp)[0].fd = connection_in; (*pfdp)[0].fd = connection_in;
(*pfdp)[0].events = POLLIN; (*pfdp)[0].events = POLLIN;
(*pfdp)[1].fd = connection_out; (*pfdp)[1].fd = connection_out;
(*pfdp)[1].events = ssh_packet_have_data_to_write(ssh) ? POLLOUT : 0; (*pfdp)[1].events = (oready && ssh_packet_have_data_to_write(ssh)) ?
POLLOUT : 0;
/* /*
* Wait for something to happen. This will suspend the process until * Wait for something to happen. This will suspend the process until
@ -553,12 +723,12 @@ client_wait_until_can_do_something(struct ssh *ssh, struct pollfd **pfdp,
ptimeout_deadline_monotime(&timeout, control_persist_exit_time); ptimeout_deadline_monotime(&timeout, control_persist_exit_time);
if (options.server_alive_interval > 0) if (options.server_alive_interval > 0)
ptimeout_deadline_monotime(&timeout, server_alive_time); ptimeout_deadline_monotime(&timeout, server_alive_time);
if (options.rekey_interval > 0 && !rekeying) { if (options.rekey_interval > 0 && !ssh_packet_is_rekeying(ssh)) {
ptimeout_deadline_sec(&timeout, ptimeout_deadline_sec(&timeout,
ssh_packet_get_rekey_timeout(ssh)); ssh_packet_get_rekey_timeout(ssh));
} }
ret = poll(*pfdp, *npfd_activep, ptimeout_get_ms(&timeout)); ret = ppoll(*pfdp, *npfd_activep, ptimeout_get_tsp(&timeout), NULL);
if (ret == -1) { if (ret == -1) {
/* /*
@ -1275,7 +1445,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
struct pollfd *pfd = NULL; struct pollfd *pfd = NULL;
u_int npfd_alloc = 0, npfd_active = 0; u_int npfd_alloc = 0, npfd_active = 0;
double start_time, total_time; double start_time, total_time;
int r, len; int channel_did_enqueue = 0, r, len;
u_int64_t ibytes, obytes; u_int64_t ibytes, obytes;
int conn_in_ready, conn_out_ready; int conn_in_ready, conn_out_ready;
@ -1365,6 +1535,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
/* Main loop of the client for the interactive session mode. */ /* Main loop of the client for the interactive session mode. */
while (!quit_pending) { while (!quit_pending) {
channel_did_enqueue = 0;
/* Process buffered packets sent by the server. */ /* Process buffered packets sent by the server. */
client_process_buffered_input_packets(ssh); client_process_buffered_input_packets(ssh);
@ -1386,7 +1557,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
* enqueue them for sending to the server. * enqueue them for sending to the server.
*/ */
if (ssh_packet_not_very_much_data_to_write(ssh)) if (ssh_packet_not_very_much_data_to_write(ssh))
channel_output_poll(ssh); channel_did_enqueue = channel_output_poll(ssh);
/* /*
* Check if the window size has changed, and buffer a * Check if the window size has changed, and buffer a
@ -1402,7 +1573,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
* available on one of the descriptors). * available on one of the descriptors).
*/ */
client_wait_until_can_do_something(ssh, &pfd, &npfd_alloc, client_wait_until_can_do_something(ssh, &pfd, &npfd_alloc,
&npfd_active, ssh_packet_is_rekeying(ssh), &npfd_active, channel_did_enqueue,
&conn_in_ready, &conn_out_ready); &conn_in_ready, &conn_out_ready);
if (quit_pending) if (quit_pending)

12
configure vendored
View File

@ -6083,7 +6083,13 @@ printf "%s\n" "$GCC_VER" >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking clang version" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking clang version" >&5
printf %s "checking clang version... " >&6; } printf %s "checking clang version... " >&6; }
CLANG_VER=`$CC -v 2>&1 | $AWK '/clang version /{print $3}'` ver="`$CC -v 2>&1`"
if echo "$ver" | grep "Apple" >/dev/null; then
CLANG_VER="apple-`echo "$ver" | \
awk '/Apple LLVM/ {print $4"-"$5}'`"
else
CLANG_VER=`echo "$ver" | $AWK '/clang version /{print $3}'`
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CLANG_VER" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CLANG_VER" >&5
printf "%s\n" "$CLANG_VER" >&6; } printf "%s\n" "$CLANG_VER" >&6; }
@ -7492,7 +7498,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
# https://bugzilla.mindrot.org/show_bug.cgi?id=3475 and # https://bugzilla.mindrot.org/show_bug.cgi?id=3475 and
# https://github.com/llvm/llvm-project/issues/59242 # https://github.com/llvm/llvm-project/issues/59242
case "$CLANG_VER" in case "$CLANG_VER" in
15.*) { 15.*|apple*) {
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -fzero-call-used-regs=used" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -fzero-call-used-regs=used" >&5
printf %s "checking if $CC supports compile flag -fzero-call-used-regs=used... " >&6; } printf %s "checking if $CC supports compile flag -fzero-call-used-regs=used... " >&6; }
saved_CFLAGS="$CFLAGS" saved_CFLAGS="$CFLAGS"
@ -10889,7 +10895,7 @@ main (void)
int a=0, b=0, c=0, d=0, n, v; int a=0, b=0, c=0, d=0, n, v;
n = sscanf(ZLIB_VERSION, "%d.%d.%d.%d", &a, &b, &c, &d); n = sscanf(ZLIB_VERSION, "%d.%d.%d.%d", &a, &b, &c, &d);
if (n != 3 && n != 4) if (n < 1)
exit(1); exit(1);
v = a*1000000 + b*10000 + c*100 + d; v = a*1000000 + b*10000 + c*100 + d;
fprintf(stderr, "found zlib version %s (%d)\n", ZLIB_VERSION, v); fprintf(stderr, "found zlib version %s (%d)\n", ZLIB_VERSION, v);

View File

@ -187,7 +187,13 @@ if test "$GCC" = "yes" || test "$GCC" = "egcs"; then
AC_MSG_RESULT([$GCC_VER]) AC_MSG_RESULT([$GCC_VER])
AC_MSG_CHECKING([clang version]) AC_MSG_CHECKING([clang version])
CLANG_VER=`$CC -v 2>&1 | $AWK '/clang version /{print $3}'` ver="`$CC -v 2>&1`"
if echo "$ver" | grep "Apple" >/dev/null; then
CLANG_VER="apple-`echo "$ver" | \
awk '/Apple LLVM/ {print $4"-"$5}'`"
else
CLANG_VER=`echo "$ver" | $AWK '/clang version /{print $3}'`
fi
AC_MSG_RESULT([$CLANG_VER]) AC_MSG_RESULT([$CLANG_VER])
OSSH_CHECK_CFLAG_COMPILE([-pipe]) OSSH_CHECK_CFLAG_COMPILE([-pipe])
@ -225,7 +231,7 @@ if test "$GCC" = "yes" || test "$GCC" = "egcs"; then
# https://bugzilla.mindrot.org/show_bug.cgi?id=3475 and # https://bugzilla.mindrot.org/show_bug.cgi?id=3475 and
# https://github.com/llvm/llvm-project/issues/59242 # https://github.com/llvm/llvm-project/issues/59242
case "$CLANG_VER" in case "$CLANG_VER" in
15.*) OSSH_CHECK_CFLAG_COMPILE([-fzero-call-used-regs=used]) ;; 15.*|apple*) OSSH_CHECK_CFLAG_COMPILE([-fzero-call-used-regs=used]) ;;
*) OSSH_CHECK_CFLAG_COMPILE([-fzero-call-used-regs=all]) ;; *) OSSH_CHECK_CFLAG_COMPILE([-fzero-call-used-regs=all]) ;;
esac esac
OSSH_CHECK_CFLAG_COMPILE([-ftrivial-auto-var-init=zero]) OSSH_CHECK_CFLAG_COMPILE([-ftrivial-auto-var-init=zero])
@ -1464,7 +1470,7 @@ else
[[ [[
int a=0, b=0, c=0, d=0, n, v; int a=0, b=0, c=0, d=0, n, v;
n = sscanf(ZLIB_VERSION, "%d.%d.%d.%d", &a, &b, &c, &d); n = sscanf(ZLIB_VERSION, "%d.%d.%d.%d", &a, &b, &c, &d);
if (n != 3 && n != 4) if (n < 1)
exit(1); exit(1);
v = a*1000000 + b*10000 + c*100 + d; v = a*1000000 + b*10000 + c*100 + d;
fprintf(stderr, "found zlib version %s (%d)\n", ZLIB_VERSION, v); fprintf(stderr, "found zlib version %s (%d)\n", ZLIB_VERSION, v);

View File

@ -1,4 +1,4 @@
%global ver 9.4p1 %global ver 9.5p1
%global rel 1%{?dist} %global rel 1%{?dist}
# OpenSSH privilege separation requires a user & group ID # OpenSSH privilege separation requires a user & group ID

View File

@ -13,7 +13,7 @@
Summary: OpenSSH, a free Secure Shell (SSH) protocol implementation Summary: OpenSSH, a free Secure Shell (SSH) protocol implementation
Name: openssh Name: openssh
Version: 9.4p1 Version: 9.5p1
URL: https://www.openssh.com/ URL: https://www.openssh.com/
Release: 1 Release: 1
Source0: openssh-%{version}.tar.gz Source0: openssh-%{version}.tar.gz

57
kex.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: kex.c,v 1.178 2023/03/12 10:40:39 dtucker Exp $ */ /* $OpenBSD: kex.c,v 1.181 2023/08/28 03:28:43 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* *
@ -492,12 +492,14 @@ kex_send_ext_info(struct ssh *ssh)
return SSH_ERR_ALLOC_FAIL; return SSH_ERR_ALLOC_FAIL;
/* XXX filter algs list by allowed pubkey/hostbased types */ /* XXX filter algs list by allowed pubkey/hostbased types */
if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
(r = sshpkt_put_u32(ssh, 2)) != 0 || (r = sshpkt_put_u32(ssh, 3)) != 0 ||
(r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
(r = sshpkt_put_cstring(ssh, algs)) != 0 || (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
(r = sshpkt_put_cstring(ssh, (r = sshpkt_put_cstring(ssh,
"publickey-hostbound@openssh.com")) != 0 || "publickey-hostbound@openssh.com")) != 0 ||
(r = sshpkt_put_cstring(ssh, "0")) != 0 || (r = sshpkt_put_cstring(ssh, "0")) != 0 ||
(r = sshpkt_put_cstring(ssh, "ping@openssh.com")) != 0 ||
(r = sshpkt_put_cstring(ssh, "0")) != 0 ||
(r = sshpkt_send(ssh)) != 0) { (r = sshpkt_send(ssh)) != 0) {
error_fr(r, "compose"); error_fr(r, "compose");
goto out; goto out;
@ -527,6 +529,23 @@ kex_send_newkeys(struct ssh *ssh)
return 0; return 0;
} }
/* Check whether an ext_info value contains the expected version string */
static int
kex_ext_info_check_ver(struct kex *kex, const char *name,
const u_char *val, size_t len, const char *want_ver, u_int flag)
{
if (memchr(val, '\0', len) != NULL) {
error("SSH2_MSG_EXT_INFO: %s value contains nul byte", name);
return SSH_ERR_INVALID_FORMAT;
}
debug_f("%s=<%s>", name, val);
if (strcmp(val, want_ver) == 0)
kex->flags |= flag;
else
debug_f("unsupported version of %s extension", name);
return 0;
}
int int
kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
{ {
@ -557,6 +576,8 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
/* Ensure no \0 lurking in value */ /* Ensure no \0 lurking in value */
if (memchr(val, '\0', vlen) != NULL) { if (memchr(val, '\0', vlen) != NULL) {
error_f("nul byte in %s", name); error_f("nul byte in %s", name);
free(name);
free(val);
return SSH_ERR_INVALID_FORMAT; return SSH_ERR_INVALID_FORMAT;
} }
debug_f("%s=<%s>", name, val); debug_f("%s=<%s>", name, val);
@ -564,18 +585,18 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
val = NULL; val = NULL;
} else if (strcmp(name, } else if (strcmp(name,
"publickey-hostbound@openssh.com") == 0) { "publickey-hostbound@openssh.com") == 0) {
/* XXX refactor */ if ((r = kex_ext_info_check_ver(kex, name, val, vlen,
/* Ensure no \0 lurking in value */ "0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) {
if (memchr(val, '\0', vlen) != NULL) { free(name);
error_f("nul byte in %s", name); free(val);
return SSH_ERR_INVALID_FORMAT; return r;
} }
debug_f("%s=<%s>", name, val); } else if (strcmp(name, "ping@openssh.com") == 0) {
if (strcmp(val, "0") == 0) if ((r = kex_ext_info_check_ver(kex, name, val, vlen,
kex->flags |= KEX_HAS_PUBKEY_HOSTBOUND; "0", KEX_HAS_PING)) != 0) {
else { free(name);
debug_f("unsupported version of %s extension", free(val);
name); return r;
} }
} else } else
debug_f("%s (unrecognised)", name); debug_f("%s (unrecognised)", name);
@ -1334,7 +1355,7 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
for (;;) { for (;;) {
if (timeout_ms > 0) { if (timeout_ms > 0) {
r = waitrfd(ssh_packet_get_connection_in(ssh), r = waitrfd(ssh_packet_get_connection_in(ssh),
&timeout_ms); &timeout_ms, NULL);
if (r == -1 && errno == ETIMEDOUT) { if (r == -1 && errno == ETIMEDOUT) {
send_error(ssh, "Timed out waiting " send_error(ssh, "Timed out waiting "
"for SSH identification string."); "for SSH identification string.");
@ -1353,7 +1374,7 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
len = atomicio(read, ssh_packet_get_connection_in(ssh), len = atomicio(read, ssh_packet_get_connection_in(ssh),
&c, 1); &c, 1);
if (len != 1 && errno == EPIPE) { if (len != 1 && errno == EPIPE) {
error_f("Connection closed by remote host"); verbose_f("Connection closed by remote host");
r = SSH_ERR_CONN_CLOSED; r = SSH_ERR_CONN_CLOSED;
goto out; goto out;
} else if (len != 1) { } else if (len != 1) {
@ -1369,7 +1390,7 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
if (c == '\n') if (c == '\n')
break; break;
if (c == '\0' || expect_nl) { if (c == '\0' || expect_nl) {
error_f("banner line contains invalid " verbose_f("banner line contains invalid "
"characters"); "characters");
goto invalid; goto invalid;
} }
@ -1379,7 +1400,7 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
goto out; goto out;
} }
if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) { if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
error_f("banner line too long"); verbose_f("banner line too long");
goto invalid; goto invalid;
} }
} }
@ -1395,7 +1416,7 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
} }
/* Do not accept lines before the SSH ident from a client */ /* Do not accept lines before the SSH ident from a client */
if (ssh->kex->server) { if (ssh->kex->server) {
error_f("client sent invalid protocol identifier " verbose_f("client sent invalid protocol identifier "
"\"%.256s\"", cp); "\"%.256s\"", cp);
free(cp); free(cp);
goto invalid; goto invalid;

3
kex.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: kex.h,v 1.118 2023/03/06 12:14:48 dtucker Exp $ */ /* $OpenBSD: kex.h,v 1.119 2023/08/28 03:28:43 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -111,6 +111,7 @@ enum kex_exchange {
#define KEX_HAS_PUBKEY_HOSTBOUND 0x0004 #define KEX_HAS_PUBKEY_HOSTBOUND 0x0004
#define KEX_RSA_SHA2_256_SUPPORTED 0x0008 /* only set in server for now */ #define KEX_RSA_SHA2_256_SUPPORTED 0x0008 /* only set in server for now */
#define KEX_RSA_SHA2_512_SUPPORTED 0x0010 /* only set in server for now */ #define KEX_RSA_SHA2_512_SUPPORTED 0x0010 /* only set in server for now */
#define KEX_HAS_PING 0x0020
struct sshenc { struct sshenc {
char *name; char *name;

71
misc.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.185 2023/08/04 06:32:40 dtucker Exp $ */ /* $OpenBSD: misc.c,v 1.187 2023/08/28 03:31:16 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005-2020 Damien Miller. All rights reserved. * Copyright (c) 2005-2020 Damien Miller. All rights reserved.
@ -313,20 +313,38 @@ set_sock_tos(int fd, int tos)
* Returns 0 if fd ready or -1 on timeout or error (see errno). * Returns 0 if fd ready or -1 on timeout or error (see errno).
*/ */
static int static int
waitfd(int fd, int *timeoutp, short events) waitfd(int fd, int *timeoutp, short events, volatile sig_atomic_t *stop)
{ {
struct pollfd pfd; struct pollfd pfd;
struct timeval t_start; struct timespec timeout;
int oerrno, r, have_timeout = (*timeoutp >= 0); int oerrno, r;
sigset_t nsigset, osigset;
if (timeoutp && *timeoutp == -1)
timeoutp = NULL;
pfd.fd = fd; pfd.fd = fd;
pfd.events = events; pfd.events = events;
for (; !have_timeout || *timeoutp >= 0;) { ptimeout_init(&timeout);
monotime_tv(&t_start); if (timeoutp != NULL)
r = poll(&pfd, 1, *timeoutp); ptimeout_deadline_ms(&timeout, *timeoutp);
if (stop != NULL)
sigfillset(&nsigset);
for (; timeoutp == NULL || *timeoutp >= 0;) {
if (stop != NULL) {
sigprocmask(SIG_BLOCK, &nsigset, &osigset);
if (*stop) {
sigprocmask(SIG_SETMASK, &osigset, NULL);
errno = EINTR;
return -1;
}
}
r = ppoll(&pfd, 1, ptimeout_get_tsp(&timeout),
stop != NULL ? &osigset : NULL);
oerrno = errno; oerrno = errno;
if (have_timeout) if (stop != NULL)
ms_subtract_diff(&t_start, timeoutp); sigprocmask(SIG_SETMASK, &osigset, NULL);
if (timeoutp)
*timeoutp = ptimeout_get_ms(&timeout);
errno = oerrno; errno = oerrno;
if (r > 0) if (r > 0)
return 0; return 0;
@ -346,8 +364,8 @@ waitfd(int fd, int *timeoutp, short events)
* Returns 0 if fd ready or -1 on timeout or error (see errno). * Returns 0 if fd ready or -1 on timeout or error (see errno).
*/ */
int int
waitrfd(int fd, int *timeoutp) { waitrfd(int fd, int *timeoutp, volatile sig_atomic_t *stop) {
return waitfd(fd, timeoutp, POLLIN); return waitfd(fd, timeoutp, POLLIN, stop);
} }
/* /*
@ -381,7 +399,7 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
break; break;
} }
if (waitfd(sockfd, timeoutp, POLLIN | POLLOUT) == -1) if (waitfd(sockfd, timeoutp, POLLIN | POLLOUT, NULL) == -1)
return -1; return -1;
/* Completed or failed */ /* Completed or failed */
@ -2883,22 +2901,33 @@ ptimeout_deadline_ms(struct timespec *pt, long ms)
ptimeout_deadline_tsp(pt, &p); ptimeout_deadline_tsp(pt, &p);
} }
/* Specify a poll/ppoll deadline at wall clock monotime 'when' (timespec) */
void
ptimeout_deadline_monotime_tsp(struct timespec *pt, struct timespec *when)
{
struct timespec now, t;
monotime_ts(&now);
if (timespeccmp(&now, when, >=)) {
/* 'when' is now or in the past. Timeout ASAP */
pt->tv_sec = 0;
pt->tv_nsec = 0;
} else {
timespecsub(when, &now, &t);
ptimeout_deadline_tsp(pt, &t);
}
}
/* Specify a poll/ppoll deadline at wall clock monotime 'when' */ /* Specify a poll/ppoll deadline at wall clock monotime 'when' */
void void
ptimeout_deadline_monotime(struct timespec *pt, time_t when) ptimeout_deadline_monotime(struct timespec *pt, time_t when)
{ {
struct timespec now, t; struct timespec t;
t.tv_sec = when; t.tv_sec = when;
t.tv_nsec = 0; t.tv_nsec = 0;
monotime_ts(&now); ptimeout_deadline_monotime_tsp(pt, &t);
if (timespeccmp(&now, &t, >=))
ptimeout_deadline_sec(pt, 0);
else {
timespecsub(&t, &now, &t);
ptimeout_deadline_tsp(pt, &t);
}
} }
/* Get a poll(2) timeout value in milliseconds */ /* Get a poll(2) timeout value in milliseconds */

6
misc.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.h,v 1.103 2023/07/19 14:02:27 djm Exp $ */ /* $OpenBSD: misc.h,v 1.105 2023/08/28 03:31:16 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -19,6 +19,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h>
/* Data structure for representing a forwarding request. */ /* Data structure for representing a forwarding request. */
struct Forward { struct Forward {
@ -57,7 +58,7 @@ char *get_rdomain(int);
int set_rdomain(int, const char *); int set_rdomain(int, const char *);
int get_sock_af(int); int get_sock_af(int);
void set_sock_tos(int, int); void set_sock_tos(int, int);
int waitrfd(int, int *); int waitrfd(int, int *, volatile sig_atomic_t *);
int timeout_connect(int, const struct sockaddr *, socklen_t, int *); int timeout_connect(int, const struct sockaddr *, socklen_t, int *);
int a2port(const char *); int a2port(const char *);
int a2tun(const char *, int *); int a2tun(const char *, int *);
@ -213,6 +214,7 @@ struct timespec;
void ptimeout_init(struct timespec *pt); void ptimeout_init(struct timespec *pt);
void ptimeout_deadline_sec(struct timespec *pt, long sec); void ptimeout_deadline_sec(struct timespec *pt, long sec);
void ptimeout_deadline_ms(struct timespec *pt, long ms); void ptimeout_deadline_ms(struct timespec *pt, long ms);
void ptimeout_deadline_monotime_tsp(struct timespec *pt, struct timespec *when);
void ptimeout_deadline_monotime(struct timespec *pt, time_t when); void ptimeout_deadline_monotime(struct timespec *pt, time_t when);
int ptimeout_get_ms(struct timespec *pt); int ptimeout_get_ms(struct timespec *pt);
struct timespec *ptimeout_get_tsp(struct timespec *pt); struct timespec *ptimeout_get_tsp(struct timespec *pt);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor.c,v 1.236 2023/05/10 10:04:20 dtucker Exp $ */ /* $OpenBSD: monitor.c,v 1.237 2023/08/16 16:14:11 djm Exp $ */
/* /*
* Copyright 2002 Niels Provos <provos@citi.umich.edu> * Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org> * Copyright 2002 Markus Friedl <markus@openbsd.org>
@ -342,6 +342,11 @@ monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor)
auth_method, auth_submethod); auth_method, auth_submethod);
} }
} }
if (authctxt->failures > options.max_authtries) {
/* Shouldn't happen */
fatal_f("privsep child made too many authentication "
"attempts");
}
} }
if (!authctxt->valid) if (!authctxt->valid)

6
mux.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: mux.c,v 1.99 2023/08/04 06:32:40 dtucker Exp $ */ /* $OpenBSD: mux.c,v 1.100 2023/08/18 01:37:41 djm Exp $ */
/* /*
* Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
* *
@ -1480,7 +1480,9 @@ mux_client_read(int fd, struct sshbuf *b, size_t need, int timeout_ms)
case EWOULDBLOCK: case EWOULDBLOCK:
#endif #endif
case EAGAIN: case EAGAIN:
if (waitrfd(fd, &timeout_ms) == -1) if (waitrfd(fd, &timeout_ms,
&muxclient_terminate) == -1 &&
errno != EINTR)
return -1; /* timeout */ return -1; /* timeout */
/* FALLTHROUGH */ /* FALLTHROUGH */
case EINTR: case EINTR:

View File

@ -28,7 +28,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#ifdef HAVE_DIRENT_H #ifdef HAVE_DIRENT_H
# include <dirent.h> # include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name) # define NAMLEN(dirent) strlen((dirent)->d_name)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.c,v 1.310 2023/04/06 03:21:31 djm Exp $ */ /* $OpenBSD: packet.c,v 1.312 2023/08/28 03:31:16 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -1054,6 +1054,8 @@ int
ssh_packet_log_type(u_char type) ssh_packet_log_type(u_char type)
{ {
switch (type) { switch (type) {
case SSH2_MSG_PING:
case SSH2_MSG_PONG:
case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_DATA:
case SSH2_MSG_CHANNEL_EXTENDED_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA:
case SSH2_MSG_CHANNEL_WINDOW_ADJUST: case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
@ -1675,7 +1677,7 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
goto out; goto out;
if (ssh_packet_log_type(*typep)) if (ssh_packet_log_type(*typep))
debug3("receive packet: type %u", *typep); debug3("receive packet: type %u", *typep);
if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) { if (*typep < SSH2_MSG_MIN) {
if ((r = sshpkt_disconnect(ssh, if ((r = sshpkt_disconnect(ssh,
"Invalid ssh2 packet type: %d", *typep)) != 0 || "Invalid ssh2 packet type: %d", *typep)) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0) (r = ssh_packet_write_wait(ssh)) != 0)
@ -1710,6 +1712,8 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
u_int reason, seqnr; u_int reason, seqnr;
int r; int r;
u_char *msg; u_char *msg;
const u_char *d;
size_t len;
for (;;) { for (;;) {
msg = NULL; msg = NULL;
@ -1753,6 +1757,21 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
debug("Received SSH2_MSG_UNIMPLEMENTED for %u", debug("Received SSH2_MSG_UNIMPLEMENTED for %u",
seqnr); seqnr);
break; break;
case SSH2_MSG_PING:
if ((r = sshpkt_get_string_direct(ssh, &d, &len)) != 0)
return r;
DBG(debug("Received SSH2_MSG_PING len %zu", len));
if ((r = sshpkt_start(ssh, SSH2_MSG_PONG)) != 0 ||
(r = sshpkt_put_string(ssh, d, len)) != 0 ||
(r = sshpkt_send(ssh)) != 0)
return r;
break;
case SSH2_MSG_PONG:
if ((r = sshpkt_get_string_direct(ssh,
NULL, &len)) != 0)
return r;
DBG(debug("Received SSH2_MSG_PONG len %zu", len));
break;
default: default:
return 0; return 0;
} }
@ -2064,6 +2083,18 @@ ssh_packet_not_very_much_data_to_write(struct ssh *ssh)
return sshbuf_len(ssh->state->output) < 128 * 1024; return sshbuf_len(ssh->state->output) < 128 * 1024;
} }
/*
* returns true when there are at most a few keystrokes of data to write
* and the connection is in interactive mode.
*/
int
ssh_packet_interactive_data_to_write(struct ssh *ssh)
{
return ssh->state->interactive_mode &&
sshbuf_len(ssh->state->output) < 256;
}
void void
ssh_packet_set_tos(struct ssh *ssh, int tos) ssh_packet_set_tos(struct ssh *ssh, int tos)
{ {

View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.h,v 1.94 2022/01/22 00:49:34 djm Exp $ */ /* $OpenBSD: packet.h,v 1.95 2023/08/28 03:31:16 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -145,6 +145,7 @@ int ssh_packet_write_poll(struct ssh *);
int ssh_packet_write_wait(struct ssh *); int ssh_packet_write_wait(struct ssh *);
int ssh_packet_have_data_to_write(struct ssh *); int ssh_packet_have_data_to_write(struct ssh *);
int ssh_packet_not_very_much_data_to_write(struct ssh *); int ssh_packet_not_very_much_data_to_write(struct ssh *);
int ssh_packet_interactive_data_to_write(struct ssh *);
int ssh_packet_connection_is_on_socket(struct ssh *); int ssh_packet_connection_is_on_socket(struct ssh *);
int ssh_packet_remaining(struct ssh *); int ssh_packet_remaining(struct ssh *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: readconf.c,v 1.380 2023/07/17 06:16:33 djm Exp $ */ /* $OpenBSD: readconf.c,v 1.381 2023/08/28 03:31:16 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -178,7 +178,7 @@ typedef enum {
oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms, oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump, oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize, oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize,
oEnableEscapeCommandline, oEnableEscapeCommandline, oObscureKeystrokeTiming,
oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
} OpCodes; } OpCodes;
@ -327,6 +327,7 @@ static struct {
{ "knownhostscommand", oKnownHostsCommand }, { "knownhostscommand", oKnownHostsCommand },
{ "requiredrsasize", oRequiredRSASize }, { "requiredrsasize", oRequiredRSASize },
{ "enableescapecommandline", oEnableEscapeCommandline }, { "enableescapecommandline", oEnableEscapeCommandline },
{ "obscurekeystroketiming", oObscureKeystrokeTiming },
{ NULL, oBadOption } { NULL, oBadOption }
}; };
@ -2280,6 +2281,48 @@ parse_pubkey_algos:
intptr = &options->required_rsa_size; intptr = &options->required_rsa_size;
goto parse_int; goto parse_int;
case oObscureKeystrokeTiming:
value = -1;
while ((arg = argv_next(&ac, &av)) != NULL) {
if (value != -1) {
error("%s line %d: invalid arguments",
filename, linenum);
goto out;
}
if (strcmp(arg, "yes") == 0 ||
strcmp(arg, "true") == 0)
value = SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
else if (strcmp(arg, "no") == 0 ||
strcmp(arg, "false") == 0)
value = 0;
else if (strncmp(arg, "interval:", 9) == 0) {
if ((errstr = atoi_err(arg + 9,
&value)) != NULL) {
error("%s line %d: integer value %s.",
filename, linenum, errstr);
goto out;
}
if (value <= 0 || value > 1000) {
error("%s line %d: value out of range.",
filename, linenum);
goto out;
}
} else {
error("%s line %d: unsupported argument \"%s\"",
filename, linenum, arg);
goto out;
}
}
if (value == -1) {
error("%s line %d: missing argument",
filename, linenum);
goto out;
}
intptr = &options->obscure_keystroke_timing_interval;
if (*activep && *intptr == -1)
*intptr = value;
break;
case oDeprecated: case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"", debug("%s line %d: Deprecated option \"%s\"",
filename, linenum, keyword); filename, linenum, keyword);
@ -2530,6 +2573,7 @@ initialize_options(Options * options)
options->known_hosts_command = NULL; options->known_hosts_command = NULL;
options->required_rsa_size = -1; options->required_rsa_size = -1;
options->enable_escape_commandline = -1; options->enable_escape_commandline = -1;
options->obscure_keystroke_timing_interval = -1;
options->tag = NULL; options->tag = NULL;
} }
@ -2731,6 +2775,10 @@ fill_default_options(Options * options)
options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE; options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
if (options->enable_escape_commandline == -1) if (options->enable_escape_commandline == -1)
options->enable_escape_commandline = 0; options->enable_escape_commandline = 0;
if (options->obscure_keystroke_timing_interval == -1) {
options->obscure_keystroke_timing_interval =
SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
}
/* Expand KEX name lists */ /* Expand KEX name lists */
all_cipher = cipher_alg_list(',', 0); all_cipher = cipher_alg_list(',', 0);
@ -3273,6 +3321,16 @@ lookup_opcode_name(OpCodes code)
static void static void
dump_cfg_int(OpCodes code, int val) dump_cfg_int(OpCodes code, int val)
{ {
if (code == oObscureKeystrokeTiming) {
if (val == 0) {
printf("%s no\n", lookup_opcode_name(code));
return;
} else if (val == SSH_KEYSTROKE_DEFAULT_INTERVAL_MS) {
printf("%s yes\n", lookup_opcode_name(code));
return;
}
/* FALLTHROUGH */
}
printf("%s %d\n", lookup_opcode_name(code), val); printf("%s %d\n", lookup_opcode_name(code), val);
} }
@ -3423,6 +3481,8 @@ dump_client_config(Options *o, const char *host)
dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max); dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
dump_cfg_int(oServerAliveInterval, o->server_alive_interval); dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
dump_cfg_int(oRequiredRSASize, o->required_rsa_size); dump_cfg_int(oRequiredRSASize, o->required_rsa_size);
dump_cfg_int(oObscureKeystrokeTiming,
o->obscure_keystroke_timing_interval);
/* String options */ /* String options */
dump_cfg_string(oBindAddress, o->bind_address); dump_cfg_string(oBindAddress, o->bind_address);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: readconf.h,v 1.151 2023/07/17 04:08:31 djm Exp $ */ /* $OpenBSD: readconf.h,v 1.152 2023/08/28 03:31:16 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -180,6 +180,7 @@ typedef struct {
int required_rsa_size; /* minimum size of RSA keys */ int required_rsa_size; /* minimum size of RSA keys */
int enable_escape_commandline; /* ~C commandline */ int enable_escape_commandline; /* ~C commandline */
int obscure_keystroke_timing_interval;
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
} Options; } Options;
@ -222,6 +223,11 @@ typedef struct {
#define SSH_STRICT_HOSTKEY_YES 2 #define SSH_STRICT_HOSTKEY_YES 2
#define SSH_STRICT_HOSTKEY_ASK 3 #define SSH_STRICT_HOSTKEY_ASK 3
/* ObscureKeystrokes parameters */
#define SSH_KEYSTROKE_DEFAULT_INTERVAL_MS 20
#define SSH_KEYSTROKE_CHAFF_MIN_MS 1024
#define SSH_KEYSTROKE_CHAFF_RNG_MS 2048
const char *kex_default_pk_alg(void); const char *kex_default_pk_alg(void);
char *ssh_connection_hash(const char *thishost, const char *host, char *ssh_connection_hash(const char *thishost, const char *host,
const char *portstr, const char *user); const char *portstr, const char *user);

View File

@ -1,4 +1,4 @@
# $OpenBSD: Makefile,v 1.125 2023/05/17 05:52:01 djm Exp $ # $OpenBSD: Makefile,v 1.126 2023/09/06 23:36:09 djm Exp $
tests: prep file-tests t-exec unit tests: prep file-tests t-exec unit
@ -103,7 +103,8 @@ LTESTS= connect \
agent-restrict \ agent-restrict \
hostbased \ hostbased \
channel-timeout \ channel-timeout \
connection-timeout connection-timeout \
match-subsystem
INTEROP_TESTS= putty-transfer putty-ciphers putty-kex conch-ciphers INTEROP_TESTS= putty-transfer putty-ciphers putty-kex conch-ciphers
#INTEROP_TESTS+=ssh-com ssh-com-client ssh-com-keygen ssh-com-sftp #INTEROP_TESTS+=ssh-com ssh-com-client ssh-com-keygen ssh-com-sftp

View File

@ -0,0 +1,90 @@
# $OpenBSD: match-subsystem.sh,v 1.1 2023/09/06 23:36:09 djm Exp $
# Placed in the Public Domain.
tid="sshd_config match subsystem"
cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak
try_subsystem() {
_id=$1
_subsystem=$2
_expect=$3
${SSHD} -tf $OBJ/sshd_proxy || fatal "$_id: bad config"
${SSH} -sF $OBJ/ssh_proxy somehost $_subsystem
_exit=$?
trace "$_id subsystem $_subsystem"
if [ $_exit -ne $_expect ] ; then
fail "$_id: subsystem $_subsystem exit $_exit expected $_expect"
fi
return $?
}
# Simple case: subsystem in main config.
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
cat >> $OBJ/sshd_proxy << _EOF
Subsystem xxx /bin/sh -c "exit 23"
_EOF
try_subsystem "main config" xxx 23
# No clobber in main config.
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
cat >> $OBJ/sshd_proxy << _EOF
Subsystem xxx /bin/sh -c "exit 23"
Subsystem xxx /bin/sh -c "exit 24"
_EOF
try_subsystem "main config no clobber" xxx 23
# Subsystem in match all block
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
cat >> $OBJ/sshd_proxy << _EOF
Match all
Subsystem xxx /bin/sh -c "exit 21"
_EOF
try_subsystem "match all" xxx 21
# No clobber in match all block
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
cat >> $OBJ/sshd_proxy << _EOF
Match all
Subsystem xxx /bin/sh -c "exit 21"
Subsystem xxx /bin/sh -c "exit 24"
_EOF
try_subsystem "match all no clobber" xxx 21
# Subsystem in match user block
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
cat >> $OBJ/sshd_proxy << _EOF
Match user *
Subsystem xxx /bin/sh -c "exit 20"
_EOF
try_subsystem "match user" xxx 20
# No clobber in match user block
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
cat >> $OBJ/sshd_proxy << _EOF
Match user *
Subsystem xxx /bin/sh -c "exit 20"
Subsystem xxx /bin/sh -c "exit 24"
Match all
Subsystem xxx /bin/sh -c "exit 24"
_EOF
try_subsystem "match user no clobber" xxx 20
# Override main with match all
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
cat >> $OBJ/sshd_proxy << _EOF
Subsystem xxx /bin/sh -c "exit 23"
Match all
Subsystem xxx /bin/sh -c "exit 19"
_EOF
try_subsystem "match all override" xxx 19
# Override main with match user
cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy
cat >> $OBJ/sshd_proxy << _EOF
Subsystem xxx /bin/sh -c "exit 23"
Match user *
Subsystem xxx /bin/sh -c "exit 18"
_EOF
try_subsystem "match user override" xxx 18

View File

@ -1,4 +1,4 @@
# $OpenBSD: scp.sh,v 1.18 2023/01/13 04:47:34 dtucker Exp $ # $OpenBSD: scp.sh,v 1.19 2023/09/08 05:50:57 djm Exp $
# Placed in the Public Domain. # Placed in the Public Domain.
tid="scp" tid="scp"
@ -30,13 +30,25 @@ scpclean() {
chmod 755 ${DIR} ${DIR2} ${DIR3} chmod 755 ${DIR} ${DIR2} ${DIR3}
} }
# Create directory structure for recursive copy tests.
forest() {
scpclean
rm -rf ${DIR2}
cp ${DATA} ${DIR}/copy
ln -s ${DIR}/copy ${DIR}/copy-sym
mkdir ${DIR}/subdir
cp ${DATA} ${DIR}/subdir/copy
ln -s ${DIR}/subdir ${DIR}/subdir-sym
}
for mode in scp sftp ; do for mode in scp sftp ; do
tag="$tid: $mode mode" tag="$tid: $mode mode"
if test $mode = scp ; then if test $mode = scp ; then
scpopts="-O -q -S ${OBJ}/scp-ssh-wrapper.scp" scpopts="-O -q -S ${OBJ}/scp-ssh-wrapper.scp"
else else
scpopts="-s -D ${SFTPSERVER}" scpopts="-qs -D ${SFTPSERVER}"
fi fi
verbose "$tag: simple copy local file to local file" verbose "$tag: simple copy local file to local file"
scpclean scpclean
$SCP $scpopts ${DATA} ${COPY} || fail "copy failed" $SCP $scpopts ${DATA} ${COPY} || fail "copy failed"
@ -96,21 +108,19 @@ for mode in scp sftp ; do
cmp ${COPY} ${DIR}/copy || fail "corrupted copy" cmp ${COPY} ${DIR}/copy || fail "corrupted copy"
verbose "$tag: recursive local dir to remote dir" verbose "$tag: recursive local dir to remote dir"
scpclean forest
rm -rf ${DIR2}
cp ${DATA} ${DIR}/copy
$SCP $scpopts -r ${DIR} somehost:${DIR2} || fail "copy failed" $SCP $scpopts -r ${DIR} somehost:${DIR2} || fail "copy failed"
diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy"
verbose "$tag: recursive local dir to local dir" verbose "$tag: recursive local dir to local dir"
scpclean forest
rm -rf ${DIR2} rm -rf ${DIR2}
cp ${DATA} ${DIR}/copy cp ${DATA} ${DIR}/copy
$SCP $scpopts -r ${DIR} ${DIR2} || fail "copy failed" $SCP $scpopts -r ${DIR} ${DIR2} || fail "copy failed"
diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy"
verbose "$tag: recursive remote dir to local dir" verbose "$tag: recursive remote dir to local dir"
scpclean forest
rm -rf ${DIR2} rm -rf ${DIR2}
cp ${DATA} ${DIR}/copy cp ${DATA} ${DIR}/copy
$SCP $scpopts -r somehost:${DIR} ${DIR2} || fail "copy failed" $SCP $scpopts -r somehost:${DIR} ${DIR2} || fail "copy failed"

View File

@ -1,10 +1,8 @@
# $OpenBSD: scp3.sh,v 1.4 2023/01/13 04:47:34 dtucker Exp $ # $OpenBSD: scp3.sh,v 1.5 2023/09/08 06:10:57 djm Exp $
# Placed in the Public Domain. # Placed in the Public Domain.
tid="scp3" tid="scp3"
set -x
COPY2=${OBJ}/copy2 COPY2=${OBJ}/copy2
DIR=${COPY}.dd DIR=${COPY}.dd
DIR2=${COPY}.dd2 DIR2=${COPY}.dd2
@ -22,6 +20,17 @@ scpclean() {
chmod 755 ${DIR} ${DIR2} chmod 755 ${DIR} ${DIR2}
} }
# Create directory structure for recursive copy tests.
forest() {
scpclean
rm -rf ${DIR2}
cp ${DATA} ${DIR}/copy
ln -s ${DIR}/copy ${DIR}/copy-sym
mkdir ${DIR}/subdir
cp ${DATA} ${DIR}/subdir/copy
ln -s ${DIR}/subdir ${DIR}/subdir-sym
}
for mode in scp sftp ; do for mode in scp sftp ; do
scpopts="-F${OBJ}/ssh_proxy -S ${SSH} -q" scpopts="-F${OBJ}/ssh_proxy -S ${SSH} -q"
tag="$tid: $mode mode" tag="$tid: $mode mode"
@ -43,9 +52,7 @@ for mode in scp sftp ; do
cmp ${COPY} ${DIR}/copy || fail "corrupted copy" cmp ${COPY} ${DIR}/copy || fail "corrupted copy"
verbose "$tag: recursive remote dir to remote dir" verbose "$tag: recursive remote dir to remote dir"
scpclean forest
rm -rf ${DIR2}
cp ${DATA} ${DIR}/copy
$SCP $scpopts -3r hostA:${DIR} hostB:${DIR2} || fail "copy failed" $SCP $scpopts -3r hostA:${DIR} hostB:${DIR2} || fail "copy failed"
diff -r ${DIR} ${DIR2} || fail "corrupted copy" diff -r ${DIR} ${DIR2} || fail "corrupted copy"
diff -r ${DIR2} ${DIR} || fail "corrupted copy" diff -r ${DIR2} ${DIR} || fail "corrupted copy"

50
scp.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: scp.c,v 1.257 2023/07/14 05:31:44 djm Exp $ */ /* $OpenBSD: scp.c,v 1.259 2023/09/10 23:12:32 djm Exp $ */
/* /*
* scp - secure remote copy. This is basically patched BSD rcp which * scp - secure remote copy. This is basically patched BSD rcp which
* uses ssh to do the data transfer (instead of using rcmd). * uses ssh to do the data transfer (instead of using rcmd).
@ -186,7 +186,7 @@ size_t sftp_nrequests;
/* Needed for sftp */ /* Needed for sftp */
volatile sig_atomic_t interrupted = 0; volatile sig_atomic_t interrupted = 0;
int remote_glob(struct sftp_conn *, const char *, int, int sftp_glob(struct sftp_conn *, const char *, int,
int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
static void static void
@ -1028,7 +1028,7 @@ do_sftp_connect(char *host, char *user, int port, char *sftp_direct,
reminp, remoutp, pidp) < 0) reminp, remoutp, pidp) < 0)
return NULL; return NULL;
} }
return do_init(*reminp, *remoutp, return sftp_init(*reminp, *remoutp,
sftp_copy_buflen, sftp_nrequests, limit_kbps); sftp_copy_buflen, sftp_nrequests, limit_kbps);
} }
@ -1324,8 +1324,8 @@ prepare_remote_path(struct sftp_conn *conn, const char *path)
return xstrdup("."); return xstrdup(".");
return xstrdup(path + 2 + nslash); return xstrdup(path + 2 + nslash);
} }
if (can_expand_path(conn)) if (sftp_can_expand_path(conn))
return do_expand_path(conn, path); return sftp_expand_path(conn, path);
/* No protocol extension */ /* No protocol extension */
error("server expand-path extension is required " error("server expand-path extension is required "
"for ~user paths in SFTP mode"); "for ~user paths in SFTP mode");
@ -1353,17 +1353,17 @@ source_sftp(int argc, char *src, char *targ, struct sftp_conn *conn)
*/ */
if ((target = prepare_remote_path(conn, targ)) == NULL) if ((target = prepare_remote_path(conn, targ)) == NULL)
cleanup_exit(255); cleanup_exit(255);
target_is_dir = remote_is_dir(conn, target); target_is_dir = sftp_remote_is_dir(conn, target);
if (targetshouldbedirectory && !target_is_dir) { if (targetshouldbedirectory && !target_is_dir) {
debug("target directory \"%s\" does not exist", target); debug("target directory \"%s\" does not exist", target);
a.flags = SSH2_FILEXFER_ATTR_PERMISSIONS; a.flags = SSH2_FILEXFER_ATTR_PERMISSIONS;
a.perm = st.st_mode | 0700; /* ensure writable */ a.perm = st.st_mode | 0700; /* ensure writable */
if (do_mkdir(conn, target, &a, 1) != 0) if (sftp_mkdir(conn, target, &a, 1) != 0)
cleanup_exit(255); /* error already logged */ cleanup_exit(255); /* error already logged */
target_is_dir = 1; target_is_dir = 1;
} }
if (target_is_dir) if (target_is_dir)
abs_dst = path_append(target, filename); abs_dst = sftp_path_append(target, filename);
else { else {
abs_dst = target; abs_dst = target;
target = NULL; target = NULL;
@ -1371,12 +1371,12 @@ source_sftp(int argc, char *src, char *targ, struct sftp_conn *conn)
debug3_f("copying local %s to remote %s", src, abs_dst); debug3_f("copying local %s to remote %s", src, abs_dst);
if (src_is_dir && iamrecursive) { if (src_is_dir && iamrecursive) {
if (upload_dir(conn, src, abs_dst, pflag, if (sftp_upload_dir(conn, src, abs_dst, pflag,
SFTP_PROGRESS_ONLY, 0, 0, 1, 1) != 0) { SFTP_PROGRESS_ONLY, 0, 0, 1, 1) != 0) {
error("failed to upload directory %s to %s", src, targ); error("failed to upload directory %s to %s", src, targ);
errs = 1; errs = 1;
} }
} else if (do_upload(conn, src, abs_dst, pflag, 0, 0, 1) != 0) { } else if (sftp_upload(conn, src, abs_dst, pflag, 0, 0, 1) != 0) {
error("failed to upload file %s to %s", src, targ); error("failed to upload file %s to %s", src, targ);
errs = 1; errs = 1;
} }
@ -1568,7 +1568,7 @@ sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn)
} }
debug3_f("copying remote %s to local %s", abs_src, dst); debug3_f("copying remote %s to local %s", abs_src, dst);
if ((r = remote_glob(conn, abs_src, GLOB_NOCHECK|GLOB_MARK, if ((r = sftp_glob(conn, abs_src, GLOB_NOCHECK|GLOB_MARK,
NULL, &g)) != 0) { NULL, &g)) != 0) {
if (r == GLOB_NOSPACE) if (r == GLOB_NOSPACE)
error("%s: too many glob matches", src); error("%s: too many glob matches", src);
@ -1585,7 +1585,7 @@ sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn)
* a GLOB_NOCHECK result. Check whether the unglobbed path * a GLOB_NOCHECK result. Check whether the unglobbed path
* exists so we can give a nice error message early. * exists so we can give a nice error message early.
*/ */
if (do_stat(conn, g.gl_pathv[0], 1) == NULL) { if (sftp_stat(conn, g.gl_pathv[0], 1, NULL) != 0) {
error("%s: %s", src, strerror(ENOENT)); error("%s: %s", src, strerror(ENOENT));
err = -1; err = -1;
goto out; goto out;
@ -1621,17 +1621,17 @@ sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn)
} }
if (dst_is_dir) if (dst_is_dir)
abs_dst = path_append(dst, filename); abs_dst = sftp_path_append(dst, filename);
else else
abs_dst = xstrdup(dst); abs_dst = xstrdup(dst);
debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { if (sftp_globpath_is_dir(g.gl_pathv[i]) && iamrecursive) {
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, if (sftp_download_dir(conn, g.gl_pathv[i], abs_dst,
pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) == -1) NULL, pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) == -1)
err = -1; err = -1;
} else { } else {
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, if (sftp_download(conn, g.gl_pathv[i], abs_dst, NULL,
pflag, 0, 0, 1) == -1) pflag, 0, 0, 1) == -1)
err = -1; err = -1;
} }
@ -1993,7 +1993,7 @@ throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to,
cleanup_exit(255); cleanup_exit(255);
memset(&g, 0, sizeof(g)); memset(&g, 0, sizeof(g));
targetisdir = remote_is_dir(to, target); targetisdir = sftp_remote_is_dir(to, target);
if (!targetisdir && targetshouldbedirectory) { if (!targetisdir && targetshouldbedirectory) {
error("%s: destination is not a directory", targ); error("%s: destination is not a directory", targ);
err = -1; err = -1;
@ -2001,7 +2001,7 @@ throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to,
} }
debug3_f("copying remote %s to remote %s", abs_src, target); debug3_f("copying remote %s to remote %s", abs_src, target);
if ((r = remote_glob(from, abs_src, GLOB_NOCHECK|GLOB_MARK, if ((r = sftp_glob(from, abs_src, GLOB_NOCHECK|GLOB_MARK,
NULL, &g)) != 0) { NULL, &g)) != 0) {
if (r == GLOB_NOSPACE) if (r == GLOB_NOSPACE)
error("%s: too many glob matches", src); error("%s: too many glob matches", src);
@ -2018,7 +2018,7 @@ throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to,
* a GLOB_NOCHECK result. Check whether the unglobbed path * a GLOB_NOCHECK result. Check whether the unglobbed path
* exists so we can give a nice error message early. * exists so we can give a nice error message early.
*/ */
if (do_stat(from, g.gl_pathv[0], 1) == NULL) { if (sftp_stat(from, g.gl_pathv[0], 1, NULL) != 0) {
error("%s: %s", src, strerror(ENOENT)); error("%s: %s", src, strerror(ENOENT));
err = -1; err = -1;
goto out; goto out;
@ -2034,18 +2034,18 @@ throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to,
} }
if (targetisdir) if (targetisdir)
abs_dst = path_append(target, filename); abs_dst = sftp_path_append(target, filename);
else else
abs_dst = xstrdup(target); abs_dst = xstrdup(target);
debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { if (sftp_globpath_is_dir(g.gl_pathv[i]) && iamrecursive) {
if (crossload_dir(from, to, g.gl_pathv[i], abs_dst, if (sftp_crossload_dir(from, to, g.gl_pathv[i], abs_dst,
NULL, pflag, SFTP_PROGRESS_ONLY, 1) == -1) NULL, pflag, SFTP_PROGRESS_ONLY, 1) == -1)
err = -1; err = -1;
} else { } else {
if (do_crossload(from, to, g.gl_pathv[i], abs_dst, NULL, if (sftp_crossload(from, to, g.gl_pathv[i], abs_dst,
pflag) == -1) NULL, pflag) == -1)
err = -1; err = -1;
} }
free(abs_dst); free(abs_dst);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: servconf.c,v 1.396 2023/07/17 05:26:38 djm Exp $ */ /* $OpenBSD: servconf.c,v 1.402 2023/09/08 06:34:24 djm Exp $ */
/* /*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved * All rights reserved
@ -643,7 +643,7 @@ static struct {
{ "macs", sMacs, SSHCFG_GLOBAL }, { "macs", sMacs, SSHCFG_GLOBAL },
{ "protocol", sIgnore, SSHCFG_GLOBAL }, { "protocol", sIgnore, SSHCFG_GLOBAL },
{ "gatewayports", sGatewayPorts, SSHCFG_ALL }, { "gatewayports", sGatewayPorts, SSHCFG_ALL },
{ "subsystem", sSubsystem, SSHCFG_GLOBAL }, { "subsystem", sSubsystem, SSHCFG_ALL },
{ "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, { "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
{ "persourcemaxstartups", sPerSourceMaxStartups, SSHCFG_GLOBAL }, { "persourcemaxstartups", sPerSourceMaxStartups, SSHCFG_GLOBAL },
{ "persourcenetblocksize", sPerSourceNetBlockSize, SSHCFG_GLOBAL }, { "persourcenetblocksize", sPerSourceNetBlockSize, SSHCFG_GLOBAL },
@ -1933,39 +1933,54 @@ process_server_config_line_depth(ServerOptions *options, char *line,
break; break;
case sSubsystem: case sSubsystem:
if (options->num_subsystems >= MAX_SUBSYSTEMS) {
fatal("%s line %d: too many subsystems defined.",
filename, linenum);
}
arg = argv_next(&ac, &av); arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') if (!arg || *arg == '\0')
fatal("%s line %d: %s missing argument.", fatal("%s line %d: %s missing argument.",
filename, linenum, keyword); filename, linenum, keyword);
if (!*activep) { if (!*activep) {
arg = argv_next(&ac, &av); argv_consume(&ac);
break; break;
} }
for (i = 0; i < options->num_subsystems; i++) found = 0;
if (strcmp(arg, options->subsystem_name[i]) == 0) for (i = 0; i < options->num_subsystems; i++) {
fatal("%s line %d: Subsystem '%s' " if (strcmp(arg, options->subsystem_name[i]) == 0) {
"already defined.", filename, linenum, arg); found = 1;
break;
}
}
if (found) {
debug("%s line %d: Subsystem '%s' already defined.",
filename, linenum, arg);
argv_consume(&ac);
break;
}
options->subsystem_name = xrecallocarray(
options->subsystem_name, options->num_subsystems,
options->num_subsystems + 1,
sizeof(*options->subsystem_name));
options->subsystem_command = xrecallocarray(
options->subsystem_command, options->num_subsystems,
options->num_subsystems + 1,
sizeof(*options->subsystem_command));
options->subsystem_args = xrecallocarray(
options->subsystem_args, options->num_subsystems,
options->num_subsystems + 1,
sizeof(*options->subsystem_args));
options->subsystem_name[options->num_subsystems] = xstrdup(arg); options->subsystem_name[options->num_subsystems] = xstrdup(arg);
arg = argv_next(&ac, &av); arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') if (!arg || *arg == '\0') {
fatal("%s line %d: Missing subsystem command.", fatal("%s line %d: Missing subsystem command.",
filename, linenum); filename, linenum);
options->subsystem_command[options->num_subsystems] = xstrdup(arg);
/* Collect arguments (separate to executable) */
p = xstrdup(arg);
len = strlen(p) + 1;
while ((arg = argv_next(&ac, &av)) != NULL) {
len += 1 + strlen(arg);
p = xreallocarray(p, 1, len);
strlcat(p, " ", len);
strlcat(p, arg, len);
} }
options->subsystem_args[options->num_subsystems] = p; options->subsystem_command[options->num_subsystems] =
xstrdup(arg);
/* Collect arguments (separate to executable) */
arg = argv_assemble(1, &arg); /* quote command correctly */
arg2 = argv_assemble(ac, av); /* rest of command */
xasprintf(&options->subsystem_args[options->num_subsystems],
"%s %s", arg, arg2);
free(arg2);
argv_consume(&ac);
options->num_subsystems++; options->num_subsystems++;
break; break;
@ -2030,7 +2045,7 @@ process_server_config_line_depth(ServerOptions *options, char *line,
fatal("%s line %d: %s integer value %s.", fatal("%s line %d: %s integer value %s.",
filename, linenum, keyword, errstr); filename, linenum, keyword, errstr);
} }
if (*activep) if (*activep && options->per_source_max_startups == -1)
options->per_source_max_startups = value; options->per_source_max_startups = value;
break; break;
@ -2679,6 +2694,47 @@ int parse_server_match_testspec(struct connection_info *ci, char *spec)
return 0; return 0;
} }
void
servconf_merge_subsystems(ServerOptions *dst, ServerOptions *src)
{
u_int i, j, found;
for (i = 0; i < src->num_subsystems; i++) {
found = 0;
for (j = 0; j < dst->num_subsystems; j++) {
if (strcmp(src->subsystem_name[i],
dst->subsystem_name[j]) == 0) {
found = 1;
break;
}
}
if (found) {
debug_f("override \"%s\"", dst->subsystem_name[j]);
free(dst->subsystem_command[j]);
free(dst->subsystem_args[j]);
dst->subsystem_command[j] =
xstrdup(src->subsystem_command[i]);
dst->subsystem_args[j] =
xstrdup(src->subsystem_args[i]);
continue;
}
debug_f("add \"%s\"", src->subsystem_name[i]);
dst->subsystem_name = xrecallocarray(
dst->subsystem_name, dst->num_subsystems,
dst->num_subsystems + 1, sizeof(*dst->subsystem_name));
dst->subsystem_command = xrecallocarray(
dst->subsystem_command, dst->num_subsystems,
dst->num_subsystems + 1, sizeof(*dst->subsystem_command));
dst->subsystem_args = xrecallocarray(
dst->subsystem_args, dst->num_subsystems,
dst->num_subsystems + 1, sizeof(*dst->subsystem_args));
j = dst->num_subsystems++;
dst->subsystem_name[j] = xstrdup(src->subsystem_name[i]);
dst->subsystem_command[j] = xstrdup(src->subsystem_command[i]);
dst->subsystem_args[j] = xstrdup(src->subsystem_args[i]);
}
}
/* /*
* Copy any supported values that are set. * Copy any supported values that are set.
* *
@ -2785,6 +2841,9 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
free(dst->chroot_directory); free(dst->chroot_directory);
dst->chroot_directory = NULL; dst->chroot_directory = NULL;
} }
/* Subsystems require merging. */
servconf_merge_subsystems(dst, src);
} }
#undef M_CP_INTOPT #undef M_CP_INTOPT

View File

@ -1,4 +1,4 @@
/* $OpenBSD: servconf.h,v 1.159 2023/01/17 09:44:48 djm Exp $ */ /* $OpenBSD: servconf.h,v 1.160 2023/09/06 23:35:35 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -20,8 +20,6 @@
#define MAX_PORTS 256 /* Max # ports. */ #define MAX_PORTS 256 /* Max # ports. */
#define MAX_SUBSYSTEMS 256 /* Max # subsystems. */
/* permit_root_login */ /* permit_root_login */
#define PERMIT_NOT_SET -1 #define PERMIT_NOT_SET -1
#define PERMIT_NO 0 #define PERMIT_NO 0
@ -165,9 +163,9 @@ typedef struct {
char **deny_groups; char **deny_groups;
u_int num_subsystems; u_int num_subsystems;
char *subsystem_name[MAX_SUBSYSTEMS]; char **subsystem_name;
char *subsystem_command[MAX_SUBSYSTEMS]; char **subsystem_command;
char *subsystem_args[MAX_SUBSYSTEMS]; char **subsystem_args;
u_int num_accept_env; u_int num_accept_env;
char **accept_env; char **accept_env;
@ -294,6 +292,9 @@ TAILQ_HEAD(include_list, include_item);
M_CP_STRARRAYOPT(permitted_listens, num_permitted_listens); \ M_CP_STRARRAYOPT(permitted_listens, num_permitted_listens); \
M_CP_STRARRAYOPT(channel_timeouts, num_channel_timeouts); \ M_CP_STRARRAYOPT(channel_timeouts, num_channel_timeouts); \
M_CP_STRARRAYOPT(log_verbose, num_log_verbose); \ M_CP_STRARRAYOPT(log_verbose, num_log_verbose); \
M_CP_STRARRAYOPT(subsystem_name, num_subsystems); \
M_CP_STRARRAYOPT(subsystem_command, num_subsystems); \
M_CP_STRARRAYOPT(subsystem_args, num_subsystems); \
} while (0) } while (0)
struct connection_info *get_connection_info(struct ssh *, int, int); struct connection_info *get_connection_info(struct ssh *, int, int);
@ -310,6 +311,7 @@ void parse_server_match_config(ServerOptions *,
struct include_list *includes, struct connection_info *); struct include_list *includes, struct connection_info *);
int parse_server_match_testspec(struct connection_info *, char *); int parse_server_match_testspec(struct connection_info *, char *);
int server_match_spec_complete(struct connection_info *); int server_match_spec_complete(struct connection_info *);
void servconf_merge_subsystems(ServerOptions *, ServerOptions *);
void copy_set_server_options(ServerOptions *, ServerOptions *, int); void copy_set_server_options(ServerOptions *, ServerOptions *, int);
void dump_config(ServerOptions *); void dump_config(ServerOptions *);
char *derelativise_path(const char *); char *derelativise_path(const char *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: serverloop.c,v 1.236 2023/03/08 04:43:12 guenther Exp $ */ /* $OpenBSD: serverloop.c,v 1.237 2023/08/21 04:59:54 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -253,7 +253,7 @@ wait_until_can_do_something(struct ssh *ssh,
/* ClientAliveInterval probing */ /* ClientAliveInterval probing */
if (client_alive_scheduled) { if (client_alive_scheduled) {
if (ret == 0 && if (ret == 0 &&
now > last_client_time + options.client_alive_interval) { now >= last_client_time + options.client_alive_interval) {
/* ppoll timed out and we're due to probe */ /* ppoll timed out and we're due to probe */
client_alive_check(ssh); client_alive_check(ssh);
last_client_time = now; last_client_time = now;

View File

@ -1,4 +1,4 @@
/* $OpenBSD: session.c,v 1.335 2023/03/07 06:09:14 dtucker Exp $ */ /* $OpenBSD: session.c,v 1.336 2023/08/10 23:05:48 djm Exp $ */
/* /*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved * All rights reserved
@ -2380,17 +2380,17 @@ session_exit_message(struct ssh *ssh, Session *s, int status)
{ {
Channel *c; Channel *c;
int r; int r;
char *note = NULL;
if ((c = channel_lookup(ssh, s->chanid)) == NULL) if ((c = channel_lookup(ssh, s->chanid)) == NULL)
fatal_f("session %d: no channel %d", s->self, s->chanid); fatal_f("session %d: no channel %d", s->self, s->chanid);
debug_f("session %d channel %d pid %ld",
s->self, s->chanid, (long)s->pid);
if (WIFEXITED(status)) { if (WIFEXITED(status)) {
channel_request_start(ssh, s->chanid, "exit-status", 0); channel_request_start(ssh, s->chanid, "exit-status", 0);
if ((r = sshpkt_put_u32(ssh, WEXITSTATUS(status))) != 0 || if ((r = sshpkt_put_u32(ssh, WEXITSTATUS(status))) != 0 ||
(r = sshpkt_send(ssh)) != 0) (r = sshpkt_send(ssh)) != 0)
sshpkt_fatal(ssh, r, "%s: exit reply", __func__); sshpkt_fatal(ssh, r, "%s: exit reply", __func__);
xasprintf(&note, "exit %d", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) { } else if (WIFSIGNALED(status)) {
channel_request_start(ssh, s->chanid, "exit-signal", 0); channel_request_start(ssh, s->chanid, "exit-signal", 0);
#ifndef WCOREDUMP #ifndef WCOREDUMP
@ -2402,11 +2402,18 @@ session_exit_message(struct ssh *ssh, Session *s, int status)
(r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 ||
(r = sshpkt_send(ssh)) != 0) (r = sshpkt_send(ssh)) != 0)
sshpkt_fatal(ssh, r, "%s: exit reply", __func__); sshpkt_fatal(ssh, r, "%s: exit reply", __func__);
xasprintf(&note, "signal %d%s", WTERMSIG(status),
WCOREDUMP(status) ? " core dumped" : "");
} else { } else {
/* Some weird exit cause. Just exit. */ /* Some weird exit cause. Just exit. */
ssh_packet_disconnect(ssh, "wait returned status %04x.", status); ssh_packet_disconnect(ssh, "wait returned status %04x.",
status);
} }
debug_f("session %d channel %d pid %ld %s", s->self, s->chanid,
(long)s->pid, note == NULL ? "UNKNOWN" : note);
free(note);
/* disconnect channel */ /* disconnect channel */
debug_f("release channel %d", s->chanid); debug_f("release channel %d", s->chanid);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-client.c,v 1.171 2023/04/30 22:54:22 djm Exp $ */ /* $OpenBSD: sftp-client.c,v 1.174 2023/09/08 06:10:02 djm Exp $ */
/* /*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
* *
@ -342,16 +342,17 @@ get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len,
return handle; return handle;
} }
/* XXX returning &static is error-prone. Refactor to fill *Attrib argument */ static int
static Attrib * get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet, Attrib *a)
get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
{ {
struct sshbuf *msg; struct sshbuf *msg;
u_int id; u_int id;
u_char type; u_char type;
int r; int r;
static Attrib a; Attrib attr;
if (a != NULL)
memset(a, '\0', sizeof(*a));
if ((msg = sshbuf_new()) == NULL) if ((msg = sshbuf_new()) == NULL)
fatal_f("sshbuf_new failed"); fatal_f("sshbuf_new failed");
get_msg(conn, msg); get_msg(conn, msg);
@ -372,21 +373,24 @@ get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
else else
error("stat remote: %s", fx2txt(status)); error("stat remote: %s", fx2txt(status));
sshbuf_free(msg); sshbuf_free(msg);
return(NULL); return -1;
} else if (type != SSH2_FXP_ATTRS) { } else if (type != SSH2_FXP_ATTRS) {
fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
SSH2_FXP_ATTRS, type); SSH2_FXP_ATTRS, type);
} }
if ((r = decode_attrib(msg, &a)) != 0) { if ((r = decode_attrib(msg, &attr)) != 0) {
error_fr(r, "decode_attrib"); error_fr(r, "decode_attrib");
sshbuf_free(msg); sshbuf_free(msg);
return NULL; return -1;
} }
/* success */
if (a != NULL)
*a = attr;
debug3("Received stat reply T:%u I:%u F:0x%04x M:%05o", debug3("Received stat reply T:%u I:%u F:0x%04x M:%05o",
type, id, a.flags, a.perm); type, id, attr.flags, attr.perm);
sshbuf_free(msg); sshbuf_free(msg);
return &a; return 0;
} }
static int static int
@ -449,7 +453,7 @@ get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
} }
struct sftp_conn * struct sftp_conn *
do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, sftp_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
u_int64_t limit_kbps) u_int64_t limit_kbps)
{ {
u_char type; u_char type;
@ -560,7 +564,7 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
/* Query the server for its limits */ /* Query the server for its limits */
if (ret->exts & SFTP_EXT_LIMITS) { if (ret->exts & SFTP_EXT_LIMITS) {
struct sftp_limits limits; struct sftp_limits limits;
if (do_limits(ret, &limits) != 0) if (sftp_get_limits(ret, &limits) != 0)
fatal_f("limits failed"); fatal_f("limits failed");
/* If the caller did not specify, find a good value */ /* If the caller did not specify, find a good value */
@ -614,7 +618,7 @@ sftp_proto_version(struct sftp_conn *conn)
} }
int int
do_limits(struct sftp_conn *conn, struct sftp_limits *limits) sftp_get_limits(struct sftp_conn *conn, struct sftp_limits *limits)
{ {
u_int id, msg_id; u_int id, msg_id;
u_char type; u_char type;
@ -668,7 +672,7 @@ do_limits(struct sftp_conn *conn, struct sftp_limits *limits)
} }
int int
do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len) sftp_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len)
{ {
u_int id, status; u_int id, status;
struct sshbuf *msg; struct sshbuf *msg;
@ -696,7 +700,7 @@ do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len)
static int static int
do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag, sftp_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag,
SFTP_DIRENT ***dir) SFTP_DIRENT ***dir)
{ {
struct sshbuf *msg; struct sshbuf *msg;
@ -821,16 +825,16 @@ do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag,
out: out:
sshbuf_free(msg); sshbuf_free(msg);
do_close(conn, handle, handle_len); sftp_close(conn, handle, handle_len);
free(handle); free(handle);
if (status != 0 && dir != NULL) { if (status != 0 && dir != NULL) {
/* Don't return results on error */ /* Don't return results on error */
free_sftp_dirents(*dir); sftp_free_dirents(*dir);
*dir = NULL; *dir = NULL;
} else if (interrupted && dir != NULL && *dir != NULL) { } else if (interrupted && dir != NULL && *dir != NULL) {
/* Don't return partial matches on interrupt */ /* Don't return partial matches on interrupt */
free_sftp_dirents(*dir); sftp_free_dirents(*dir);
*dir = xcalloc(1, sizeof(**dir)); *dir = xcalloc(1, sizeof(**dir));
**dir = NULL; **dir = NULL;
} }
@ -839,12 +843,12 @@ do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag,
} }
int int
do_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir) sftp_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir)
{ {
return(do_lsreaddir(conn, path, 0, dir)); return sftp_lsreaddir(conn, path, 0, dir);
} }
void free_sftp_dirents(SFTP_DIRENT **s) void sftp_free_dirents(SFTP_DIRENT **s)
{ {
int i; int i;
@ -859,7 +863,7 @@ void free_sftp_dirents(SFTP_DIRENT **s)
} }
int int
do_rm(struct sftp_conn *conn, const char *path) sftp_rm(struct sftp_conn *conn, const char *path)
{ {
u_int status, id; u_int status, id;
@ -874,7 +878,7 @@ do_rm(struct sftp_conn *conn, const char *path)
} }
int int
do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag) sftp_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag)
{ {
u_int status, id; u_int status, id;
@ -892,7 +896,7 @@ do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag)
} }
int int
do_rmdir(struct sftp_conn *conn, const char *path) sftp_rmdir(struct sftp_conn *conn, const char *path)
{ {
u_int status, id; u_int status, id;
@ -909,8 +913,8 @@ do_rmdir(struct sftp_conn *conn, const char *path)
return status == SSH2_FX_OK ? 0 : -1; return status == SSH2_FX_OK ? 0 : -1;
} }
Attrib * int
do_stat(struct sftp_conn *conn, const char *path, int quiet) sftp_stat(struct sftp_conn *conn, const char *path, int quiet, Attrib *a)
{ {
u_int id; u_int id;
@ -922,33 +926,31 @@ do_stat(struct sftp_conn *conn, const char *path, int quiet)
conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
path, strlen(path)); path, strlen(path));
return(get_decode_stat(conn, id, quiet)); return get_decode_stat(conn, id, quiet, a);
} }
Attrib * int
do_lstat(struct sftp_conn *conn, const char *path, int quiet) sftp_lstat(struct sftp_conn *conn, const char *path, int quiet, Attrib *a)
{ {
u_int id; u_int id;
if (conn->version == 0) { if (conn->version == 0) {
if (quiet) do_log2(quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO,
debug("Server version does not support lstat operation"); "Server version does not support lstat operation");
else return sftp_stat(conn, path, quiet, a);
logit("Server version does not support lstat operation");
return(do_stat(conn, path, quiet));
} }
id = conn->msg_id++; id = conn->msg_id++;
send_string_request(conn, id, SSH2_FXP_LSTAT, path, send_string_request(conn, id, SSH2_FXP_LSTAT, path,
strlen(path)); strlen(path));
return(get_decode_stat(conn, id, quiet)); return get_decode_stat(conn, id, quiet, a);
} }
#ifdef notyet #ifdef notyet
Attrib * int
do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, sftp_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
int quiet) int quiet, Attrib *a)
{ {
u_int id; u_int id;
@ -958,12 +960,12 @@ do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
send_string_request(conn, id, SSH2_FXP_FSTAT, handle, send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
handle_len); handle_len);
return(get_decode_stat(conn, id, quiet)); return get_decode_stat(conn, id, quiet, a);
} }
#endif #endif
int int
do_setstat(struct sftp_conn *conn, const char *path, Attrib *a) sftp_setstat(struct sftp_conn *conn, const char *path, Attrib *a)
{ {
u_int status, id; u_int status, id;
@ -981,7 +983,7 @@ do_setstat(struct sftp_conn *conn, const char *path, Attrib *a)
} }
int int
do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, sftp_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
Attrib *a) Attrib *a)
{ {
u_int status, id; u_int status, id;
@ -1001,7 +1003,7 @@ do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
/* Implements both the realpath and expand-path operations */ /* Implements both the realpath and expand-path operations */
static char * static char *
do_realpath_expand(struct sftp_conn *conn, const char *path, int expand) sftp_realpath_expand(struct sftp_conn *conn, const char *path, int expand)
{ {
struct sshbuf *msg; struct sshbuf *msg;
u_int expected_id, count, id; u_int expected_id, count, id;
@ -1076,31 +1078,31 @@ do_realpath_expand(struct sftp_conn *conn, const char *path, int expand)
} }
char * char *
do_realpath(struct sftp_conn *conn, const char *path) sftp_realpath(struct sftp_conn *conn, const char *path)
{ {
return do_realpath_expand(conn, path, 0); return sftp_realpath_expand(conn, path, 0);
} }
int int
can_expand_path(struct sftp_conn *conn) sftp_can_expand_path(struct sftp_conn *conn)
{ {
return (conn->exts & SFTP_EXT_PATH_EXPAND) != 0; return (conn->exts & SFTP_EXT_PATH_EXPAND) != 0;
} }
char * char *
do_expand_path(struct sftp_conn *conn, const char *path) sftp_expand_path(struct sftp_conn *conn, const char *path)
{ {
if (!can_expand_path(conn)) { if (!sftp_can_expand_path(conn)) {
debug3_f("no server support, fallback to realpath"); debug3_f("no server support, fallback to realpath");
return do_realpath_expand(conn, path, 0); return sftp_realpath_expand(conn, path, 0);
} }
return do_realpath_expand(conn, path, 1); return sftp_realpath_expand(conn, path, 1);
} }
int int
do_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath) sftp_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath)
{ {
Attrib junk, *a; Attrib junk, attr;
struct sshbuf *msg; struct sshbuf *msg;
u_char *old_handle, *new_handle; u_char *old_handle, *new_handle;
u_int mode, status, id; u_int mode, status, id;
@ -1114,14 +1116,14 @@ do_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath)
} }
/* Make sure the file exists, and we can copy its perms */ /* Make sure the file exists, and we can copy its perms */
if ((a = do_stat(conn, oldpath, 0)) == NULL) if (sftp_stat(conn, oldpath, 0, &attr) != 0)
return -1; return -1;
/* Do not preserve set[ug]id here, as we do not preserve ownership */ /* Do not preserve set[ug]id here, as we do not preserve ownership */
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { if (attr.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
mode = a->perm & 0777; mode = attr.perm & 0777;
if (!S_ISREG(a->perm)) { if (!S_ISREG(attr.perm)) {
error("Cannot copy non-regular file: %s", oldpath); error("Cannot copy non-regular file: %s", oldpath);
return -1; return -1;
} }
@ -1131,9 +1133,9 @@ do_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath)
} }
/* Set up the new perms for the new file */ /* Set up the new perms for the new file */
attrib_clear(a); attrib_clear(&attr);
a->perm = mode; attr.perm = mode;
a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; attr.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
if ((msg = sshbuf_new()) == NULL) if ((msg = sshbuf_new()) == NULL)
fatal("%s: sshbuf_new failed", __func__); fatal("%s: sshbuf_new failed", __func__);
@ -1167,7 +1169,7 @@ do_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath)
(r = sshbuf_put_cstring(msg, newpath)) != 0 || (r = sshbuf_put_cstring(msg, newpath)) != 0 ||
(r = sshbuf_put_u32(msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT| (r = sshbuf_put_u32(msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|
SSH2_FXF_TRUNC)) != 0 || SSH2_FXF_TRUNC)) != 0 ||
(r = encode_attrib(msg, a)) != 0) (r = encode_attrib(msg, &attr)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r)); fatal("%s: buffer error: %s", __func__, ssh_err(r));
send_msg(conn, msg); send_msg(conn, msg);
debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, newpath); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, newpath);
@ -1204,8 +1206,8 @@ do_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath)
/* Clean up everything */ /* Clean up everything */
sshbuf_free(msg); sshbuf_free(msg);
do_close(conn, old_handle, old_handle_len); sftp_close(conn, old_handle, old_handle_len);
do_close(conn, new_handle, new_handle_len); sftp_close(conn, new_handle, new_handle_len);
free(old_handle); free(old_handle);
free(new_handle); free(new_handle);
@ -1213,7 +1215,7 @@ do_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath)
} }
int int
do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath, sftp_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath,
int force_legacy) int force_legacy)
{ {
struct sshbuf *msg; struct sshbuf *msg;
@ -1258,7 +1260,7 @@ do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath,
} }
int int
do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) sftp_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
{ {
struct sshbuf *msg; struct sshbuf *msg;
u_int status, id; u_int status, id;
@ -1296,7 +1298,7 @@ do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
} }
int int
do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) sftp_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
{ {
struct sshbuf *msg; struct sshbuf *msg;
u_int status, id; u_int status, id;
@ -1332,7 +1334,7 @@ do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
} }
int int
do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len) sftp_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len)
{ {
struct sshbuf *msg; struct sshbuf *msg;
u_int status, id; u_int status, id;
@ -1365,7 +1367,7 @@ do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len)
#ifdef notyet #ifdef notyet
char * char *
do_readlink(struct sftp_conn *conn, const char *path) sftp_readlink(struct sftp_conn *conn, const char *path)
{ {
struct sshbuf *msg; struct sshbuf *msg;
u_int expected_id, count, id; u_int expected_id, count, id;
@ -1423,7 +1425,7 @@ do_readlink(struct sftp_conn *conn, const char *path)
#endif #endif
int int
do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, sftp_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
int quiet) int quiet)
{ {
struct sshbuf *msg; struct sshbuf *msg;
@ -1454,7 +1456,7 @@ do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
#ifdef notyet #ifdef notyet
int int
do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len, sftp_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
struct sftp_statvfs *st, int quiet) struct sftp_statvfs *st, int quiet)
{ {
struct sshbuf *msg; struct sshbuf *msg;
@ -1484,7 +1486,7 @@ do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
#endif #endif
int int
do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a) sftp_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a)
{ {
struct sshbuf *msg; struct sshbuf *msg;
u_int status, id; u_int status, id;
@ -1592,7 +1594,7 @@ progress_meter_path(const char *path)
} }
int int
do_download(struct sftp_conn *conn, const char *remote_path, sftp_download(struct sftp_conn *conn, const char *remote_path,
const char *local_path, Attrib *a, int preserve_flag, int resume_flag, const char *local_path, Attrib *a, int preserve_flag, int resume_flag,
int fsync_flag, int inplace_flag) int fsync_flag, int inplace_flag)
{ {
@ -1608,14 +1610,18 @@ do_download(struct sftp_conn *conn, const char *remote_path,
struct requests requests; struct requests requests;
struct request *req; struct request *req;
u_char type; u_char type;
Attrib attr;
debug2_f("download remote \"%s\" to local \"%s\"", debug2_f("download remote \"%s\" to local \"%s\"",
remote_path, local_path); remote_path, local_path);
TAILQ_INIT(&requests); TAILQ_INIT(&requests);
if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) if (a == NULL) {
if (sftp_stat(conn, remote_path, 0, &attr) != 0)
return -1; return -1;
a = &attr;
}
/* Do not preserve set[ug]id here, as we do not preserve ownership */ /* Do not preserve set[ug]id here, as we do not preserve ownership */
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
@ -1661,7 +1667,7 @@ do_download(struct sftp_conn *conn, const char *remote_path,
error("Unable to resume download of \"%s\": " error("Unable to resume download of \"%s\": "
"local file is larger than remote", local_path); "local file is larger than remote", local_path);
fail: fail:
do_close(conn, handle, handle_len); sftp_close(conn, handle, handle_len);
free(handle); free(handle);
if (local_fd != -1) if (local_fd != -1)
close(local_fd); close(local_fd);
@ -1836,14 +1842,14 @@ do_download(struct sftp_conn *conn, const char *remote_path,
if (read_error) { if (read_error) {
error("read remote \"%s\" : %s", remote_path, fx2txt(status)); error("read remote \"%s\" : %s", remote_path, fx2txt(status));
status = -1; status = -1;
do_close(conn, handle, handle_len); sftp_close(conn, handle, handle_len);
} else if (write_error) { } else if (write_error) {
error("write local \"%s\": %s", local_path, error("write local \"%s\": %s", local_path,
strerror(write_errno)); strerror(write_errno));
status = SSH2_FX_FAILURE; status = SSH2_FX_FAILURE;
do_close(conn, handle, handle_len); sftp_close(conn, handle, handle_len);
} else { } else {
if (do_close(conn, handle, handle_len) != 0 || interrupted) if (sftp_close(conn, handle, handle_len) != 0 || interrupted)
status = SSH2_FX_FAILURE; status = SSH2_FX_FAILURE;
else else
status = SSH2_FX_OK; status = SSH2_FX_OK;
@ -1890,6 +1896,7 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
SFTP_DIRENT **dir_entries; SFTP_DIRENT **dir_entries;
char *filename, *new_src = NULL, *new_dst = NULL; char *filename, *new_src = NULL, *new_dst = NULL;
mode_t mode = 0777, tmpmode = mode; mode_t mode = 0777, tmpmode = mode;
Attrib *a, ldirattrib, lsym;
if (depth >= MAX_DIR_DEPTH) { if (depth >= MAX_DIR_DEPTH) {
error("Maximum directory depth exceeded: %d levels", depth); error("Maximum directory depth exceeded: %d levels", depth);
@ -1898,11 +1905,13 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
debug2_f("download dir remote \"%s\" to local \"%s\"", src, dst); debug2_f("download dir remote \"%s\" to local \"%s\"", src, dst);
if (dirattrib == NULL && if (dirattrib == NULL) {
(dirattrib = do_stat(conn, src, 1)) == NULL) { if (sftp_stat(conn, src, 1, &ldirattrib) != 0) {
error("stat remote \"%s\" directory failed", src); error("stat remote \"%s\" directory failed", src);
return -1; return -1;
} }
dirattrib = &ldirattrib;
}
if (!S_ISDIR(dirattrib->perm)) { if (!S_ISDIR(dirattrib->perm)) {
error("\"%s\" is not a directory", src); error("\"%s\" is not a directory", src);
return -1; return -1;
@ -1923,7 +1932,7 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
return -1; return -1;
} }
if (do_readdir(conn, src, &dir_entries) == -1) { if (sftp_readdir(conn, src, &dir_entries) == -1) {
error("remote readdir \"%s\" failed", src); error("remote readdir \"%s\" failed", src);
return -1; return -1;
} }
@ -1933,28 +1942,36 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
free(new_src); free(new_src);
filename = dir_entries[i]->filename; filename = dir_entries[i]->filename;
new_dst = path_append(dst, filename); new_dst = sftp_path_append(dst, filename);
new_src = path_append(src, filename); new_src = sftp_path_append(src, filename);
if (S_ISDIR(dir_entries[i]->a.perm)) { a = &dir_entries[i]->a;
if (S_ISLNK(a->perm)) {
if (!follow_link_flag) {
logit("download \"%s\": not a regular file",
new_src);
continue;
}
/* Replace the stat contents with the symlink target */
if (sftp_stat(conn, new_src, 1, &lsym) != 0) {
logit("remote stat \"%s\" failed", new_src);
ret = -1;
continue;
}
a = &lsym;
}
if (S_ISDIR(a->perm)) {
if (strcmp(filename, ".") == 0 || if (strcmp(filename, ".") == 0 ||
strcmp(filename, "..") == 0) strcmp(filename, "..") == 0)
continue; continue;
if (download_dir_internal(conn, new_src, new_dst, if (download_dir_internal(conn, new_src, new_dst,
depth + 1, &(dir_entries[i]->a), preserve_flag, depth + 1, a, preserve_flag,
print_flag, resume_flag, print_flag, resume_flag,
fsync_flag, follow_link_flag, inplace_flag) == -1) fsync_flag, follow_link_flag, inplace_flag) == -1)
ret = -1; ret = -1;
} else if (S_ISREG(dir_entries[i]->a.perm) || } else if (S_ISREG(a->perm)) {
(follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { if (sftp_download(conn, new_src, new_dst, a,
/*
* If this is a symlink then don't send the link's
* Attrib. do_download() will do a FXP_STAT operation
* and get the link target's attributes.
*/
if (do_download(conn, new_src, new_dst,
S_ISLNK(dir_entries[i]->a.perm) ? NULL :
&(dir_entries[i]->a),
preserve_flag, resume_flag, fsync_flag, preserve_flag, resume_flag, fsync_flag,
inplace_flag) == -1) { inplace_flag) == -1) {
error("Download of file %s to %s failed", error("Download of file %s to %s failed",
@ -1986,20 +2003,20 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
error("local chmod directory \"%s\": %s", dst, error("local chmod directory \"%s\": %s", dst,
strerror(errno)); strerror(errno));
free_sftp_dirents(dir_entries); sftp_free_dirents(dir_entries);
return ret; return ret;
} }
int int
download_dir(struct sftp_conn *conn, const char *src, const char *dst, sftp_download_dir(struct sftp_conn *conn, const char *src, const char *dst,
Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
int fsync_flag, int follow_link_flag, int inplace_flag) int fsync_flag, int follow_link_flag, int inplace_flag)
{ {
char *src_canon; char *src_canon;
int ret; int ret;
if ((src_canon = do_realpath(conn, src)) == NULL) { if ((src_canon = sftp_realpath(conn, src)) == NULL) {
error("download \"%s\": path canonicalization failed", src); error("download \"%s\": path canonicalization failed", src);
return -1; return -1;
} }
@ -2012,7 +2029,7 @@ download_dir(struct sftp_conn *conn, const char *src, const char *dst,
} }
int int
do_upload(struct sftp_conn *conn, const char *local_path, sftp_upload(struct sftp_conn *conn, const char *local_path,
const char *remote_path, int preserve_flag, int resume, const char *remote_path, int preserve_flag, int resume,
int fsync_flag, int inplace_flag) int fsync_flag, int inplace_flag)
{ {
@ -2022,7 +2039,7 @@ do_upload(struct sftp_conn *conn, const char *local_path,
u_char type, *handle, *data; u_char type, *handle, *data;
struct sshbuf *msg; struct sshbuf *msg;
struct stat sb; struct stat sb;
Attrib a, t, *c = NULL; Attrib a, t, c;
u_int32_t startid, ackid; u_int32_t startid, ackid;
u_int64_t highwater = 0, maxack = 0; u_int64_t highwater = 0, maxack = 0;
struct request *ack = NULL; struct request *ack = NULL;
@ -2058,19 +2075,19 @@ do_upload(struct sftp_conn *conn, const char *local_path,
if (resume) { if (resume) {
/* Get remote file size if it exists */ /* Get remote file size if it exists */
if ((c = do_stat(conn, remote_path, 0)) == NULL) { if (sftp_stat(conn, remote_path, 0, &c) != 0) {
close(local_fd); close(local_fd);
return -1; return -1;
} }
if ((off_t)c->size >= sb.st_size) { if ((off_t)c.size >= sb.st_size) {
error("resume \"%s\": destination file " error("resume \"%s\": destination file "
"same size or larger", local_path); "same size or larger", local_path);
close(local_fd); close(local_fd);
return -1; return -1;
} }
if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) { if (lseek(local_fd, (off_t)c.size, SEEK_SET) == -1) {
close(local_fd); close(local_fd);
return -1; return -1;
} }
@ -2094,7 +2111,7 @@ do_upload(struct sftp_conn *conn, const char *local_path,
data = xmalloc(conn->upload_buflen); data = xmalloc(conn->upload_buflen);
/* Read from local and write to remote */ /* Read from local and write to remote */
offset = progress_counter = (resume ? c->size : 0); offset = progress_counter = (resume ? c.size : 0);
if (showprogress) { if (showprogress) {
start_progress_meter(progress_meter_path(local_path), start_progress_meter(progress_meter_path(local_path),
sb.st_size, &progress_counter); sb.st_size, &progress_counter);
@ -2206,7 +2223,7 @@ do_upload(struct sftp_conn *conn, const char *local_path,
attrib_clear(&t); attrib_clear(&t);
t.flags = SSH2_FILEXFER_ATTR_SIZE; t.flags = SSH2_FILEXFER_ATTR_SIZE;
t.size = highwater; t.size = highwater;
do_fsetstat(conn, handle, handle_len, &t); sftp_fsetstat(conn, handle, handle_len, &t);
} }
if (close(local_fd) == -1) { if (close(local_fd) == -1) {
@ -2216,12 +2233,12 @@ do_upload(struct sftp_conn *conn, const char *local_path,
/* Override umask and utimes if asked */ /* Override umask and utimes if asked */
if (preserve_flag) if (preserve_flag)
do_fsetstat(conn, handle, handle_len, &a); sftp_fsetstat(conn, handle, handle_len, &a);
if (fsync_flag) if (fsync_flag)
(void)do_fsync(conn, handle, handle_len); (void)sftp_fsync(conn, handle, handle_len);
if (do_close(conn, handle, handle_len) != 0) if (sftp_close(conn, handle, handle_len) != 0)
status = SSH2_FX_FAILURE; status = SSH2_FX_FAILURE;
free(handle); free(handle);
@ -2239,7 +2256,7 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
struct dirent *dp; struct dirent *dp;
char *filename, *new_src = NULL, *new_dst = NULL; char *filename, *new_src = NULL, *new_dst = NULL;
struct stat sb; struct stat sb;
Attrib a, *dirattrib; Attrib a, dirattrib;
u_int32_t saved_perm; u_int32_t saved_perm;
debug2_f("upload local dir \"%s\" to remote \"%s\"", src, dst); debug2_f("upload local dir \"%s\" to remote \"%s\"", src, dst);
@ -2275,10 +2292,10 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
*/ */
saved_perm = a.perm; saved_perm = a.perm;
a.perm |= (S_IWUSR|S_IXUSR); a.perm |= (S_IWUSR|S_IXUSR);
if (do_mkdir(conn, dst, &a, 0) != 0) { if (sftp_mkdir(conn, dst, &a, 0) != 0) {
if ((dirattrib = do_stat(conn, dst, 0)) == NULL) if (sftp_stat(conn, dst, 0, &dirattrib) != 0)
return -1; return -1;
if (!S_ISDIR(dirattrib->perm)) { if (!S_ISDIR(dirattrib.perm)) {
error("\"%s\" exists but is not a directory", dst); error("\"%s\" exists but is not a directory", dst);
return -1; return -1;
} }
@ -2296,25 +2313,37 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
free(new_dst); free(new_dst);
free(new_src); free(new_src);
filename = dp->d_name; filename = dp->d_name;
new_dst = path_append(dst, filename); new_dst = sftp_path_append(dst, filename);
new_src = path_append(src, filename); new_src = sftp_path_append(src, filename);
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
continue;
if (lstat(new_src, &sb) == -1) { if (lstat(new_src, &sb) == -1) {
logit("local lstat \"%s\": %s", filename, logit("local lstat \"%s\": %s", filename,
strerror(errno)); strerror(errno));
ret = -1; ret = -1;
} else if (S_ISDIR(sb.st_mode)) {
if (strcmp(filename, ".") == 0 ||
strcmp(filename, "..") == 0)
continue; continue;
}
if (S_ISLNK(sb.st_mode)) {
if (!follow_link_flag) {
logit("%s: not a regular file", filename);
continue;
}
/* Replace the stat contents with the symlink target */
if (stat(new_src, &sb) == -1) {
logit("local stat \"%s\": %s", filename,
strerror(errno));
ret = -1;
continue;
}
}
if (S_ISDIR(sb.st_mode)) {
if (upload_dir_internal(conn, new_src, new_dst, if (upload_dir_internal(conn, new_src, new_dst,
depth + 1, preserve_flag, print_flag, resume, depth + 1, preserve_flag, print_flag, resume,
fsync_flag, follow_link_flag, inplace_flag) == -1) fsync_flag, follow_link_flag, inplace_flag) == -1)
ret = -1; ret = -1;
} else if (S_ISREG(sb.st_mode) || } else if (S_ISREG(sb.st_mode)) {
(follow_link_flag && S_ISLNK(sb.st_mode))) { if (sftp_upload(conn, new_src, new_dst,
if (do_upload(conn, new_src, new_dst,
preserve_flag, resume, fsync_flag, preserve_flag, resume, fsync_flag,
inplace_flag) == -1) { inplace_flag) == -1) {
error("upload \"%s\" to \"%s\" failed", error("upload \"%s\" to \"%s\" failed",
@ -2327,21 +2356,21 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
free(new_dst); free(new_dst);
free(new_src); free(new_src);
do_setstat(conn, dst, &a); sftp_setstat(conn, dst, &a);
(void) closedir(dirp); (void) closedir(dirp);
return ret; return ret;
} }
int int
upload_dir(struct sftp_conn *conn, const char *src, const char *dst, sftp_upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
int preserve_flag, int print_flag, int resume, int fsync_flag, int preserve_flag, int print_flag, int resume, int fsync_flag,
int follow_link_flag, int inplace_flag) int follow_link_flag, int inplace_flag)
{ {
char *dst_canon; char *dst_canon;
int ret; int ret;
if ((dst_canon = do_realpath(conn, dst)) == NULL) { if ((dst_canon = sftp_realpath(conn, dst)) == NULL) {
error("upload \"%s\": path canonicalization failed", dst); error("upload \"%s\": path canonicalization failed", dst);
return -1; return -1;
} }
@ -2400,13 +2429,13 @@ handle_dest_replies(struct sftp_conn *to, const char *to_path, int synchronous,
*write_errorp = status; *write_errorp = status;
} }
/* /*
* XXX this doesn't do full reply matching like do_upload and * XXX this doesn't do full reply matching like sftp_upload and
* so cannot gracefully truncate terminated uploads at a * so cannot gracefully truncate terminated uploads at a
* high-water mark. ATM the only caller of this function (scp) * high-water mark. ATM the only caller of this function (scp)
* doesn't support transfer resumption, so this doesn't matter * doesn't support transfer resumption, so this doesn't matter
* a whole lot. * a whole lot.
* *
* To be safe, do_crossload truncates the destination file to * To be safe, sftp_crossload truncates the destination file to
* zero length on upload failure, since we can't trust the * zero length on upload failure, since we can't trust the
* server not to have reordered replies that could have * server not to have reordered replies that could have
* inserted holes where none existed in the source file. * inserted holes where none existed in the source file.
@ -2421,7 +2450,7 @@ handle_dest_replies(struct sftp_conn *to, const char *to_path, int synchronous,
} }
int int
do_crossload(struct sftp_conn *from, struct sftp_conn *to, sftp_crossload(struct sftp_conn *from, struct sftp_conn *to,
const char *from_path, const char *to_path, const char *from_path, const char *to_path,
Attrib *a, int preserve_flag) Attrib *a, int preserve_flag)
{ {
@ -2436,13 +2465,17 @@ do_crossload(struct sftp_conn *from, struct sftp_conn *to,
struct requests requests; struct requests requests;
struct request *req; struct request *req;
u_char type; u_char type;
Attrib attr;
debug2_f("crossload src \"%s\" to dst \"%s\"", from_path, to_path); debug2_f("crossload src \"%s\" to dst \"%s\"", from_path, to_path);
TAILQ_INIT(&requests); TAILQ_INIT(&requests);
if (a == NULL && (a = do_stat(from, from_path, 0)) == NULL) if (a == NULL) {
if (sftp_stat(from, from_path, 0, &attr) != 0)
return -1; return -1;
a = &attr;
}
if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
(!S_ISREG(a->perm))) { (!S_ISREG(a->perm))) {
@ -2472,7 +2505,7 @@ do_crossload(struct sftp_conn *from, struct sftp_conn *to,
if (send_open(to, to_path, "dest", if (send_open(to, to_path, "dest",
SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a,
&to_handle, &to_handle_len) != 0) { &to_handle, &to_handle_len) != 0) {
do_close(from, from_handle, from_handle_len); sftp_close(from, from_handle, from_handle_len);
return -1; return -1;
} }
@ -2622,7 +2655,7 @@ do_crossload(struct sftp_conn *from, struct sftp_conn *to,
/* Truncate at 0 length on interrupt or error to avoid holes at dest */ /* Truncate at 0 length on interrupt or error to avoid holes at dest */
if (read_error || write_error || interrupted) { if (read_error || write_error || interrupted) {
debug("truncating \"%s\" at 0", to_path); debug("truncating \"%s\" at 0", to_path);
do_close(to, to_handle, to_handle_len); sftp_close(to, to_handle, to_handle_len);
free(to_handle); free(to_handle);
if (send_open(to, to_path, "dest", if (send_open(to, to_path, "dest",
SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a,
@ -2634,17 +2667,17 @@ do_crossload(struct sftp_conn *from, struct sftp_conn *to,
if (read_error) { if (read_error) {
error("read origin \"%s\": %s", from_path, fx2txt(status)); error("read origin \"%s\": %s", from_path, fx2txt(status));
status = -1; status = -1;
do_close(from, from_handle, from_handle_len); sftp_close(from, from_handle, from_handle_len);
if (to_handle != NULL) if (to_handle != NULL)
do_close(to, to_handle, to_handle_len); sftp_close(to, to_handle, to_handle_len);
} else if (write_error) { } else if (write_error) {
error("write dest \"%s\": %s", to_path, fx2txt(write_error)); error("write dest \"%s\": %s", to_path, fx2txt(write_error));
status = SSH2_FX_FAILURE; status = SSH2_FX_FAILURE;
do_close(from, from_handle, from_handle_len); sftp_close(from, from_handle, from_handle_len);
if (to_handle != NULL) if (to_handle != NULL)
do_close(to, to_handle, to_handle_len); sftp_close(to, to_handle, to_handle_len);
} else { } else {
if (do_close(from, from_handle, from_handle_len) != 0 || if (sftp_close(from, from_handle, from_handle_len) != 0 ||
interrupted) interrupted)
status = -1; status = -1;
else else
@ -2652,8 +2685,8 @@ do_crossload(struct sftp_conn *from, struct sftp_conn *to,
if (to_handle != NULL) { if (to_handle != NULL) {
/* Need to resend utimes after write */ /* Need to resend utimes after write */
if (preserve_flag) if (preserve_flag)
do_fsetstat(to, to_handle, to_handle_len, a); sftp_fsetstat(to, to_handle, to_handle_len, a);
do_close(to, to_handle, to_handle_len); sftp_close(to, to_handle, to_handle_len);
} }
} }
sshbuf_free(msg); sshbuf_free(msg);
@ -2673,7 +2706,7 @@ crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to,
SFTP_DIRENT **dir_entries; SFTP_DIRENT **dir_entries;
char *filename, *new_from_path = NULL, *new_to_path = NULL; char *filename, *new_from_path = NULL, *new_to_path = NULL;
mode_t mode = 0777; mode_t mode = 0777;
Attrib curdir; Attrib *a, curdir, ldirattrib, newdir, lsym;
debug2_f("crossload dir src \"%s\" to dst \"%s\"", from_path, to_path); debug2_f("crossload dir src \"%s\" to dst \"%s\"", from_path, to_path);
@ -2682,11 +2715,13 @@ crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to,
return -1; return -1;
} }
if (dirattrib == NULL && if (dirattrib == NULL) {
(dirattrib = do_stat(from, from_path, 1)) == NULL) { if (sftp_stat(from, from_path, 1, &ldirattrib) != 0) {
error("stat remote \"%s\" failed", from_path); error("stat remote \"%s\" failed", from_path);
return -1; return -1;
} }
dirattrib = &ldirattrib;
}
if (!S_ISDIR(dirattrib->perm)) { if (!S_ISDIR(dirattrib->perm)) {
error("\"%s\" is not a directory", from_path); error("\"%s\" is not a directory", from_path);
return -1; return -1;
@ -2713,17 +2748,17 @@ crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to,
* the path already existed and is a directory. Ensure we can * the path already existed and is a directory. Ensure we can
* write to the directory we create for the duration of the transfer. * write to the directory we create for the duration of the transfer.
*/ */
if (do_mkdir(to, to_path, &curdir, 0) != 0) { if (sftp_mkdir(to, to_path, &curdir, 0) != 0) {
if ((dirattrib = do_stat(to, to_path, 0)) == NULL) if (sftp_stat(to, to_path, 0, &newdir) != 0)
return -1; return -1;
if (!S_ISDIR(dirattrib->perm)) { if (!S_ISDIR(newdir.perm)) {
error("\"%s\" exists but is not a directory", to_path); error("\"%s\" exists but is not a directory", to_path);
return -1; return -1;
} }
} }
curdir.perm = mode; curdir.perm = mode;
if (do_readdir(from, from_path, &dir_entries) == -1) { if (sftp_readdir(from, from_path, &dir_entries) == -1) {
error("origin readdir \"%s\" failed", from_path); error("origin readdir \"%s\" failed", from_path);
return -1; return -1;
} }
@ -2733,28 +2768,36 @@ crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to,
free(new_to_path); free(new_to_path);
filename = dir_entries[i]->filename; filename = dir_entries[i]->filename;
new_from_path = path_append(from_path, filename); new_from_path = sftp_path_append(from_path, filename);
new_to_path = path_append(to_path, filename); new_to_path = sftp_path_append(to_path, filename);
if (S_ISDIR(dir_entries[i]->a.perm)) { a = &dir_entries[i]->a;
if (S_ISLNK(a->perm)) {
if (!follow_link_flag) {
logit("%s: not a regular file", filename);
continue;
}
/* Replace the stat contents with the symlink target */
if (sftp_stat(from, new_from_path, 1, &lsym) != 0) {
logit("remote stat \"%s\" failed",
new_from_path);
ret = -1;
continue;
}
a = &lsym;
}
if (S_ISDIR(a->perm)) {
if (strcmp(filename, ".") == 0 || if (strcmp(filename, ".") == 0 ||
strcmp(filename, "..") == 0) strcmp(filename, "..") == 0)
continue; continue;
if (crossload_dir_internal(from, to, if (crossload_dir_internal(from, to,
new_from_path, new_to_path, new_from_path, new_to_path,
depth + 1, &(dir_entries[i]->a), preserve_flag, depth + 1, a, preserve_flag,
print_flag, follow_link_flag) == -1) print_flag, follow_link_flag) == -1)
ret = -1; ret = -1;
} else if (S_ISREG(dir_entries[i]->a.perm) || } else if (S_ISREG(a->perm)) {
(follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { if (sftp_crossload(from, to, new_from_path,
/* new_to_path, a, preserve_flag) == -1) {
* If this is a symlink then don't send the link's
* Attrib. do_download() will do a FXP_STAT operation
* and get the link target's attributes.
*/
if (do_crossload(from, to, new_from_path, new_to_path,
S_ISLNK(dir_entries[i]->a.perm) ? NULL :
&(dir_entries[i]->a), preserve_flag) == -1) {
error("crossload \"%s\" to \"%s\" failed", error("crossload \"%s\" to \"%s\" failed",
new_from_path, new_to_path); new_from_path, new_to_path);
ret = -1; ret = -1;
@ -2767,22 +2810,22 @@ crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to,
free(new_to_path); free(new_to_path);
free(new_from_path); free(new_from_path);
do_setstat(to, to_path, &curdir); sftp_setstat(to, to_path, &curdir);
free_sftp_dirents(dir_entries); sftp_free_dirents(dir_entries);
return ret; return ret;
} }
int int
crossload_dir(struct sftp_conn *from, struct sftp_conn *to, sftp_crossload_dir(struct sftp_conn *from, struct sftp_conn *to,
const char *from_path, const char *to_path, const char *from_path, const char *to_path,
Attrib *dirattrib, int preserve_flag, int print_flag, int follow_link_flag) Attrib *dirattrib, int preserve_flag, int print_flag, int follow_link_flag)
{ {
char *from_path_canon; char *from_path_canon;
int ret; int ret;
if ((from_path_canon = do_realpath(from, from_path)) == NULL) { if ((from_path_canon = sftp_realpath(from, from_path)) == NULL) {
error("crossload \"%s\": path canonicalization failed", error("crossload \"%s\": path canonicalization failed",
from_path); from_path);
return -1; return -1;
@ -2795,13 +2838,13 @@ crossload_dir(struct sftp_conn *from, struct sftp_conn *to,
} }
int int
can_get_users_groups_by_id(struct sftp_conn *conn) sftp_can_get_users_groups_by_id(struct sftp_conn *conn)
{ {
return (conn->exts & SFTP_EXT_GETUSERSGROUPS_BY_ID) != 0; return (conn->exts & SFTP_EXT_GETUSERSGROUPS_BY_ID) != 0;
} }
int int
do_get_users_groups_by_id(struct sftp_conn *conn, sftp_get_users_groups_by_id(struct sftp_conn *conn,
const u_int *uids, u_int nuids, const u_int *uids, u_int nuids,
const u_int *gids, u_int ngids, const u_int *gids, u_int ngids,
char ***usernamesp, char ***groupnamesp) char ***usernamesp, char ***groupnamesp)
@ -2813,7 +2856,7 @@ do_get_users_groups_by_id(struct sftp_conn *conn,
int r; int r;
*usernamesp = *groupnamesp = NULL; *usernamesp = *groupnamesp = NULL;
if (!can_get_users_groups_by_id(conn)) if (!sftp_can_get_users_groups_by_id(conn))
return SSH_ERR_FEATURE_UNSUPPORTED; return SSH_ERR_FEATURE_UNSUPPORTED;
if ((msg = sshbuf_new()) == NULL || if ((msg = sshbuf_new()) == NULL ||
@ -2909,7 +2952,7 @@ do_get_users_groups_by_id(struct sftp_conn *conn,
} }
char * char *
path_append(const char *p1, const char *p2) sftp_path_append(const char *p1, const char *p2)
{ {
char *ret; char *ret;
size_t len = strlen(p1) + strlen(p2) + 2; size_t len = strlen(p1) + strlen(p2) + 2;
@ -2928,13 +2971,13 @@ path_append(const char *p1, const char *p2)
* freed and a replacement allocated. Caller must free returned string. * freed and a replacement allocated. Caller must free returned string.
*/ */
char * char *
make_absolute(char *p, const char *pwd) sftp_make_absolute(char *p, const char *pwd)
{ {
char *abs_str; char *abs_str;
/* Derelativise */ /* Derelativise */
if (p && !path_absolute(p)) { if (p && !path_absolute(p)) {
abs_str = path_append(pwd, p); abs_str = sftp_path_append(pwd, p);
free(p); free(p);
return(abs_str); return(abs_str);
} else } else
@ -2942,34 +2985,22 @@ make_absolute(char *p, const char *pwd)
} }
int int
remote_is_dir(struct sftp_conn *conn, const char *path) sftp_remote_is_dir(struct sftp_conn *conn, const char *path)
{ {
Attrib *a; Attrib a;
/* XXX: report errors? */ /* XXX: report errors? */
if ((a = do_stat(conn, path, 1)) == NULL) if (sftp_stat(conn, path, 1, &a) != 0)
return(0); return(0);
if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) if (!(a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
return(0); return(0);
return(S_ISDIR(a->perm)); return S_ISDIR(a.perm);
} }
int
local_is_dir(const char *path)
{
struct stat sb;
/* XXX: report errors? */
if (stat(path, &sb) == -1)
return(0);
return(S_ISDIR(sb.st_mode));
}
/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
int int
globpath_is_dir(const char *pathname) sftp_globpath_is_dir(const char *pathname)
{ {
size_t l = strlen(pathname); size_t l = strlen(pathname);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-client.h,v 1.38 2022/09/19 10:43:12 djm Exp $ */ /* $OpenBSD: sftp-client.h,v 1.39 2023/09/08 05:56:13 djm Exp $ */
/* /*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@ -70,107 +70,106 @@ struct sftp_limits {
* Initialise a SSH filexfer connection. Returns NULL on error or * Initialise a SSH filexfer connection. Returns NULL on error or
* a pointer to a initialized sftp_conn struct on success. * a pointer to a initialized sftp_conn struct on success.
*/ */
struct sftp_conn *do_init(int, int, u_int, u_int, u_int64_t); struct sftp_conn *sftp_init(int, int, u_int, u_int, u_int64_t);
u_int sftp_proto_version(struct sftp_conn *); u_int sftp_proto_version(struct sftp_conn *);
/* Query server limits */ /* Query server limits */
int do_limits(struct sftp_conn *, struct sftp_limits *); int sftp_get_limits(struct sftp_conn *, struct sftp_limits *);
/* Close file referred to by 'handle' */ /* Close file referred to by 'handle' */
int do_close(struct sftp_conn *, const u_char *, u_int); int sftp_close(struct sftp_conn *, const u_char *, u_int);
/* Read contents of 'path' to NULL-terminated array 'dir' */ /* Read contents of 'path' to NULL-terminated array 'dir' */
int do_readdir(struct sftp_conn *, const char *, SFTP_DIRENT ***); int sftp_readdir(struct sftp_conn *, const char *, SFTP_DIRENT ***);
/* Frees a NULL-terminated array of SFTP_DIRENTs (eg. from do_readdir) */ /* Frees a NULL-terminated array of SFTP_DIRENTs (eg. from sftp_readdir) */
void free_sftp_dirents(SFTP_DIRENT **); void sftp_free_dirents(SFTP_DIRENT **);
/* Delete file 'path' */ /* Delete file 'path' */
int do_rm(struct sftp_conn *, const char *); int sftp_rm(struct sftp_conn *, const char *);
/* Create directory 'path' */ /* Create directory 'path' */
int do_mkdir(struct sftp_conn *, const char *, Attrib *, int); int sftp_mkdir(struct sftp_conn *, const char *, Attrib *, int);
/* Remove directory 'path' */ /* Remove directory 'path' */
int do_rmdir(struct sftp_conn *, const char *); int sftp_rmdir(struct sftp_conn *, const char *);
/* Get file attributes of 'path' (follows symlinks) */ /* Get file attributes of 'path' (follows symlinks) */
Attrib *do_stat(struct sftp_conn *, const char *, int); int sftp_stat(struct sftp_conn *, const char *, int, Attrib *);
/* Get file attributes of 'path' (does not follow symlinks) */ /* Get file attributes of 'path' (does not follow symlinks) */
Attrib *do_lstat(struct sftp_conn *, const char *, int); int sftp_lstat(struct sftp_conn *, const char *, int, Attrib *);
/* Set file attributes of 'path' */ /* Set file attributes of 'path' */
int do_setstat(struct sftp_conn *, const char *, Attrib *); int sftp_setstat(struct sftp_conn *, const char *, Attrib *);
/* Set file attributes of open file 'handle' */ /* Set file attributes of open file 'handle' */
int do_fsetstat(struct sftp_conn *, const u_char *, u_int, Attrib *); int sftp_fsetstat(struct sftp_conn *, const u_char *, u_int, Attrib *);
/* Set file attributes of 'path', not following symlinks */ /* Set file attributes of 'path', not following symlinks */
int do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a); int sftp_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a);
/* Canonicalise 'path' - caller must free result */ /* Canonicalise 'path' - caller must free result */
char *do_realpath(struct sftp_conn *, const char *); char *sftp_realpath(struct sftp_conn *, const char *);
/* Canonicalisation with tilde expansion (requires server extension) */ /* Canonicalisation with tilde expansion (requires server extension) */
char *do_expand_path(struct sftp_conn *, const char *); char *sftp_expand_path(struct sftp_conn *, const char *);
/* Returns non-zero if server can tilde-expand paths */ /* Returns non-zero if server can tilde-expand paths */
int can_expand_path(struct sftp_conn *); int sftp_can_expand_path(struct sftp_conn *);
/* Get statistics for filesystem hosting file at "path" */ /* Get statistics for filesystem hosting file at "path" */
int do_statvfs(struct sftp_conn *, const char *, struct sftp_statvfs *, int); int sftp_statvfs(struct sftp_conn *, const char *, struct sftp_statvfs *, int);
/* Rename 'oldpath' to 'newpath' */ /* Rename 'oldpath' to 'newpath' */
int do_rename(struct sftp_conn *, const char *, const char *, int); int sftp_rename(struct sftp_conn *, const char *, const char *, int);
/* Copy 'oldpath' to 'newpath' */ /* Copy 'oldpath' to 'newpath' */
int do_copy(struct sftp_conn *, const char *, const char *); int sftp_copy(struct sftp_conn *, const char *, const char *);
/* Link 'oldpath' to 'newpath' */ /* Link 'oldpath' to 'newpath' */
int do_hardlink(struct sftp_conn *, const char *, const char *); int sftp_hardlink(struct sftp_conn *, const char *, const char *);
/* Rename 'oldpath' to 'newpath' */ /* Rename 'oldpath' to 'newpath' */
int do_symlink(struct sftp_conn *, const char *, const char *); int sftp_symlink(struct sftp_conn *, const char *, const char *);
/* Call fsync() on open file 'handle' */ /* Call fsync() on open file 'handle' */
int do_fsync(struct sftp_conn *conn, u_char *, u_int); int sftp_fsync(struct sftp_conn *conn, u_char *, u_int);
/* /*
* Download 'remote_path' to 'local_path'. Preserve permissions and times * Download 'remote_path' to 'local_path'. Preserve permissions and times
* if 'pflag' is set * if 'pflag' is set
*/ */
int do_download(struct sftp_conn *, const char *, const char *, Attrib *, int sftp_download(struct sftp_conn *, const char *, const char *, Attrib *,
int, int, int, int); int, int, int, int);
/* /*
* Recursively download 'remote_directory' to 'local_directory'. Preserve * Recursively download 'remote_directory' to 'local_directory'. Preserve
* times if 'pflag' is set * times if 'pflag' is set
*/ */
int download_dir(struct sftp_conn *, const char *, const char *, Attrib *, int sftp_download_dir(struct sftp_conn *, const char *, const char *, Attrib *,
int, int, int, int, int, int); int, int, int, int, int, int);
/* /*
* Upload 'local_path' to 'remote_path'. Preserve permissions and times * Upload 'local_path' to 'remote_path'. Preserve permissions and times
* if 'pflag' is set * if 'pflag' is set
*/ */
int do_upload(struct sftp_conn *, const char *, const char *, int sftp_upload(struct sftp_conn *, const char *, const char *,
int, int, int, int); int, int, int, int);
/* /*
* Recursively upload 'local_directory' to 'remote_directory'. Preserve * Recursively upload 'local_directory' to 'remote_directory'. Preserve
* times if 'pflag' is set * times if 'pflag' is set
*/ */
int upload_dir(struct sftp_conn *, const char *, const char *, int sftp_upload_dir(struct sftp_conn *, const char *, const char *,
int, int, int, int, int, int); int, int, int, int, int, int);
/* /*
* Download a 'from_path' from the 'from' connection and upload it to * Download a 'from_path' from the 'from' connection and upload it to
* to 'to' connection at 'to_path'. * to 'to' connection at 'to_path'.
*/ */
int int sftp_crossload(struct sftp_conn *from, struct sftp_conn *to,
do_crossload(struct sftp_conn *from, struct sftp_conn *to,
const char *from_path, const char *to_path, const char *from_path, const char *to_path,
Attrib *a, int preserve_flag); Attrib *a, int preserve_flag);
@ -178,7 +177,7 @@ do_crossload(struct sftp_conn *from, struct sftp_conn *to,
* Recursively download a directory from 'from_path' from the 'from' * Recursively download a directory from 'from_path' from the 'from'
* connection and upload it to 'to' connection at 'to_path'. * connection and upload it to 'to' connection at 'to_path'.
*/ */
int crossload_dir(struct sftp_conn *from, struct sftp_conn *to, int sftp_crossload_dir(struct sftp_conn *from, struct sftp_conn *to,
const char *from_path, const char *to_path, const char *from_path, const char *to_path,
Attrib *dirattrib, int preserve_flag, int print_flag, Attrib *dirattrib, int preserve_flag, int print_flag,
int follow_link_flag); int follow_link_flag);
@ -186,26 +185,23 @@ int crossload_dir(struct sftp_conn *from, struct sftp_conn *to,
/* /*
* User/group ID to name translation. * User/group ID to name translation.
*/ */
int can_get_users_groups_by_id(struct sftp_conn *conn); int sftp_can_get_users_groups_by_id(struct sftp_conn *conn);
int do_get_users_groups_by_id(struct sftp_conn *conn, int sftp_get_users_groups_by_id(struct sftp_conn *conn,
const u_int *uids, u_int nuids, const u_int *uids, u_int nuids,
const u_int *gids, u_int ngids, const u_int *gids, u_int ngids,
char ***usernamesp, char ***groupnamesp); char ***usernamesp, char ***groupnamesp);
/* Concatenate paths, taking care of slashes. Caller must free result. */ /* Concatenate paths, taking care of slashes. Caller must free result. */
char *path_append(const char *, const char *); char *sftp_path_append(const char *, const char *);
/* Make absolute path if relative path and CWD is given. Does not modify /* Make absolute path if relative path and CWD is given. Does not modify
* original if the path is already absolute. */ * original if the path is already absolute. */
char *make_absolute(char *, const char *); char *sftp_make_absolute(char *, const char *);
/* Check if remote path is directory */ /* Check if remote path is directory */
int remote_is_dir(struct sftp_conn *conn, const char *path); int sftp_remote_is_dir(struct sftp_conn *conn, const char *path);
/* Check if local path is directory */
int local_is_dir(const char *path);
/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
int globpath_is_dir(const char *pathname); int sftp_globpath_is_dir(const char *pathname);
#endif #endif

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-glob.c,v 1.31 2022/10/24 21:51:55 djm Exp $ */ /* $OpenBSD: sftp-glob.c,v 1.33 2023/09/10 23:12:32 djm Exp $ */
/* /*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
* *
@ -32,7 +32,7 @@
#include "sftp-common.h" #include "sftp-common.h"
#include "sftp-client.h" #include "sftp-client.h"
int remote_glob(struct sftp_conn *, const char *, int, int sftp_glob(struct sftp_conn *, const char *, int,
int (*)(const char *, int), glob_t *); int (*)(const char *, int), glob_t *);
struct SFTP_OPENDIR { struct SFTP_OPENDIR {
@ -51,7 +51,7 @@ fudge_opendir(const char *path)
r = xcalloc(1, sizeof(*r)); r = xcalloc(1, sizeof(*r));
if (do_readdir(cur.conn, path, &r->dir)) { if (sftp_readdir(cur.conn, path, &r->dir)) {
free(r); free(r);
return(NULL); return(NULL);
} }
@ -103,38 +103,38 @@ fudge_readdir(struct SFTP_OPENDIR *od)
static void static void
fudge_closedir(struct SFTP_OPENDIR *od) fudge_closedir(struct SFTP_OPENDIR *od)
{ {
free_sftp_dirents(od->dir); sftp_free_dirents(od->dir);
free(od); free(od);
} }
static int static int
fudge_lstat(const char *path, struct stat *st) fudge_lstat(const char *path, struct stat *st)
{ {
Attrib *a; Attrib a;
if (!(a = do_lstat(cur.conn, path, 1))) if (sftp_lstat(cur.conn, path, 1, &a) != 0)
return(-1); return -1;
attrib_to_stat(a, st); attrib_to_stat(&a, st);
return(0); return 0;
} }
static int static int
fudge_stat(const char *path, struct stat *st) fudge_stat(const char *path, struct stat *st)
{ {
Attrib *a; Attrib a;
if (!(a = do_stat(cur.conn, path, 1))) if (sftp_stat(cur.conn, path, 1, &a) != 0)
return(-1); return -1;
attrib_to_stat(a, st); attrib_to_stat(&a, st);
return(0); return(0);
} }
int int
remote_glob(struct sftp_conn *conn, const char *pattern, int flags, sftp_glob(struct sftp_conn *conn, const char *pattern, int flags,
int (*errfunc)(const char *, int), glob_t *pglob) int (*errfunc)(const char *, int), glob_t *pglob)
{ {
int r; int r;

View File

@ -106,9 +106,9 @@ lookup_and_record(struct sftp_conn *conn,
u_int i; u_int i;
char **usernames = NULL, **groupnames = NULL; char **usernames = NULL, **groupnames = NULL;
if ((r = do_get_users_groups_by_id(conn, uids, nuids, gids, ngids, if ((r = sftp_get_users_groups_by_id(conn, uids, nuids, gids, ngids,
&usernames, &groupnames)) != 0) { &usernames, &groupnames)) != 0) {
debug_fr(r, "do_get_users_groups_by_id"); debug_fr(r, "sftp_get_users_groups_by_id");
return; return;
} }
for (i = 0; i < nuids; i++) { for (i = 0; i < nuids; i++) {
@ -176,7 +176,7 @@ get_remote_user_groups_from_glob(struct sftp_conn *conn, glob_t *g)
{ {
u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0; u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0;
if (!can_get_users_groups_by_id(conn)) if (!sftp_can_get_users_groups_by_id(conn))
return; return;
collect_ids_from_glob(g, 1, &uids, &nuids); collect_ids_from_glob(g, 1, &uids, &nuids);
@ -215,7 +215,7 @@ get_remote_user_groups_from_dirents(struct sftp_conn *conn, SFTP_DIRENT **d)
{ {
u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0; u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0;
if (!can_get_users_groups_by_id(conn)) if (!sftp_can_get_users_groups_by_id(conn))
return; return;
collect_ids_from_dirents(d, 1, &uids, &nuids); collect_ids_from_dirents(d, 1, &uids, &nuids);

138
sftp.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp.c,v 1.234 2023/04/12 08:53:54 jsg Exp $ */ /* $OpenBSD: sftp.c,v 1.236 2023/09/10 23:12:32 djm Exp $ */
/* /*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
* *
@ -110,7 +110,7 @@ struct complete_ctx {
char **remote_pathp; char **remote_pathp;
}; };
int remote_glob(struct sftp_conn *, const char *, int, int sftp_glob(struct sftp_conn *, const char *, int,
int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
extern char *__progname; extern char *__progname;
@ -628,11 +628,21 @@ make_absolute_pwd_glob(char *p, const char *pwd)
escpwd = escape_glob(pwd); escpwd = escape_glob(pwd);
if (p == NULL) if (p == NULL)
return escpwd; return escpwd;
ret = make_absolute(p, escpwd); ret = sftp_make_absolute(p, escpwd);
free(escpwd); free(escpwd);
return ret; return ret;
} }
static int
local_is_dir(const char *path)
{
struct stat sb;
if (stat(path, &sb) == -1)
return 0;
return S_ISDIR(sb.st_mode);
}
static int static int
process_get(struct sftp_conn *conn, const char *src, const char *dst, process_get(struct sftp_conn *conn, const char *src, const char *dst,
const char *pwd, int pflag, int rflag, int resume, int fflag) const char *pwd, int pflag, int rflag, int resume, int fflag)
@ -645,7 +655,7 @@ process_get(struct sftp_conn *conn, const char *src, const char *dst,
memset(&g, 0, sizeof(g)); memset(&g, 0, sizeof(g));
debug3("Looking up %s", abs_src); debug3("Looking up %s", abs_src);
if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) { if ((r = sftp_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
if (r == GLOB_NOSPACE) { if (r == GLOB_NOSPACE) {
error("Too many matches for \"%s\".", abs_src); error("Too many matches for \"%s\".", abs_src);
} else { } else {
@ -677,12 +687,12 @@ process_get(struct sftp_conn *conn, const char *src, const char *dst,
if (g.gl_matchc == 1 && dst) { if (g.gl_matchc == 1 && dst) {
if (local_is_dir(dst)) { if (local_is_dir(dst)) {
abs_dst = path_append(dst, filename); abs_dst = sftp_path_append(dst, filename);
} else { } else {
abs_dst = xstrdup(dst); abs_dst = xstrdup(dst);
} }
} else if (dst) { } else if (dst) {
abs_dst = path_append(dst, filename); abs_dst = sftp_path_append(dst, filename);
} else { } else {
abs_dst = xstrdup(filename); abs_dst = xstrdup(filename);
} }
@ -696,13 +706,14 @@ process_get(struct sftp_conn *conn, const char *src, const char *dst,
mprintf("Fetching %s to %s\n", mprintf("Fetching %s to %s\n",
g.gl_pathv[i], abs_dst); g.gl_pathv[i], abs_dst);
/* XXX follow link flag */ /* XXX follow link flag */
if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { if (sftp_globpath_is_dir(g.gl_pathv[i]) &&
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, (rflag || global_rflag)) {
pflag || global_pflag, 1, resume, if (sftp_download_dir(conn, g.gl_pathv[i], abs_dst,
NULL, pflag || global_pflag, 1, resume,
fflag || global_fflag, 0, 0) == -1) fflag || global_fflag, 0, 0) == -1)
err = -1; err = -1;
} else { } else {
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, if (sftp_download(conn, g.gl_pathv[i], abs_dst, NULL,
pflag || global_pflag, resume, pflag || global_pflag, resume,
fflag || global_fflag, 0) == -1) fflag || global_fflag, 0) == -1)
err = -1; err = -1;
@ -731,7 +742,7 @@ process_put(struct sftp_conn *conn, const char *src, const char *dst,
if (dst) { if (dst) {
tmp_dst = xstrdup(dst); tmp_dst = xstrdup(dst);
tmp_dst = make_absolute(tmp_dst, pwd); tmp_dst = sftp_make_absolute(tmp_dst, pwd);
} }
memset(&g, 0, sizeof(g)); memset(&g, 0, sizeof(g));
@ -744,7 +755,7 @@ process_put(struct sftp_conn *conn, const char *src, const char *dst,
/* If we aren't fetching to pwd then stash this status for later */ /* If we aren't fetching to pwd then stash this status for later */
if (tmp_dst != NULL) if (tmp_dst != NULL)
dst_is_dir = remote_is_dir(conn, tmp_dst); dst_is_dir = sftp_remote_is_dir(conn, tmp_dst);
/* If multiple matches, dst may be directory or unspecified */ /* If multiple matches, dst may be directory or unspecified */
if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
@ -774,13 +785,13 @@ process_put(struct sftp_conn *conn, const char *src, const char *dst,
if (g.gl_matchc == 1 && tmp_dst) { if (g.gl_matchc == 1 && tmp_dst) {
/* If directory specified, append filename */ /* If directory specified, append filename */
if (dst_is_dir) if (dst_is_dir)
abs_dst = path_append(tmp_dst, filename); abs_dst = sftp_path_append(tmp_dst, filename);
else else
abs_dst = xstrdup(tmp_dst); abs_dst = xstrdup(tmp_dst);
} else if (tmp_dst) { } else if (tmp_dst) {
abs_dst = path_append(tmp_dst, filename); abs_dst = sftp_path_append(tmp_dst, filename);
} else { } else {
abs_dst = make_absolute(xstrdup(filename), pwd); abs_dst = sftp_make_absolute(xstrdup(filename), pwd);
} }
free(tmp); free(tmp);
@ -792,13 +803,14 @@ process_put(struct sftp_conn *conn, const char *src, const char *dst,
mprintf("Uploading %s to %s\n", mprintf("Uploading %s to %s\n",
g.gl_pathv[i], abs_dst); g.gl_pathv[i], abs_dst);
/* XXX follow_link_flag */ /* XXX follow_link_flag */
if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { if (sftp_globpath_is_dir(g.gl_pathv[i]) &&
if (upload_dir(conn, g.gl_pathv[i], abs_dst, (rflag || global_rflag)) {
if (sftp_upload_dir(conn, g.gl_pathv[i], abs_dst,
pflag || global_pflag, 1, resume, pflag || global_pflag, 1, resume,
fflag || global_fflag, 0, 0) == -1) fflag || global_fflag, 0, 0) == -1)
err = -1; err = -1;
} else { } else {
if (do_upload(conn, g.gl_pathv[i], abs_dst, if (sftp_upload(conn, g.gl_pathv[i], abs_dst,
pflag || global_pflag, resume, pflag || global_pflag, resume,
fflag || global_fflag, 0) == -1) fflag || global_fflag, 0) == -1)
err = -1; err = -1;
@ -839,7 +851,7 @@ do_ls_dir(struct sftp_conn *conn, const char *path,
u_int c = 1, colspace = 0, columns = 1; u_int c = 1, colspace = 0, columns = 1;
SFTP_DIRENT **d; SFTP_DIRENT **d;
if ((n = do_readdir(conn, path, &d)) != 0) if ((n = sftp_readdir(conn, path, &d)) != 0)
return (n); return (n);
if (!(lflag & LS_SHORT_VIEW)) { if (!(lflag & LS_SHORT_VIEW)) {
@ -881,13 +893,13 @@ do_ls_dir(struct sftp_conn *conn, const char *path,
if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
continue; continue;
tmp = path_append(path, d[n]->filename); tmp = sftp_path_append(path, d[n]->filename);
fname = path_strip(tmp, strip_path); fname = path_strip(tmp, strip_path);
free(tmp); free(tmp);
if (lflag & LS_LONG_VIEW) { if (lflag & LS_LONG_VIEW) {
if ((lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) != 0 || if ((lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) != 0 ||
can_get_users_groups_by_id(conn)) { sftp_can_get_users_groups_by_id(conn)) {
char *lname; char *lname;
struct stat sb; struct stat sb;
@ -916,7 +928,7 @@ do_ls_dir(struct sftp_conn *conn, const char *path,
if (!(lflag & LS_LONG_VIEW) && (c != 1)) if (!(lflag & LS_LONG_VIEW) && (c != 1))
printf("\n"); printf("\n");
free_sftp_dirents(d); sftp_free_dirents(d);
return (0); return (0);
} }
@ -965,7 +977,7 @@ do_globbed_ls(struct sftp_conn *conn, const char *path,
memset(&g, 0, sizeof(g)); memset(&g, 0, sizeof(g));
if ((r = remote_glob(conn, path, if ((r = sftp_glob(conn, path,
GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
NULL, &g)) != 0 || NULL, &g)) != 0 ||
(g.gl_pathc && !g.gl_matchc)) { (g.gl_pathc && !g.gl_matchc)) {
@ -1070,7 +1082,7 @@ do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE]; char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
char s_icapacity[16], s_dcapacity[16]; char s_icapacity[16], s_dcapacity[16];
if (do_statvfs(conn, path, &st, 1) == -1) if (sftp_statvfs(conn, path, &st, 1) == -1)
return -1; return -1;
if (st.f_files == 0) if (st.f_files == 0)
strlcpy(s_icapacity, "ERR", sizeof(s_icapacity)); strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
@ -1544,7 +1556,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
int lflag = 0, pflag = 0, rflag = 0, sflag = 0; int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
int cmdnum, i; int cmdnum, i;
unsigned long n_arg = 0; unsigned long n_arg = 0;
Attrib a, *aa; Attrib a, aa;
char path_buf[PATH_MAX]; char path_buf[PATH_MAX];
int err = 0; int err = 0;
glob_t g; glob_t g;
@ -1585,66 +1597,67 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
rflag, aflag, fflag); rflag, aflag, fflag);
break; break;
case I_COPY: case I_COPY:
path1 = make_absolute(path1, *pwd); path1 = sftp_make_absolute(path1, *pwd);
path2 = make_absolute(path2, *pwd); path2 = sftp_make_absolute(path2, *pwd);
err = do_copy(conn, path1, path2); err = sftp_copy(conn, path1, path2);
break; break;
case I_RENAME: case I_RENAME:
path1 = make_absolute(path1, *pwd); path1 = sftp_make_absolute(path1, *pwd);
path2 = make_absolute(path2, *pwd); path2 = sftp_make_absolute(path2, *pwd);
err = do_rename(conn, path1, path2, lflag); err = sftp_rename(conn, path1, path2, lflag);
break; break;
case I_SYMLINK: case I_SYMLINK:
sflag = 1; sflag = 1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case I_LINK: case I_LINK:
if (!sflag) if (!sflag)
path1 = make_absolute(path1, *pwd); path1 = sftp_make_absolute(path1, *pwd);
path2 = make_absolute(path2, *pwd); path2 = sftp_make_absolute(path2, *pwd);
err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2); err = (sflag ? sftp_symlink : sftp_hardlink)(conn,
path1, path2);
break; break;
case I_RM: case I_RM:
path1 = make_absolute_pwd_glob(path1, *pwd); path1 = make_absolute_pwd_glob(path1, *pwd);
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) { for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
if (!quiet) if (!quiet)
mprintf("Removing %s\n", g.gl_pathv[i]); mprintf("Removing %s\n", g.gl_pathv[i]);
err = do_rm(conn, g.gl_pathv[i]); err = sftp_rm(conn, g.gl_pathv[i]);
if (err != 0 && err_abort) if (err != 0 && err_abort)
break; break;
} }
break; break;
case I_MKDIR: case I_MKDIR:
path1 = make_absolute(path1, *pwd); path1 = sftp_make_absolute(path1, *pwd);
attrib_clear(&a); attrib_clear(&a);
a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
a.perm = 0777; a.perm = 0777;
err = do_mkdir(conn, path1, &a, 1); err = sftp_mkdir(conn, path1, &a, 1);
break; break;
case I_RMDIR: case I_RMDIR:
path1 = make_absolute(path1, *pwd); path1 = sftp_make_absolute(path1, *pwd);
err = do_rmdir(conn, path1); err = sftp_rmdir(conn, path1);
break; break;
case I_CHDIR: case I_CHDIR:
if (path1 == NULL || *path1 == '\0') if (path1 == NULL || *path1 == '\0')
path1 = xstrdup(startdir); path1 = xstrdup(startdir);
path1 = make_absolute(path1, *pwd); path1 = sftp_make_absolute(path1, *pwd);
if ((tmp = do_realpath(conn, path1)) == NULL) { if ((tmp = sftp_realpath(conn, path1)) == NULL) {
err = 1; err = 1;
break; break;
} }
if ((aa = do_stat(conn, tmp, 0)) == NULL) { if (sftp_stat(conn, tmp, 0, &aa) != 0) {
free(tmp); free(tmp);
err = 1; err = 1;
break; break;
} }
if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { if (!(aa.flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
error("Can't change directory: Can't check target"); error("Can't change directory: Can't check target");
free(tmp); free(tmp);
err = 1; err = 1;
break; break;
} }
if (!S_ISDIR(aa->perm)) { if (!S_ISDIR(aa.perm)) {
error("Can't change directory: \"%s\" is not " error("Can't change directory: \"%s\" is not "
"a directory", tmp); "a directory", tmp);
free(tmp); free(tmp);
@ -1672,7 +1685,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
/* Default to current directory if no path specified */ /* Default to current directory if no path specified */
if (path1 == NULL) if (path1 == NULL)
path1 = xstrdup(*pwd); path1 = xstrdup(*pwd);
path1 = make_absolute(path1, *pwd); path1 = sftp_make_absolute(path1, *pwd);
err = do_df(conn, path1, hflag, iflag); err = do_df(conn, path1, hflag, iflag);
break; break;
case I_LCHDIR: case I_LCHDIR:
@ -1709,12 +1722,12 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
attrib_clear(&a); attrib_clear(&a);
a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
a.perm = n_arg; a.perm = n_arg;
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) { for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
if (!quiet) if (!quiet)
mprintf("Changing mode on %s\n", mprintf("Changing mode on %s\n",
g.gl_pathv[i]); g.gl_pathv[i]);
err = (hflag ? do_lsetstat : do_setstat)(conn, err = (hflag ? sftp_lsetstat : sftp_setstat)(conn,
g.gl_pathv[i], &a); g.gl_pathv[i], &a);
if (err != 0 && err_abort) if (err != 0 && err_abort)
break; break;
@ -1723,17 +1736,17 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
case I_CHOWN: case I_CHOWN:
case I_CHGRP: case I_CHGRP:
path1 = make_absolute_pwd_glob(path1, *pwd); path1 = make_absolute_pwd_glob(path1, *pwd);
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) { for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
if (!(aa = (hflag ? do_lstat : do_stat)(conn, if ((hflag ? sftp_lstat : sftp_stat)(conn,
g.gl_pathv[i], 0))) { g.gl_pathv[i], 0, &aa) != 0) {
if (err_abort) { if (err_abort) {
err = -1; err = -1;
break; break;
} else } else
continue; continue;
} }
if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { if (!(aa.flags & SSH2_FILEXFER_ATTR_UIDGID)) {
error("Can't get current ownership of " error("Can't get current ownership of "
"remote file \"%s\"", g.gl_pathv[i]); "remote file \"%s\"", g.gl_pathv[i]);
if (err_abort) { if (err_abort) {
@ -1742,20 +1755,20 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
} else } else
continue; continue;
} }
aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; aa.flags &= SSH2_FILEXFER_ATTR_UIDGID;
if (cmdnum == I_CHOWN) { if (cmdnum == I_CHOWN) {
if (!quiet) if (!quiet)
mprintf("Changing owner on %s\n", mprintf("Changing owner on %s\n",
g.gl_pathv[i]); g.gl_pathv[i]);
aa->uid = n_arg; aa.uid = n_arg;
} else { } else {
if (!quiet) if (!quiet)
mprintf("Changing group on %s\n", mprintf("Changing group on %s\n",
g.gl_pathv[i]); g.gl_pathv[i]);
aa->gid = n_arg; aa.gid = n_arg;
} }
err = (hflag ? do_lsetstat : do_setstat)(conn, err = (hflag ? sftp_lsetstat : sftp_setstat)(conn,
g.gl_pathv[i], aa); g.gl_pathv[i], &aa);
if (err != 0 && err_abort) if (err != 0 && err_abort)
break; break;
} }
@ -2004,7 +2017,7 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
memset(&g, 0, sizeof(g)); memset(&g, 0, sizeof(g));
if (remote != LOCAL) { if (remote != LOCAL) {
tmp = make_absolute_pwd_glob(tmp, remote_path); tmp = make_absolute_pwd_glob(tmp, remote_path);
remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); sftp_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
} else } else
(void)glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); (void)glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
@ -2234,16 +2247,15 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
} }
#endif /* USE_LIBEDIT */ #endif /* USE_LIBEDIT */
remote_path = do_realpath(conn, "."); if ((remote_path = sftp_realpath(conn, ".")) == NULL)
if (remote_path == NULL)
fatal("Need cwd"); fatal("Need cwd");
startdir = xstrdup(remote_path); startdir = xstrdup(remote_path);
if (file1 != NULL) { if (file1 != NULL) {
dir = xstrdup(file1); dir = xstrdup(file1);
dir = make_absolute(dir, remote_path); dir = sftp_make_absolute(dir, remote_path);
if (remote_is_dir(conn, dir) && file2 == NULL) { if (sftp_remote_is_dir(conn, dir) && file2 == NULL) {
if (!quiet) if (!quiet)
mprintf("Changing to: %s\n", dir); mprintf("Changing to: %s\n", dir);
snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
@ -2652,7 +2664,7 @@ main(int argc, char **argv)
} }
freeargs(&args); freeargs(&args);
conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps); conn = sftp_init(in, out, copy_buffer_len, num_requests, limit_kbps);
if (conn == NULL) if (conn == NULL)
fatal("Couldn't initialise connection to server"); fatal("Couldn't initialise connection to server");

View File

@ -48,7 +48,7 @@ DESCRIPTION
only local clients may perform this operation. Note that only local clients may perform this operation. Note that
signalling that an ssh-agent client is remote is performed by signalling that an ssh-agent client is remote is performed by
ssh(1), and use of other tools to forward access to the agent ssh(1), and use of other tools to forward access to the agent
socket, may circumvent this restriction. socket may circumvent this restriction.
The no-restrict-websafe option instructs ssh-agent to permit The no-restrict-websafe option instructs ssh-agent to permit
signatures using FIDO keys that might be web authentication signatures using FIDO keys that might be web authentication
@ -66,7 +66,7 @@ DESCRIPTION
used with the -S or -s options to ssh-add(1). Libraries that do used with the -S or -s options to ssh-add(1). Libraries that do
not match the pattern list will be refused. See PATTERNS in not match the pattern list will be refused. See PATTERNS in
ssh_config(5) for a description of pattern-list syntax. The ssh_config(5) for a description of pattern-list syntax. The
default list is M-bM-^@M-^\/usr/lib/*,/usr/local/lib/*M-bM-^@M-^]. default list is M-bM-^@M-^\usr/lib*/*,/usr/local/lib*/*M-bM-^@M-^].
-s Generate Bourne shell commands on stdout. This is the default if -s Generate Bourne shell commands on stdout. This is the default if
SHELL does not look like it's a csh style of shell. SHELL does not look like it's a csh style of shell.
@ -137,4 +137,4 @@ AUTHORS
created OpenSSH. Markus Friedl contributed the support for SSH protocol created OpenSSH. Markus Friedl contributed the support for SSH protocol
versions 1.5 and 2.0. versions 1.5 and 2.0.
OpenBSD 7.3 July 23, 2023 OpenBSD 7.3 OpenBSD 7.3 August 10, 2023 OpenBSD 7.3

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-agent.1,v 1.78 2023/07/23 20:04:45 naddy Exp $ .\" $OpenBSD: ssh-agent.1,v 1.79 2023/08/10 14:37:32 naddy Exp $
.\" .\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi> .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -34,7 +34,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.Dd $Mdocdate: July 23 2023 $ .Dd $Mdocdate: August 10 2023 $
.Dt SSH-AGENT 1 .Dt SSH-AGENT 1
.Os .Os
.Sh NAME .Sh NAME
@ -122,7 +122,7 @@ Note that signalling that an
.Nm .Nm
client is remote is performed by client is remote is performed by
.Xr ssh 1 , .Xr ssh 1 ,
and use of other tools to forward access to the agent socket, may circumvent and use of other tools to forward access to the agent socket may circumvent
this restriction. this restriction.
.Pp .Pp
The The
@ -156,7 +156,7 @@ See PATTERNS in
.Xr ssh_config 5 .Xr ssh_config 5
for a description of pattern-list syntax. for a description of pattern-list syntax.
The default list is The default list is
.Dq /usr/lib/*,/usr/local/lib/* . .Dq usr/lib*/*,/usr/local/lib*/* .
.It Fl s .It Fl s
Generate Bourne shell commands on Generate Bourne shell commands on
.Dv stdout . .Dv stdout .

View File

@ -46,7 +46,7 @@ DESCRIPTION
ssh(1). ssh-keygen can create keys for use by SSH protocol version 2. ssh(1). ssh-keygen can create keys for use by SSH protocol version 2.
The type of key to be generated is specified with the -t option. If The type of key to be generated is specified with the -t option. If
invoked without any arguments, ssh-keygen will generate an RSA key. invoked without any arguments, ssh-keygen will generate an Ed25519 key.
ssh-keygen is also used to generate groups for use in Diffie-Hellman ssh-keygen is also used to generate groups for use in Diffie-Hellman
group exchange (DH-GEX). See the MODULI GENERATION section for details. group exchange (DH-GEX). See the MODULI GENERATION section for details.
@ -907,4 +907,4 @@ AUTHORS
created OpenSSH. Markus Friedl contributed the support for SSH protocol created OpenSSH. Markus Friedl contributed the support for SSH protocol
versions 1.5 and 2.0. versions 1.5 and 2.0.
OpenBSD 7.3 July 23, 2023 OpenBSD 7.3 OpenBSD 7.3 September 4, 2023 OpenBSD 7.3

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-keygen.1,v 1.229 2023/07/23 20:04:45 naddy Exp $ .\" $OpenBSD: ssh-keygen.1,v 1.230 2023/09/04 10:29:58 job Exp $
.\" .\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi> .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -35,7 +35,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.Dd $Mdocdate: July 23 2023 $ .Dd $Mdocdate: September 4 2023 $
.Dt SSH-KEYGEN 1 .Dt SSH-KEYGEN 1
.Os .Os
.Sh NAME .Sh NAME
@ -185,7 +185,7 @@ The type of key to be generated is specified with the
option. option.
If invoked without any arguments, If invoked without any arguments,
.Nm .Nm
will generate an RSA key. will generate an Ed25519 key.
.Pp .Pp
.Nm .Nm
is also used to generate groups for use in Diffie-Hellman group is also used to generate groups for use in Diffie-Hellman group

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.470 2023/07/17 04:01:10 djm Exp $ */ /* $OpenBSD: ssh-keygen.c,v 1.471 2023/09/04 10:29:58 job Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -67,11 +67,7 @@
#include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */ #include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */
#include "cipher.h" #include "cipher.h"
#ifdef WITH_OPENSSL
# define DEFAULT_KEY_TYPE_NAME "rsa"
#else
#define DEFAULT_KEY_TYPE_NAME "ed25519" #define DEFAULT_KEY_TYPE_NAME "ed25519"
#endif
/* /*
* Default number of bits in the RSA, DSA and ECDSA keys. These value can be * Default number of bits in the RSA, DSA and ECDSA keys. These value can be
@ -263,7 +259,7 @@ ask_filename(struct passwd *pw, const char *prompt)
char *name = NULL; char *name = NULL;
if (key_type_name == NULL) if (key_type_name == NULL)
name = _PATH_SSH_CLIENT_ID_RSA; name = _PATH_SSH_CLIENT_ID_ED25519;
else { else {
switch (sshkey_type_from_name(key_type_name)) { switch (sshkey_type_from_name(key_type_name)) {
case KEY_DSA_CERT: case KEY_DSA_CERT:

11
ssh.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh.c,v 1.593 2023/07/26 23:06:00 djm Exp $ */ /* $OpenBSD: ssh.c,v 1.594 2023/09/03 23:59:32 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -2140,7 +2140,7 @@ ssh_session2_open(struct ssh *ssh)
static int static int
ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo) ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo)
{ {
int r, id = -1; int r, interactive, id = -1;
char *cp, *tun_fwd_ifname = NULL; char *cp, *tun_fwd_ifname = NULL;
/* XXX should be pre-session */ /* XXX should be pre-session */
@ -2197,8 +2197,11 @@ ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo)
if (options.session_type != SESSION_TYPE_NONE) if (options.session_type != SESSION_TYPE_NONE)
id = ssh_session2_open(ssh); id = ssh_session2_open(ssh);
else { else {
ssh_packet_set_interactive(ssh, interactive = options.control_master == SSHCTL_MASTER_NO;
options.control_master == SSHCTL_MASTER_NO, /* ControlPersist may have clobbered ControlMaster, so check */
if (need_controlpersist_detach)
interactive = otty_flag != 0;
ssh_packet_set_interactive(ssh, interactive,
options.ip_qos_interactive, options.ip_qos_bulk); options.ip_qos_interactive, options.ip_qos_bulk);
} }

7
ssh2.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh2.h,v 1.19 2020/11/19 23:05:05 dtucker Exp $ */ /* $OpenBSD: ssh2.h,v 1.21 2023/08/28 03:28:43 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
@ -85,6 +85,7 @@
#define SSH2_MSG_SERVICE_REQUEST 5 #define SSH2_MSG_SERVICE_REQUEST 5
#define SSH2_MSG_SERVICE_ACCEPT 6 #define SSH2_MSG_SERVICE_ACCEPT 6
#define SSH2_MSG_EXT_INFO 7 #define SSH2_MSG_EXT_INFO 7
#define SSH2_MSG_NEWCOMPRESS 8
/* transport layer: alg negotiation */ /* transport layer: alg negotiation */
@ -107,6 +108,10 @@
#define SSH2_MSG_KEX_ECDH_INIT 30 #define SSH2_MSG_KEX_ECDH_INIT 30
#define SSH2_MSG_KEX_ECDH_REPLY 31 #define SSH2_MSG_KEX_ECDH_REPLY 31
/* transport layer: OpenSSH extensions */
#define SSH2_MSG_PING 192
#define SSH2_MSG_PONG 193
/* user authentication: generic */ /* user authentication: generic */
#define SSH2_MSG_USERAUTH_REQUEST 50 #define SSH2_MSG_USERAUTH_REQUEST 50

View File

@ -805,6 +805,18 @@ DESCRIPTION
Specifies the number of password prompts before giving up. The Specifies the number of password prompts before giving up. The
argument to this keyword must be an integer. The default is 3. argument to this keyword must be an integer. The default is 3.
ObscureKeystrokeTiming
Specifies whether ssh(1) should try to obscure inter-keystroke
timings from passive observers of network traffic. If enabled,
then for interactive sessions, ssh(1) will send keystrokes at
fixed intervals of a few tens of milliseconds and will send fake
keystroke packets for some time after typing ceases. The
argument to this keyword must be yes, no or an interval specifier
of the form interval:milliseconds (e.g. interval:80 for 80
milliseconds). The default is to obscure keystrokes using a 20ms
packet interval. Note that smaller intervals will result in
higher fake keystroke packet rates.
PasswordAuthentication PasswordAuthentication
Specifies whether to use password authentication. The argument Specifies whether to use password authentication. The argument
to this keyword must be yes (the default) or no. to this keyword must be yes (the default) or no.
@ -1306,6 +1318,13 @@ TOKENS
ProxyCommand and ProxyJump accept the tokens %%, %h, %n, %p, and %r. ProxyCommand and ProxyJump accept the tokens %%, %h, %n, %p, and %r.
Note that some of these directives build commands for execution via the
shell. Because ssh(1) performs no filtering or escaping of characters
that have special meaning in shell commands (e.g. quotes), it is the
user's reposibility to ensure that the arguments passed to ssh(1) do not
contain such characters and that tokens are appropriately quoted when
used.
ENVIRONMENT VARIABLES ENVIRONMENT VARIABLES
Arguments to some keywords can be expanded at runtime from environment Arguments to some keywords can be expanded at runtime from environment
variables on the client by enclosing them in ${}, for example variables on the client by enclosing them in ${}, for example
@ -1341,4 +1360,4 @@ AUTHORS
created OpenSSH. Markus Friedl contributed the support for SSH protocol created OpenSSH. Markus Friedl contributed the support for SSH protocol
versions 1.5 and 2.0. versions 1.5 and 2.0.
OpenBSD 7.3 July 17, 2023 OpenBSD 7.3 OpenBSD 7.3 October 4, 2023 OpenBSD 7.3

View File

@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.\" $OpenBSD: ssh_config.5,v 1.383 2023/07/17 05:36:14 jsg Exp $ .\" $OpenBSD: ssh_config.5,v 1.387 2023/10/04 04:03:50 djm Exp $
.Dd $Mdocdate: July 17 2023 $ .Dd $Mdocdate: October 4 2023 $
.Dt SSH_CONFIG 5 .Dt SSH_CONFIG 5
.Os .Os
.Sh NAME .Sh NAME
@ -1358,6 +1358,25 @@ or
Specifies the number of password prompts before giving up. Specifies the number of password prompts before giving up.
The argument to this keyword must be an integer. The argument to this keyword must be an integer.
The default is 3. The default is 3.
.It Cm ObscureKeystrokeTiming
Specifies whether
.Xr ssh 1
should try to obscure inter-keystroke timings from passive observers of
network traffic.
If enabled, then for interactive sessions,
.Xr ssh 1
will send keystrokes at fixed intervals of a few tens of milliseconds
and will send fake keystroke packets for some time after typing ceases.
The argument to this keyword must be
.Cm yes ,
.Cm no
or an interval specifier of the form
.Cm interval:milliseconds
(e.g.\&
.Cm interval:80
for 80 milliseconds).
The default is to obscure keystrokes using a 20ms packet interval.
Note that smaller intervals will result in higher fake keystroke packet rates.
.It Cm PasswordAuthentication .It Cm PasswordAuthentication
Specifies whether to use password authentication. Specifies whether to use password authentication.
The argument to this keyword must be The argument to this keyword must be
@ -2187,6 +2206,16 @@ accepts all tokens.
and and
.Cm ProxyJump .Cm ProxyJump
accept the tokens %%, %h, %n, %p, and %r. accept the tokens %%, %h, %n, %p, and %r.
.Pp
Note that some of these directives build commands for execution via the shell.
Because
.Xr ssh 1
performs no filtering or escaping of characters that have special meaning in
shell commands (e.g. quotes), it is the user's reposibility to ensure that
the arguments passed to
.Xr ssh 1
do not contain such characters and that tokens are appropriately quoted
when used.
.Sh ENVIRONMENT VARIABLES .Sh ENVIRONMENT VARIABLES
Arguments to some keywords can be expanded at runtime from environment Arguments to some keywords can be expanded at runtime from environment
variables on the client by enclosing them in variables on the client by enclosing them in

4
sshd.0
View File

@ -168,7 +168,7 @@ AUTHENTICATION
secure channel. secure channel.
After this, the client either requests an interactive shell or execution After this, the client either requests an interactive shell or execution
or a non-interactive command, which sshd will execute via the user's of a non-interactive command, which sshd will execute via the user's
shell using its -c option. The sides then enter session mode. In this shell using its -c option. The sides then enter session mode. In this
mode, either side may send data at any time, and such data is forwarded mode, either side may send data at any time, and such data is forwarded
to/from the shell or command on the server side, and the user terminal in to/from the shell or command on the server side, and the user terminal in
@ -683,4 +683,4 @@ AUTHORS
versions 1.5 and 2.0. Niels Provos and Markus Friedl contributed support versions 1.5 and 2.0. Niels Provos and Markus Friedl contributed support
for privilege separation. for privilege separation.
OpenBSD 7.3 February 10, 2023 OpenBSD 7.3 OpenBSD 7.3 September 19, 2023 OpenBSD 7.3

6
sshd.8
View File

@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.\" $OpenBSD: sshd.8,v 1.324 2023/02/10 06:39:27 jmc Exp $ .\" $OpenBSD: sshd.8,v 1.325 2023/09/19 20:37:07 deraadt Exp $
.Dd $Mdocdate: February 10 2023 $ .Dd $Mdocdate: September 19 2023 $
.Dt SSHD 8 .Dt SSHD 8
.Os .Os
.Sh NAME .Sh NAME
@ -320,7 +320,7 @@ forwarding TCP connections, or forwarding the authentication agent
connection over the secure channel. connection over the secure channel.
.Pp .Pp
After this, the client either requests an interactive shell or execution After this, the client either requests an interactive shell or execution
or a non-interactive command, which of a non-interactive command, which
.Nm .Nm
will execute via the user's shell using its will execute via the user's shell using its
.Fl c .Fl c

2
sshd.c
View File

@ -1697,7 +1697,7 @@ main(int ac, char **av)
break; break;
case 'V': case 'V':
fprintf(stderr, "%s, %s\n", fprintf(stderr, "%s, %s\n",
SSH_VERSION, SSH_OPENSSL_VERSION); SSH_RELEASE, SSH_OPENSSL_VERSION);
exit(0); exit(0);
default: default:
usage(); usage();

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.c,v 1.137 2023/07/27 22:23:05 djm Exp $ */ /* $OpenBSD: sshkey.c,v 1.138 2023/08/21 04:36:46 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@ -41,6 +41,7 @@
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <resolv.h> #include <resolv.h>
#include <time.h> #include <time.h>

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshsig.c,v 1.32 2023/04/06 03:56:02 djm Exp $ */ /* $OpenBSD: sshsig.c,v 1.33 2023/09/06 23:18:15 djm Exp $ */
/* /*
* Copyright (c) 2019 Google LLC * Copyright (c) 2019 Google LLC
* *
@ -38,7 +38,7 @@
#define SIG_VERSION 0x01 #define SIG_VERSION 0x01
#define MAGIC_PREAMBLE "SSHSIG" #define MAGIC_PREAMBLE "SSHSIG"
#define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1) #define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1)
#define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----\n" #define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----"
#define END_SIGNATURE "-----END SSH SIGNATURE-----" #define END_SIGNATURE "-----END SSH SIGNATURE-----"
#define RSA_SIGN_ALG "rsa-sha2-512" /* XXX maybe make configurable */ #define RSA_SIGN_ALG "rsa-sha2-512" /* XXX maybe make configurable */
#define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256" #define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256"
@ -59,8 +59,7 @@ sshsig_armor(const struct sshbuf *blob, struct sshbuf **out)
goto out; goto out;
} }
if ((r = sshbuf_put(buf, BEGIN_SIGNATURE, if ((r = sshbuf_putf(buf, "%s\n", BEGIN_SIGNATURE)) != 0) {
sizeof(BEGIN_SIGNATURE)-1)) != 0) {
error_fr(r, "sshbuf_putf"); error_fr(r, "sshbuf_putf");
goto out; goto out;
} }
@ -99,23 +98,35 @@ sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out)
return SSH_ERR_ALLOC_FAIL; return SSH_ERR_ALLOC_FAIL;
} }
/* Expect and consume preamble + lf/crlf */
if ((r = sshbuf_cmp(sbuf, 0, if ((r = sshbuf_cmp(sbuf, 0,
BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) { BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
error("Couldn't parse signature: missing header"); error("Couldn't parse signature: missing header");
goto done; goto done;
} }
if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) { if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
error_fr(r, "consume"); error_fr(r, "consume");
goto done; goto done;
} }
if ((r = sshbuf_cmp(sbuf, 0, "\r\n", 2)) == 0)
eoffset = 2;
else if ((r = sshbuf_cmp(sbuf, 0, "\n", 1)) == 0)
eoffset = 1;
else {
r = SSH_ERR_INVALID_FORMAT;
error_f("no header eol");
goto done;
}
if ((r = sshbuf_consume(sbuf, eoffset)) != 0) {
error_fr(r, "consume eol");
goto done;
}
/* Find and consume lf + suffix (any prior cr would be ignored) */
if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE, if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
sizeof("\n" END_SIGNATURE)-1, &eoffset)) != 0) { sizeof(END_SIGNATURE), &eoffset)) != 0) {
error("Couldn't parse signature: missing footer"); error("Couldn't parse signature: missing footer");
goto done; goto done;
} }
if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) { if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
error_fr(r, "consume"); error_fr(r, "consume");
goto done; goto done;

View File

@ -1,6 +1,6 @@
/* $OpenBSD: version.h,v 1.98 2023/08/10 01:01:07 djm Exp $ */ /* $OpenBSD: version.h,v 1.99 2023/10/04 04:04:09 djm Exp $ */
#define SSH_VERSION "OpenSSH_9.4" #define SSH_VERSION "OpenSSH_9.5"
#define SSH_PORTABLE "p1" #define SSH_PORTABLE "p1"
#define SSH_RELEASE SSH_VERSION SSH_PORTABLE #define SSH_RELEASE SSH_VERSION SSH_PORTABLE