Files
openssh/openssh-mitigate-lingering-secrets.patch
Antonio Larrosa 52583b8481 Accepting request 1268126 from home:alarrosa:branches:network
- Update to openssh 10.0p1:
  * No changes for askpass, see main package changelog for
    details.

- Update to openssh 10.0p1:
  = Potentially-incompatible changes
  * This release removes support for the weak DSA signature
    algorithm, completing the deprecation process that began in
    2015 (when DSA was disabled by default) and repeatedly warned
    over the last 12 months.
  * scp(1), sftp(1): pass "ControlMaster no" to ssh when invoked by
    scp & sftp. This disables implicit session creation by these
    tools when ControlMaster was set to yes/auto by configuration,
    which some users found surprising. This change will not prevent
    scp/sftp from using an existing multiplexing session if one had
    already been created. GHPR557
  * This release has the version number 10.0 and announces itself
    as "SSH-2.0-OpenSSH_10.0". Software that naively matches
    versions using patterns like "OpenSSH_1*" may be confused by
    this.
  * sshd(8): this release removes the code responsible for the
    user authentication phase of the protocol from the per-
    connection sshd-session binary to a new sshd-auth binary.
    Splitting this code into a separate binary ensures that the
    crucial pre-authentication attack surface has an entirely
    disjoint address space from the code used for the rest of the
    connection. It also yields a small runtime memory saving as the
    authentication code will be unloaded after the authentication
    phase completes. This change should be largely invisible to
    users, though some log messages may now come from "sshd-auth"

OBS-URL: https://build.opensuse.org/request/show/1268126
OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=286
2025-04-09 10:49:15 +00:00

378 lines
10 KiB
Diff

Index: openssh-9.3p2/kex.c
===================================================================
--- openssh-9.3p2.orig/kex.c
+++ openssh-9.3p2/kex.c
@@ -1564,16 +1564,16 @@ enc_destroy(struct sshenc *enc)
return;
if (enc->key) {
- memset(enc->key, 0, enc->key_len);
+ explicit_bzero(enc->key, enc->key_len);
free(enc->key);
}
if (enc->iv) {
- memset(enc->iv, 0, enc->iv_len);
+ explicit_bzero(enc->iv, enc->iv_len);
free(enc->iv);
}
- memset(enc, 0, sizeof(*enc));
+ explicit_bzero(enc, sizeof(*enc));
}
void
@@ -1584,7 +1584,7 @@ newkeys_destroy(struct newkeys *newkeys)
enc_destroy(&newkeys->enc);
mac_destroy(&newkeys->mac);
- memset(&newkeys->comp, 0, sizeof(newkeys->comp));
+ explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
}
/*
Index: openssh-9.3p2/mac.c
===================================================================
--- openssh-9.3p2.orig/mac.c
+++ openssh-9.3p2/mac.c
@@ -284,11 +284,11 @@ mac_destroy(struct sshmac *mac)
return;
if (mac->key) {
- memset(mac->key, 0, mac->key_len);
+ explicit_bzero(mac->key, mac->key_len);
free(mac->key);
}
- memset(mac, 0, sizeof(*mac));
+ explicit_bzero(mac, sizeof(*mac));
}
/* XXX copied from ciphers_valid */
Index: openssh-9.3p2/monitor.c
===================================================================
--- openssh-9.3p2.orig/monitor.c
+++ openssh-9.3p2/monitor.c
@@ -1789,8 +1789,12 @@ mm_answer_audit_end_command(struct ssh *
void
monitor_clear_keystate(struct ssh *ssh, struct monitor *pmonitor)
{
- ssh_clear_newkeys(ssh, MODE_IN);
- ssh_clear_newkeys(ssh, MODE_OUT);
+ u_int mode;
+
+ for (mode = 0; mode < MODE_MAX; mode++) {
+ ssh_clear_curkeys(ssh, mode); /* current keys */
+ ssh_clear_newkeys(ssh, mode); /* next keys */
+ }
sshbuf_free(child_state);
child_state = NULL;
}
Index: openssh-9.3p2/packet.c
===================================================================
--- openssh-9.3p2.orig/packet.c
+++ openssh-9.3p2/packet.c
@@ -655,6 +655,7 @@ ssh_packet_close_internal(struct ssh *ss
ssh->local_ipaddr = NULL;
free(ssh->remote_ipaddr);
ssh->remote_ipaddr = NULL;
+ explicit_bzero(ssh->state, sizeof(*ssh->state));
free(ssh->state);
ssh->state = NULL;
kex_free(ssh->kex);
@@ -783,8 +784,10 @@ compress_buffer(struct ssh *ssh, struct
case Z_OK:
/* Append compressed data to output_buffer. */
if ((r = sshbuf_put(out, buf, sizeof(buf) -
- ssh->state->compression_out_stream.avail_out)) != 0)
+ ssh->state->compression_out_stream.avail_out)) != 0) {
+ explicit_bzero(buf, sizeof(buf));
return r;
+ }
break;
case Z_STREAM_ERROR:
default:
@@ -819,8 +822,10 @@ uncompress_buffer(struct ssh *ssh, struc
switch (status) {
case Z_OK:
if ((r = sshbuf_put(out, buf, sizeof(buf) -
- ssh->state->compression_in_stream.avail_out)) != 0)
+ ssh->state->compression_in_stream.avail_out)) != 0) {
+ explicit_bzero(buf, sizeof(buf));
return r;
+ }
break;
case Z_BUF_ERROR:
/*
@@ -870,6 +875,17 @@ uncompress_buffer(struct ssh *ssh, struc
#endif /* WITH_ZLIB */
void
+ssh_clear_curkeys(struct ssh *ssh, int mode)
+{
+ struct session_state *state = ssh->state;
+
+ if (state && state->newkeys[mode]) {
+ kex_free_newkeys(state->newkeys[mode]);
+ state->newkeys[mode] = NULL;
+ }
+}
+
+void
ssh_clear_newkeys(struct ssh *ssh, int mode)
{
if (ssh->kex && ssh->kex->newkeys[mode]) {
@@ -1418,7 +1434,9 @@ ssh_packet_read_seqnr(struct ssh *ssh, u
}
/* Append it to the buffer. */
- if ((r = ssh_packet_process_incoming(ssh, buf, len)) != 0)
+ r = ssh_packet_process_incoming(ssh, buf, len);
+ explicit_bzero(buf, len);
+ if (r != 0)
goto out;
}
out:
@@ -2375,9 +2393,12 @@ ssh_packet_get_state(struct ssh *ssh, st
(r = sshbuf_put_u32(m, state->p_read.packets)) != 0 ||
(r = sshbuf_put_u64(m, state->p_read.bytes)) != 0 ||
(r = sshbuf_put_stringb(m, state->input)) != 0 ||
- (r = sshbuf_put_stringb(m, state->output)) != 0)
+ (r = sshbuf_put_stringb(m, state->output)) != 0) {
+ sshbuf_obfuscate(m);
return r;
+ }
+ sshbuf_obfuscate(m);
return 0;
}
@@ -2496,6 +2517,8 @@ ssh_packet_set_state(struct ssh *ssh, st
size_t ilen, olen;
int r;
+ sshbuf_unobfuscate(m);
+
if ((r = kex_from_blob(m, &ssh->kex)) != 0 ||
(r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 ||
(r = newkeys_from_blob(m, ssh, MODE_IN)) != 0 ||
@@ -2509,7 +2532,7 @@ ssh_packet_set_state(struct ssh *ssh, st
(r = sshbuf_get_u64(m, &state->p_read.blocks)) != 0 ||
(r = sshbuf_get_u32(m, &state->p_read.packets)) != 0 ||
(r = sshbuf_get_u64(m, &state->p_read.bytes)) != 0)
- return r;
+ goto out;
/*
* We set the time here so that in post-auth privsep child we
* count from the completion of the authentication.
@@ -2518,10 +2541,10 @@ ssh_packet_set_state(struct ssh *ssh, st
/* XXX ssh_set_newkeys overrides p_read.packets? XXX */
if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0 ||
(r = ssh_set_newkeys(ssh, MODE_OUT)) != 0)
- return r;
+ goto out;
if ((r = ssh_packet_set_postauth(ssh)) != 0)
- return r;
+ goto out;
sshbuf_reset(state->input);
sshbuf_reset(state->output);
@@ -2529,12 +2552,19 @@ ssh_packet_set_state(struct ssh *ssh, st
(r = sshbuf_get_string_direct(m, &output, &olen)) != 0 ||
(r = sshbuf_put(state->input, input, ilen)) != 0 ||
(r = sshbuf_put(state->output, output, olen)) != 0)
- return r;
+ goto out;
- if (sshbuf_len(m))
- return SSH_ERR_INVALID_FORMAT;
+ if (sshbuf_len(m)) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+
+ r = 0;
+out:
+ if (r != 0)
+ sshbuf_obfuscate(m);
debug3_f("done");
- return 0;
+ return r;
}
/* NEW API */
Index: openssh-9.3p2/packet.h
===================================================================
--- openssh-9.3p2.orig/packet.h
+++ openssh-9.3p2/packet.h
@@ -103,6 +103,7 @@ void ssh_packet_close(struct ssh *);
void ssh_packet_close(struct ssh *);
void ssh_packet_set_input_hook(struct ssh *, ssh_packet_hook_fn *, void *);
void ssh_packet_clear_keys(struct ssh *);
+void ssh_clear_curkeys(struct ssh *, int);
void ssh_clear_newkeys(struct ssh *, int);
int ssh_packet_is_rekeying(struct ssh *);
Index: openssh-9.3p2/sshbuf.c
===================================================================
--- openssh-9.3p2.orig/sshbuf.c
+++ openssh-9.3p2/sshbuf.c
@@ -309,6 +309,31 @@ sshbuf_mutable_ptr(const struct sshbuf *
return buf->d + buf->off;
}
+/* Trivially obfuscate the buffer. This is used to make sensitive data
+ * (e.g. keystate) slightly less obvious if found lingering in kernel
+ * memory after being sent from the privsep child to its parent.
+ *
+ * Longer term we should consider using a one-time pad or a stream cipher
+ * here. */
+void
+sshbuf_obfuscate(struct sshbuf *buf)
+{
+ size_t i;
+
+ if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
+ return;
+
+ for (i = buf->off; i < buf->size; i++) {
+ buf->d [i] ^= 0xaa;
+ }
+}
+
+void
+sshbuf_unobfuscate(struct sshbuf *buf)
+{
+ sshbuf_obfuscate(buf);
+}
+
int
sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
{
Index: openssh-9.3p2/sshbuf.h
===================================================================
--- openssh-9.3p2.orig/sshbuf.h
+++ openssh-9.3p2/sshbuf.h
@@ -298,6 +298,9 @@ int sshbuf_write_file(const char *path,
int sshbuf_read(int, struct sshbuf *, size_t, size_t *)
__attribute__((__nonnull__ (2)));
+void sshbuf_obfuscate(struct sshbuf *buf);
+void sshbuf_unobfuscate(struct sshbuf *buf);
+
/* Macros for decoding/encoding integers */
#define PEEK_U64(p) \
(((u_int64_t)(((const u_char *)(p))[0]) << 56) | \
Index: openssh-9.3p2/sshd-session.c
===================================================================
--- openssh-9.3p2.orig/sshd-session.c
+++ openssh-9.3p2/sshd-session.c
@@ -197,6 +197,19 @@ static void do_ssh2_kex(struct ssh *);
}
/*
+ * Clear some stack space. This is a bit naive, but hopefully helps mitigate
+ * information leaks due to registers and other data having been stored on
+ * the stack. Called after fork() and before exit().
+ */
+static void
+clobber_stack(void)
+{
+ char data [32768];
+
+ explicit_bzero(data, 32768);
+}
+
+/*
* Signal handler for the alarm after the login grace period has expired.
* As usual, this may only take signal-safe actions, even though it is
* terminal.
@@ -260,6 +260,8 @@ destroy_sensitive_data(struct ssh *ssh,
sensitive_data.host_certificates[i] = NULL;
}
}
+
+ clobber_stack();
}
/* Demote private to public keys for network child */
@@ -431,6 +432,8 @@ privsep_preauth(struct ssh *ssh)
{
int skip_privdrop = 0;
+ clobber_stack();
+
/*
* Hack for systems that don't support FD passing: retain privileges
* in the post-auth privsep process so it can allocate PTYs directly.
#@@ -1354,6 +1356,7 @@ main(int ac, char **av)
# */
# mm_send_keystate(ssh, pmonitor);
# ssh_packet_clear_keys(ssh);
#+ clobber_stack();
# exit(0);
#
# authenticated:
@@ -1431,6 +1434,7 @@ main(int ac, char **av)
mm_terminate();
+ clobber_stack();
exit(0);
}
@@ -1577,8 +1581,10 @@ cleanup_exit(int i)
/* cleanup_exit can be called at the very least from the privsep
wrappers used for auditing. Make sure we don't recurse
indefinitely. */
- if (in_cleanup)
+ if (in_cleanup) {
+ clobber_stack();
_exit(i);
+ }
in_cleanup = 1;
extern int auth_attempted; /* monitor.c */
@@ -1604,6 +1610,8 @@ cleanup_exit(int i)
mm_is_monitor())
audit_event(the_active_state, SSH_CONNECTION_ABANDON);
#endif
+
+ clobber_stack();
/* Override default fatal exit value when auth was attempted */
if (i == 255 && auth_attempted)
_exit(EXIT_AUTH_ATTEMPTED);
Index: openssh-9.9p2/sshd-auth.c
===================================================================
--- openssh-9.9p2.orig/sshd-auth.c
+++ openssh-9.9p2/sshd-auth.c
@@ -197,6 +197,19 @@ static void do_ssh2_kex(struct ssh *);
return 0;
}
+/*
+ * Clear some stack space. This is a bit naive, but hopefully helps mitigate
+ * information leaks due to registers and other data having been stored on
+ * the stack. Called after fork() and before exit().
+ */
+static void
+clobber_stack(void)
+{
+ char data [32768];
+
+ explicit_bzero(data, 32768);
+}
+
static void
privsep_child_demote(void)
{
@@ -796,6 +796,7 @@ main(int ac, char **av)
*/
mm_send_keystate(ssh, pmonitor);
ssh_packet_clear_keys(ssh);
+ clobber_stack();
exit(0);
}