Compare commits

..

No commits in common. "c8" and "c8s" have entirely different histories.
c8 ... c8s

106 changed files with 79 additions and 831 deletions

39
.gitignore vendored
View File

@ -1,3 +1,36 @@
SOURCES/DJM-GPG-KEY.gpg
SOURCES/openssh-8.0p1.tar.gz
SOURCES/pam_ssh_agent_auth-0.10.3.tar.bz2
openssh-5.5p1-noacss.tar.bz2
pam_ssh_agent_auth-0.9.2.tar.bz2
/openssh-5.6p1-noacss.tar.bz2
/pam_ssh_agent_auth-0.9.2.tar.bz2
/openssh-5.8p1-noacss.tar.bz2
/openssh-5.8p2-noacss.tar.bz2
/openssh-5.9p1-noacss.tar.bz2
/pam_ssh_agent_auth-0.9.3.tar.bz2
/openssh-6.0p1-noacss.tar.bz2
/openssh-6.1p1-noacss.tar.bz2
/openssh-6.2p1.tar.gz
/openssh-6.2p2.tar.gz
/openssh-6.3p1.tar.gz
/openssh-6.4p1.tar.gz
/openssh-6.6p1.tar.gz
/openssh-6.7p1.tar.gz
/openssh-6.8p1.tar.gz
/openssh-6.9p1.tar.gz
/openssh-7.0p1.tar.gz
/openssh-7.1p1.tar.gz
/openssh-7.1p2.tar.gz
/pam_ssh_agent_auth-0.10.2.tar.bz2
/openssh-7.2p1.tar.gz
/openssh-7.2p2.tar.gz
/openssh-7.3p1.tar.gz
/openssh-7.4p1.tar.gz
/pam_ssh_agent_auth-0.10.3.tar.bz2
/openssh-7.5p1.tar.gz
/openssh-7.6p1.tar.gz
/openssh-7.7p1.tar.gz
/openssh-7.7p1.tar.gz.asc
/DJM-GPG-KEY.gpg
/openssh-7.8p1.tar.gz
/openssh-7.8p1.tar.gz.asc
/openssh-8.0p1.tar.gz
/openssh-8.0p1.tar.gz.asc

View File

@ -1,3 +0,0 @@
bed7240bb17840b451b8f8457791c33456814d93 SOURCES/DJM-GPG-KEY.gpg
756dbb99193f9541c9206a667eaa27b0fa184a4f SOURCES/openssh-8.0p1.tar.gz
a4482a050fdad1d012427e45799564136708cf6b SOURCES/pam_ssh_agent_auth-0.10.3.tar.bz2

View File

@ -1,57 +0,0 @@
diff --git a/openssh-8.0p1/krl.c b/openssh-8.0p1/krl.c
index 8e2d5d5..e5b046d 100644
--- a/openssh-8.0p1/krl.c
+++ b/openssh-8.0p1/krl.c
@@ -676,6 +676,7 @@ revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf)
break;
case KRL_SECTION_CERT_SERIAL_BITMAP:
if (rs->lo - bitmap_start > INT_MAX) {
+ r = SSH_ERR_INVALID_FORMAT;
error("%s: insane bitmap gap", __func__);
goto out;
}
@@ -1011,6 +1012,7 @@ ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp,
goto out;
if ((krl = ssh_krl_init()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
error("%s: alloc failed", __func__);
goto out;
}
diff --git a/openssh-8.0p1/sshconnect2.c b/openssh-8.0p1/sshconnect2.c
index ce855eb..9650b24 100644
--- a/openssh-8.0p1/sshconnect2.c
+++ b/openssh-8.0p1/sshconnect2.c
@@ -95,7 +95,7 @@ struct sockaddr *xxx_hostaddr;
static int
verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh)
{
- if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1)
+ if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) != 0)
fatal("Host key verification failed.");
return 0;
}
@@ -767,6 +767,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
if ((pktype = sshkey_type_from_name(pkalg)) == KEY_UNSPEC) {
debug("%s: server sent unknown pkalg %s", __func__, pkalg);
+ r = SSH_ERR_INVALID_FORMAT;
goto done;
}
if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
@@ -777,6 +778,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
error("input_userauth_pk_ok: type mismatch "
"for decoded key (received %d, expected %d)",
key->type, pktype);
+ r = SSH_ERR_INVALID_FORMAT;
goto done;
}
@@ -796,6 +798,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
SSH_FP_DEFAULT);
error("%s: server replied with unknown key: %s %s", __func__,
sshkey_type(key), fp == NULL ? "<ERROR>" : fp);
+ r = SSH_ERR_INVALID_FORMAT;
goto done;
}
ident = format_identity(id);

View File

@ -1,99 +0,0 @@
diff --color -ruNp a/auth2-hostbased.c b/auth2-hostbased.c
--- a/auth2-hostbased.c 2026-04-15 12:41:41.506985043 +0200
+++ b/auth2-hostbased.c 2026-04-15 12:55:55.039916421 +0200
@@ -96,9 +96,10 @@ userauth_hostbased(struct ssh *ssh)
error("%s: cannot decode key: %s", __func__, pkalg);
goto done;
}
- if (key->type != pktype) {
- error("%s: type mismatch for decoded key "
- "(received %d, expected %d)", __func__, key->type, pktype);
+ if (key->type != pktype || (sshkey_type_plain(pktype) == KEY_ECDSA &&
+ sshkey_ecdsa_nid_from_name(pkalg) != key->ecdsa_nid)) {
+ error("%s: key type mismatch for decoded key "
+ "(received %s, expected %s)", __func__, sshkey_ssh_name(key), pkalg);
goto done;
}
if (sshkey_type_plain(key->type) == KEY_RSA &&
diff --color -ruNp a/auth2-pubkey.c b/auth2-pubkey.c
--- a/auth2-pubkey.c 2026-04-15 12:41:41.507225986 +0200
+++ b/auth2-pubkey.c 2026-04-15 12:55:06.559875789 +0200
@@ -136,9 +136,10 @@ userauth_pubkey(struct ssh *ssh)
error("%s: cannot decode key: %s", __func__, pkalg);
goto done;
}
- if (key->type != pktype) {
- error("%s: type mismatch for decoded key "
- "(received %d, expected %d)", __func__, key->type, pktype);
+ if (key->type != pktype || (sshkey_type_plain(pktype) == KEY_ECDSA &&
+ sshkey_ecdsa_nid_from_name(pkalg) != key->ecdsa_nid)) {
+ error("%s: key type mismatch for decoded key "
+ "(received %s, expected %s)", __func__, sshkey_ssh_name(key), pkalg);
goto done;
}
if (sshkey_type_plain(key->type) == KEY_RSA &&
diff --color -ruNp a/sshconnect2.c b/sshconnect2.c
--- a/sshconnect2.c 2026-04-15 12:41:41.546573648 +0200
+++ b/sshconnect2.c 2026-04-15 12:47:56.862867930 +0200
@@ -91,10 +91,15 @@ u_int session_id2_len = 0;
char *xxx_host;
struct sockaddr *xxx_hostaddr;
+static int key_type_allowed(struct sshkey *, const char *);
static int
verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh)
{
+ if (!key_type_allowed(hostkey, options.hostkeyalgorithms)) {
+ fatal("Server host key %s not in HostKeyAlgorithms",
+ sshkey_ssh_name(hostkey));
+ }
if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) != 0)
fatal("Host key verification failed.");
return 0;
@@ -1662,34 +1667,36 @@ load_identity_file(Identity *id)
}
static int
-key_type_allowed_by_config(struct sshkey *key)
+key_type_allowed(struct sshkey *key, const char *allowlist)
{
- if (match_pattern_list(sshkey_ssh_name(key),
- options.pubkey_key_types, 0) == 1)
+ if (match_pattern_list(sshkey_ssh_name(key), allowlist, 0) == 1)
return 1;
/* RSA keys/certs might be allowed by alternate signature types */
switch (key->type) {
case KEY_RSA:
- if (match_pattern_list("rsa-sha2-512",
- options.pubkey_key_types, 0) == 1)
+ if (match_pattern_list("rsa-sha2-512", allowlist, 0) == 1)
return 1;
- if (match_pattern_list("rsa-sha2-256",
- options.pubkey_key_types, 0) == 1)
+ if (match_pattern_list("rsa-sha2-256", allowlist, 0) == 1)
return 1;
break;
case KEY_RSA_CERT:
if (match_pattern_list("rsa-sha2-512-cert-v01@openssh.com",
- options.pubkey_key_types, 0) == 1)
+ allowlist, 0) == 1)
return 1;
if (match_pattern_list("rsa-sha2-256-cert-v01@openssh.com",
- options.pubkey_key_types, 0) == 1)
+ allowlist, 0) == 1)
return 1;
break;
}
return 0;
}
+static int
+key_type_allowed_by_config(struct sshkey *key)
+{
+ return key_type_allowed(key, options.pubkey_key_types);
+}
/*
* try keys in the following order:

View File

@ -1,20 +0,0 @@
diff --color -ruNp a/mux.c b/mux.c
--- a/mux.c 2026-04-15 12:22:36.533931440 +0200
+++ b/mux.c 2026-04-15 12:24:40.020578991 +0200
@@ -1133,6 +1133,16 @@ mux_master_process_proxy(struct ssh *ssh
debug("%s: channel %d: proxy request", __func__, c->self);
+ if (options.control_master == SSHCTL_MASTER_ASK ||
+ options.control_master == SSHCTL_MASTER_AUTO_ASK) {
+ if (!ask_permission("Allow multiplex proxy connection?")) {
+ debug2("%s: proxy refused by user", __func__);
+ reply_error(reply, MUX_S_PERMISSION_DENIED, rid,
+ "Permission denied");
+ return 0;
+ }
+ }
+
c->mux_rcb = channel_proxy_downstream;
if ((r = sshbuf_put_u32(reply, MUX_S_PROXY)) != 0 ||
(r = sshbuf_put_u32(reply, rid)) != 0)

View File

@ -1,425 +0,0 @@
diff --color -ruNp a/misc.c b/misc.c
--- a/misc.c 2026-04-15 13:31:07.822096096 +0200
+++ b/misc.c 2026-04-15 13:33:04.228235182 +0200
@@ -89,6 +89,20 @@ chop(char *s)
}
+/* remove whitespace from end of string */
+void
+rtrim(char *s)
+{
+ size_t i;
+
+ if ((i = strlen(s)) == 0)
+ return;
+ for (i--; i > 0; i--) {
+ if (isspace((int)s[i]))
+ s[i] = '\0';
+ }
+}
+
/* set/unset filedescriptor to non-blocking */
int
set_nonblock(int fd)
diff --color -ruNp a/misc.h b/misc.h
--- a/misc.h 2019-04-18 00:52:57.000000000 +0200
+++ b/misc.h 2026-04-15 13:33:13.656428006 +0200
@@ -44,6 +44,7 @@ struct ForwardOptions {
/* misc.c */
char *chop(char *);
+void rtrim(char *);
char *strdelim(char **);
char *strdelimw(char **);
int set_nonblock(int);
diff --color -ruNp a/readconf.c b/readconf.c
--- a/readconf.c 2026-04-15 13:31:07.787080808 +0200
+++ b/readconf.c 2026-04-15 13:31:40.930531592 +0200
@@ -1195,9 +1195,6 @@ parse_char_array:
case oProxyCommand:
charptr = &options->proxy_command;
- /* Ignore ProxyCommand if ProxyJump already specified */
- if (options->jump_host != NULL)
- charptr = &options->jump_host; /* Skip below */
parse_command:
if (s == NULL)
fatal("%.200s line %d: Missing argument.", filename, linenum);
@@ -1212,7 +1209,7 @@ parse_command:
filename, linenum);
}
len = strspn(s, WHITESPACE "=");
- if (parse_jump(s + len, options, *activep) == -1) {
+ if (parse_jump(s + len, options, cmdline, *activep) == -1) {
fatal("%.200s line %d: Invalid ProxyJump \"%s\"",
filename, linenum, s + len);
}
@@ -2440,57 +2437,116 @@ parse_forward(struct Forward *fwd, const
}
int
-parse_jump(const char *s, Options *o, int active)
+ssh_valid_hostname(const char *s)
{
- char *orig, *sdup, *cp;
- char *host = NULL, *user = NULL;
- int ret = -1, port = -1, first;
+ size_t i;
- active &= o->proxy_command == NULL && o->jump_host == NULL;
+ if (*s == '-')
+ return 0;
+ for (i = 0; s[i] != 0; i++) {
+ if (strchr("'`\"$\\;&<>|(){},", s[i]) != NULL ||
+ isspace((u_char)s[i]) || iscntrl((u_char)s[i]))
+ return 0;
+ }
+ return 1;
+}
+
+int
+ssh_valid_ruser(const char *s)
+{
+ size_t i;
+
+ if (*s == '-')
+ return 0;
+ for (i = 0; s[i] != 0; i++) {
+ if (iscntrl((u_char)s[i]))
+ return 0;
+ if (strchr("'`\";&<>|(){}", s[i]) != NULL)
+ return 0;
+ /* Disallow '-' after whitespace */
+ if (isspace((u_char)s[i]) && s[i + 1] == '-')
+ return 0;
+ /* Disallow \ in last position */
+ if (s[i] == '\\' && s[i + 1] == '\0')
+ return 0;
+ }
+ return 1;
+}
+
+int
+parse_jump(const char *s, Options *o, int strict, int active)
+{
+ char *orig = NULL, *sdup = NULL, *cp;
+ char *tmp_user = NULL, *tmp_host = NULL, *host = NULL, *user = NULL;
+ int r, ret = -1, tmp_port = -1, port = -1, first = 1;
+
+ if (strcasecmp(s, "none") == 0) {
+ if (active && o->jump_host == NULL) {
+ o->jump_host = xstrdup("none");
+ o->jump_port = 0;
+ }
+ return 0;
+ }
+
+ orig = xstrdup(s);
+ if ((cp = strchr(orig, '#')) != NULL)
+ *cp = '\0';
+ rtrim(orig);
- orig = sdup = xstrdup(s);
- first = active;
+ active &= o->proxy_command == NULL && o->jump_host == NULL;
+ sdup = xstrdup(orig);
do {
- if (strcasecmp(s, "none") == 0)
- break;
+ /* Work backwards through string */
if ((cp = strrchr(sdup, ',')) == NULL)
cp = sdup; /* last */
else
*cp++ = '\0';
- if (first) {
- /* First argument and configuration is active */
- if (parse_ssh_uri(cp, &user, &host, &port) == -1 ||
- parse_user_host_port(cp, &user, &host, &port) != 0)
+ r = parse_ssh_uri(cp, &tmp_user, &tmp_host, &tmp_port);
+ if (r == -1 || (r == 1 && parse_user_host_port(cp,
+ &tmp_user, &tmp_host, &tmp_port) != 0))
+ goto out; /* error already logged */
+ if (strict) {
+ if (!ssh_valid_hostname(tmp_host)) {
+ error("%s: invalid hostname \"%s\"", __func__, tmp_host);
goto out;
- } else {
- /* Subsequent argument or inactive configuration */
- if (parse_ssh_uri(cp, NULL, NULL, NULL) == -1 ||
- parse_user_host_port(cp, NULL, NULL, NULL) != 0)
+ }
+ if (tmp_user != NULL && !ssh_valid_ruser(tmp_user)) {
+ error("%s: invalid username \"%s\"", __func__, tmp_user);
goto out;
+ }
+ }
+ if (first) {
+ user = tmp_user;
+ host = tmp_host;
+ port = tmp_port;
+ tmp_user = tmp_host = NULL; /* transferred */
}
first = 0; /* only check syntax for subsequent hosts */
+ free(tmp_user);
+ free(tmp_host);
+ tmp_user = tmp_host = NULL;
+ tmp_port = -1;
} while (cp != sdup);
+
/* success */
if (active) {
- if (strcasecmp(s, "none") == 0) {
- o->jump_host = xstrdup("none");
- o->jump_port = 0;
- } else {
- o->jump_user = user;
- o->jump_host = host;
- o->jump_port = port;
- o->proxy_command = xstrdup("none");
- user = host = NULL;
- if ((cp = strrchr(s, ',')) != NULL && cp != s) {
- o->jump_extra = xstrdup(s);
- o->jump_extra[cp - s] = '\0';
- }
+ o->jump_user = user;
+ o->jump_host = host;
+ o->jump_port = port;
+ o->proxy_command = xstrdup("none");
+ user = host = NULL; /* transferred */
+ if (orig != NULL && (cp = strrchr(orig, ',')) != NULL) {
+ o->jump_extra = xstrdup(orig);
+ o->jump_extra[cp - orig] = '\0';
}
}
ret = 0;
out:
free(orig);
+ free(sdup);
+ free(tmp_user);
+ free(tmp_host);
free(user);
free(host);
return ret;
diff --color -ruNp a/readconf.h b/readconf.h
--- a/readconf.h 2026-04-15 13:31:07.615351090 +0200
+++ b/readconf.h 2026-04-15 13:31:40.931797538 +0200
@@ -211,7 +211,9 @@ int process_config_line(Options *, stru
int read_config_file(const char *, struct passwd *, const char *,
const char *, Options *, int, int *);
int parse_forward(struct Forward *, const char *, int, int);
-int parse_jump(const char *, Options *, int);
+int ssh_valid_hostname(const char *);
+int ssh_valid_ruser(const char *);
+int parse_jump(const char *, Options *, int, int);
int parse_ssh_uri(const char *, char **, char **, int *);
int default_ssh_port(void);
int option_clear_or_none(const char *);
diff --color -ruNp a/regress/Makefile b/regress/Makefile
--- a/regress/Makefile 2026-04-15 13:31:07.748508810 +0200
+++ b/regress/Makefile 2026-04-15 13:31:40.932487948 +0200
@@ -84,7 +84,8 @@ LTESTS= connect \
cfginclude \
servcfginclude \
allow-deny-users \
- authinfo
+ authinfo \
+ proxyjump
# dhgex \
diff --color -ruNp a/regress/proxyjump.sh b/regress/proxyjump.sh
--- a/regress/proxyjump.sh 1970-01-01 01:00:00.000000000 +0100
+++ b/regress/proxyjump.sh 2026-04-15 13:31:40.932813345 +0200
@@ -0,0 +1,102 @@
+# $OpenBSD: proxyjump.sh,v 1.1 2026/03/30 07:19:02 djm Exp $
+# Placed in the Public Domain.
+
+tid="proxyjump"
+
+# Parsing tests
+verbose "basic parsing"
+for jspec in \
+ "jump1" \
+ "user@jump1" \
+ "jump1:2222" \
+ "user@jump1:2222" \
+ "jump1,jump2" \
+ "user1@jump1:2221,user2@jump2:2222" \
+ "ssh://user@host:2223" \
+ ; do
+ case "$jspec" in
+ "jump1") expected="jump1" ;;
+ "user@jump1") expected="user@jump1" ;;
+ "jump1:2222") expected="jump1:2222" ;;
+ "user@jump1:2222") expected="user@jump1:2222" ;;
+ "jump1,jump2") expected="jump1,jump2" ;;
+ "user1@jump1:2221,user2@jump2:2222")
+ expected="user1@jump1:2221,user2@jump2:2222" ;;
+ "ssh://user@host:2223") expected="user@host:2223" ;;
+ esac
+ f=`${SSH} -GF /dev/null -oProxyJump="$jspec" somehost | \
+ awk '/^proxyjump /{print $2}'`
+ if [ "$f" != "$expected" ]; then
+ fail "ProxyJump $jspec: expected $expected, got $f"
+ fi
+ f=`${SSH} -GF /dev/null -J "$jspec" somehost | \
+ awk '/^proxyjump /{print $2}'`
+ if [ "$f" != "$expected" ]; then
+ fail "ssh -J $jspec: expected $expected, got $f"
+ fi
+done
+
+verbose "precedence"
+f=`${SSH} -GF /dev/null -oProxyJump=none -oProxyJump=jump1 somehost | \
+ grep "^proxyjump "`
+if [ -n "$f" ]; then
+ fail "ProxyJump=none first did not win"
+fi
+f=`${SSH} -GF /dev/null -oProxyJump=jump -oProxyCommand=foo somehost | \
+ grep "^proxyjump "`
+if [ "$f" != "proxyjump jump" ]; then
+ fail "ProxyJump first did not win over ProxyCommand"
+fi
+f=`${SSH} -GF /dev/null -oProxyCommand=foo -oProxyJump=jump somehost | \
+ grep "^proxycommand "`
+if [ "$f" != "proxycommand foo" ]; then
+ fail "ProxyCommand first did not win over ProxyJump"
+fi
+
+verbose "command-line -J invalid characters"
+cp $OBJ/ssh_config $OBJ/ssh_config.orig
+for jspec in \
+ "host;with;semicolon" \
+ "host'with'quote" \
+ "host\`with\`backtick" \
+ "host\$with\$dollar" \
+ "host(with)brace" \
+ "user;with;semicolon@host" \
+ "user'with'quote@host" \
+ "user\`with\`backtick@host" \
+ "user(with)brace@host" ; do
+ ${SSH} -GF /dev/null -J "$jspec" somehost >/dev/null 2>&1
+ if [ $? -ne 255 ]; then
+ fail "ssh -J \"$jspec\" was not rejected"
+ fi
+ ${SSH} -GF /dev/null -oProxyJump="$jspec" somehost >/dev/null 2>&1
+ if [ $? -ne 255 ]; then
+ fail "ssh -oProxyJump=\"$jspec\" was not rejected"
+ fi
+done
+# Special characters should be accepted in the config though.
+echo "ProxyJump user;with;semicolon@host;with;semicolon" >> $OBJ/ssh_config
+f=`${SSH} -GF $OBJ/ssh_config somehost | grep "^proxyjump "`
+if [ "$f" != "proxyjump user;with;semicolon@host;with;semicolon" ]; then
+ fail "ProxyJump did not allow special characters in config: $f"
+fi
+
+verbose "functional test"
+# Use different names to avoid the loop detection in ssh.c
+grep -iv HostKeyAlias $OBJ/ssh_config.orig > $OBJ/ssh_config
+cat << _EOF >> $OBJ/ssh_config
+Host jump-host
+ HostkeyAlias jump-host
+Host target-host
+ HostkeyAlias target-host
+_EOF
+cp $OBJ/known_hosts $OBJ/known_hosts.orig
+sed 's/^[^ ]* /jump-host /' < $OBJ/known_hosts.orig > $OBJ/known_hosts
+sed 's/^[^ ]* /target-host /' < $OBJ/known_hosts.orig >> $OBJ/known_hosts
+start_sshd
+
+verbose "functional ProxyJump"
+res=`${REAL_SSH} -F $OBJ/ssh_config -J jump-host target-host echo "SUCCESS" 2>/dev/null`
+if [ "$res" != "SUCCESS" ]; then
+ fail "functional test failed: expected SUCCESS, got $res"
+fi
diff --color -ruNp a/ssh.c b/ssh.c
--- a/ssh.c 2026-04-15 13:31:07.820492902 +0200
+++ b/ssh.c 2026-04-15 13:31:40.933136324 +0200
@@ -576,43 +576,6 @@ set_addrinfo_port(struct addrinfo *addrs
}
}
-static int
-valid_hostname(const char *s)
-{
- size_t i;
-
- if (*s == '-')
- return 0;
- for (i = 0; s[i] != 0; i++) {
- if (strchr("'`\"$\\;&<>|(){}", s[i]) != NULL ||
- isspace((u_char)s[i]) || iscntrl((u_char)s[i]))
- return 0;
- }
- return 1;
-}
-
-static int
-valid_ruser(const char *s)
-{
- size_t i;
-
- if (*s == '-')
- return 0;
- for (i = 0; s[i] != 0; i++) {
- if (iscntrl((u_char)s[i]))
- return 0;
- if (strchr("'`\";&<>|(){}", s[i]) != NULL)
- return 0;
- /* Disallow '-' after whitespace */
- if (isspace((u_char)s[i]) && s[i + 1] == '-')
- return 0;
- /* Disallow \ in last position */
- if (s[i] == '\\' && s[i + 1] == '\0')
- return 0;
- }
- return 1;
-}
-
/*
* Main program for the ssh client.
*/
@@ -842,9 +805,9 @@ main(int ac, char **av)
fatal("Only a single -J option permitted");
if (options.proxy_command != NULL)
fatal("Cannot specify -J with ProxyCommand");
- if (parse_jump(optarg, &options, 1) == -1)
+ if (parse_jump(optarg, &options, 1, 1) == -1)
+
fatal("Invalid -J argument");
- options.proxy_command = xstrdup("none");
break;
case 't':
if (options.request_tty == REQUEST_TTY_YES)
@@ -1085,10 +1048,15 @@ main(int ac, char **av)
if (!host)
usage();
- if (!valid_hostname(host))
- fatal("hostname contains invalid characters");
- if (options.user != NULL && !valid_ruser(options.user))
+ /*
+ * Validate commandline-specified values that end up in %tokens
+ * before they are used in config parsing.
+ */
+ if (options.user != NULL && !ssh_valid_ruser(options.user))
fatal("remote username contains invalid characters");
+ if (!ssh_valid_hostname(host))
+ fatal("hostname contains invalid characters");
+
host_arg = xstrdup(host);
/* Initialize the command to execute on remote host. */
@@ -1249,7 +1217,8 @@ main(int ac, char **av)
sshbin = "ssh";
/* Consistency check */
- if (options.proxy_command != NULL)
+ if (options.proxy_command != NULL &&
+ strcasecmp(options.proxy_command, "none") != 0)
fatal("inconsistent options: ProxyCommand+ProxyJump");
/* Never use FD passing for ProxyJump */
options.proxy_use_fdpass = 0;

View File

@ -1,12 +0,0 @@
diff --color -ruNp a/ssh.c b/ssh.c
--- a/ssh.c 2025-12-10 11:51:34.927545274 +0100
+++ b/ssh.c 2025-12-10 12:05:26.210486999 +0100
@@ -599,6 +599,8 @@ valid_ruser(const char *s)
if (*s == '-')
return 0;
for (i = 0; s[i] != 0; i++) {
+ if (iscntrl((u_char)s[i]))
+ return 0;
if (strchr("'`\";&<>|(){}", s[i]) != NULL)
return 0;
/* Disallow '-' after whitespace */

View File

@ -1,38 +0,0 @@
From d33ff14309e33aa79fdf95e1bc4facafa80b90a9 Mon Sep 17 00:00:00 2001
From: Stepan Broz <sbroz@redhat.com>
Date: Tue, 25 Jun 2024 17:38:22 +0200
Subject: [PATCH] upstream: ignore SIGPIPE earlier in main(), specifically
before
muxclient() which performs operations that could cause one; Reported by Noam
Lewis via bz3454, ok dtucker@
OpenBSD-Commit-ID: 63d8e13276869eebac6d7a05d5a96307f9026e47
---
ssh.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/ssh.c b/ssh.c
index 786e26d..e037c66 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1115,6 +1115,8 @@ main(int ac, char **av)
}
}
+ signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */
+
/*
* Initialize "log" output. Since we are the client all output
* goes to stderr unless otherwise specified by -y or -E.
@@ -1545,7 +1547,6 @@ main(int ac, char **av)
options.num_system_hostfiles);
tilde_expand_paths(options.user_hostfiles, options.num_user_hostfiles);
- signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */
signal(SIGCHLD, main_sigchld_handler);
/* Log into the remote system. Never returns if the login fails. */
--
2.45.2

View File

@ -1,14 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iQHDBAABCgAdFiEEWcIRjtIG2SfmZ+vj0+X1a22SDTAFAly3ro8ACgkQ0+X1a22S
DTCAiAx/W9XHoDs5NijyNIP43W2nFYuf6HG1duoLjdJ8rnsC3e90gx8h5RpUUh24
JDACoUFnbJsNgiQBaYpO7bOnf3Vw5Oui1gPeKnQ76KQsXDwD/N/0wLUf55+XdNJ6
tcgm6/x1W4b8bWje5bcS3qhxv6t/hSL/OxusA8zoNmnTD5XMg6QtJ0Rp9ZHPriCJ
C4eCPdHfmyHCr1IATMX9+n5CO5JUPexaDjQug7k/Z1XA/UlwVfRRs1JMpviBodC+
ZUOuk9tH11RKSBcUeR3Ef4iaR3FchryyyBZUZdYBkmDrnHrYpUK5ifdHT+ZXdzPl
laX03Kz094LqrP6L3lafk6b1PKOVjKwx1vM5fhnv+pfx4dmao9BwZMuIq6Fa5uMX
w2oHGhlIDmeT66Yny5d0APn2wCewyYUGPanSZY/HolHAPs+doOBgI361kMAR9J3e
Ii3VKhIdE8i4K3fC19uDkf7xL8UVvRVXjgM7i+GNndh1ou/vDYxmEAsW9IR/D3XC
HM/jMdq+UewAiRG46aI5rsi/A8J8/A==
=YtoH
-----END PGP SIGNATURE-----

View File

@ -1,37 +0,0 @@
diff --color -ruNp a/auth2-pubkey.c b/auth2-pubkey.c
--- a/auth2-pubkey.c 2026-04-13 15:13:58.759515611 +0200
+++ b/auth2-pubkey.c 2026-04-13 15:20:28.131029727 +0200
@@ -329,20 +329,23 @@ user_key_verify(struct ssh *ssh, const s
static int
match_principals_option(const char *principal_list, struct sshkey_cert *cert)
{
- char *result;
+ char *list, *olist, *entry;
u_int i;
- /* XXX percent_expand() sequences for authorized_principals? */
-
- for (i = 0; i < cert->nprincipals; i++) {
- if ((result = match_list(cert->principals[i],
- principal_list, NULL)) != NULL) {
- debug3("matched principal from key options \"%.100s\"",
- result);
- free(result);
- return 1;
+ olist = list = xstrdup(principal_list);
+ for (;;) {
+ if ((entry = strsep(&list, ",")) == NULL || *entry == '\0')
+ break;
+ for (i = 0; i < cert->nprincipals; i++) {
+ if (strcmp(entry, cert->principals[i]) == 0) {
+ debug3("matched principal from key i"
+ "options \"%.100s\"", entry);
+ free(olist);
+ return 1;
+ }
}
}
+ free(olist);
return 0;
}

View File

@ -1,15 +0,0 @@
diff --color -ruNp a/misc.c b/misc.c
--- a/misc.c 2025-12-09 17:16:21.637368818 +0100
+++ b/misc.c 2025-12-09 17:48:22.679192853 +0100
@@ -936,9 +936,10 @@ urldecode(const char *src)
*dst++ = ' ';
break;
case '%':
+ /* note: don't allow \0 characters */
if (!isxdigit((unsigned char)src[1]) ||
!isxdigit((unsigned char)src[2]) ||
- (ch = hexchar(src + 1)) == -1) {
+ (ch = hexchar(src + 1)) == -1 || ch == 0) {
free(ret);
return NULL;
}

View File

@ -1,15 +0,0 @@
diff --color -ruNp a/scp.c b/scp.c
--- a/scp.c 2026-04-07 15:54:11.193730842 +0200
+++ b/scp.c 2026-04-07 15:55:52.529425481 +0200
@@ -1705,8 +1705,10 @@ sink(int argc, char **argv, const char *
setimes = targisdir = 0;
mask = umask(0);
- if (!pflag)
+ if (!pflag) {
+ mask |= 07000;
(void) umask(mask);
+ }
if (argc != 1) {
run_err("ambiguous target");
exit(1);

8
gating.yaml Normal file
View File

@ -0,0 +1,8 @@
--- !Policy
product_versions:
- rhel-8
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.tier1.functional}
- !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.userspace-fips-mode.functional}
- !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.tedude.validation}

View File

@ -1504,9 +1504,11 @@ index 2abbb9ef..569dc83f 100644
kex_gen_hash(
int hash_alg,
const struct sshbuf *client_version,
diff --color -ruNp a/kexgssc.c b/kexgssc.c
--- a/kexgssc.c 1970-01-01 01:00:00.000000000 +0100
+++ b/kexgssc.c 2026-03-16 15:43:01.131354176 +0100
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 00000000..0b2f6a56
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,618 @@
+/*
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
@ -1562,8 +1564,8 @@ diff --color -ruNp a/kexgssc.c b/kexgssc.c
+{
+ struct kex *kex = ssh->kex;
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
+ recv_tok = GSS_C_EMPTY_BUFFER, gssbuf = GSS_C_EMPTY_BUFFER,
+ msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
+ recv_tok = GSS_C_EMPTY_BUFFER,
+ gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
+ Gssctxt *ctxt;
+ OM_uint32 maj_status, min_status, ret_flags;
+ struct sshbuf *server_blob = NULL;
@ -1715,11 +1717,11 @@ diff --color -ruNp a/kexgssc.c b/kexgssc.c
+ fatal("Failed to read token: %s", ssh_err(r));
+ /* If we're already complete - protocol error */
+ if (maj_status == GSS_S_COMPLETE)
+ ssh_packet_disconnect(ssh, "Protocol error: received token when complete");
+ sshpkt_disconnect(ssh, "Protocol error: received token when complete");
+ } else {
+ /* No token included */
+ if (maj_status != GSS_S_COMPLETE)
+ ssh_packet_disconnect(ssh, "Protocol error: did not receive final token");
+ sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
+ }
+ if ((r = sshpkt_get_end(ssh)) != 0) {
+ fatal("Expecting end of packet.");
@ -1735,7 +1737,7 @@ diff --color -ruNp a/kexgssc.c b/kexgssc.c
+ fatal("sshpkt_get failed: %s", ssh_err(r));
+ fatal("GSSAPI Error: \n%.400s", msg);
+ default:
+ ssh_packet_disconnect(ssh, "Protocol error: didn't expect packet type %d",
+ sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
+ type);
+ }
+ token_ptr = &recv_tok;
@ -1808,7 +1810,7 @@ diff --color -ruNp a/kexgssc.c b/kexgssc.c
+
+ /* Verify that the hash matches the MIC we just got. */
+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
+ ssh_packet_disconnect(ssh, "Hash's MIC didn't verify");
+ sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
+
+ gss_release_buffer(&min_status, &msg_tok);
+
@ -1840,8 +1842,8 @@ diff --color -ruNp a/kexgssc.c b/kexgssc.c
+{
+ struct kex *kex = ssh->kex;
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
+ recv_tok = GSS_C_EMPTY_BUFFER, gssbuf = GSS_C_EMPTY_BUFFER,
+ msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
+ recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
+ msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
+ Gssctxt *ctxt;
+ OM_uint32 maj_status, min_status, ret_flags;
+ struct sshbuf *shared_secret = NULL;
@ -2012,11 +2014,11 @@ diff --color -ruNp a/kexgssc.c b/kexgssc.c
+ fatal("sshpkt failed: %s", ssh_err(r));
+ /* If we're already complete - protocol error */
+ if (maj_status == GSS_S_COMPLETE)
+ ssh_packet_disconnect(ssh, "Protocol error: received token when complete");
+ sshpkt_disconnect(ssh, "Protocol error: received token when complete");
+ } else {
+ /* No token included */
+ if (maj_status != GSS_S_COMPLETE)
+ ssh_packet_disconnect(ssh, "Protocol error: did not receive final token");
+ sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
+ }
+ break;
+ case SSH2_MSG_KEXGSS_ERROR:
@ -2029,7 +2031,7 @@ diff --color -ruNp a/kexgssc.c b/kexgssc.c
+ fatal("sshpkt failed: %s", ssh_err(r));
+ fatal("GSSAPI Error: \n%.400s", msg);
+ default:
+ ssh_packet_disconnect(ssh, "Protocol error: didn't expect packet type %d",
+ sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
+ type);
+ }
+ token_ptr = &recv_tok;
@ -2091,7 +2093,7 @@ diff --color -ruNp a/kexgssc.c b/kexgssc.c
+
+ /* Verify that the hash matches the MIC we just got. */
+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
+ ssh_packet_disconnect(ssh, "Hash's MIC didn't verify");
+ sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
+
+ gss_release_buffer(&min_status, &msg_tok);
+
@ -2126,9 +2128,11 @@ diff --color -ruNp a/kexgssc.c b/kexgssc.c
+ return r;
+}
+#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
diff --color -ruNp a/kexgsss.c b/kexgsss.c
--- a/kexgsss.c 1970-01-01 01:00:00.000000000 +0100
+++ b/kexgsss.c 2026-03-16 15:45:31.256395698 +0100
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 00000000..60bc02de
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
@ -2196,8 +2200,7 @@ diff --color -ruNp a/kexgsss.c b/kexgsss.c
+ */
+
+ OM_uint32 ret_flags = 0;
+ gss_buffer_desc gssbuf = GSS_C_EMPTY_BUFFER,
+ recv_tok = GSS_C_EMPTY_BUFFER, msg_tok = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc gssbuf = {0, NULL}, recv_tok, msg_tok;
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+ Gssctxt *ctxt = NULL;
+ struct sshbuf *shared_secret = NULL;
@ -2301,7 +2304,7 @@ diff --color -ruNp a/kexgsss.c b/kexgsss.c
+ fatal("sshpkt failed: %s", ssh_err(r));
+ break;
+ default:
+ ssh_packet_disconnect(ssh,
+ sshpkt_disconnect(ssh,
+ "Protocol error: didn't expect packet type %d",
+ type);
+ }
@ -2400,8 +2403,7 @@ diff --color -ruNp a/kexgsss.c b/kexgsss.c
+ */
+
+ OM_uint32 ret_flags = 0;
+ gss_buffer_desc gssbuf = GSS_C_EMPTY_BUFFER,
+ recv_tok = GSS_C_EMPTY_BUFFER, msg_tok = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc gssbuf, recv_tok, msg_tok;
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+ Gssctxt *ctxt = NULL;
+ struct sshbuf *shared_secret = NULL;
@ -2457,8 +2459,10 @@ diff --color -ruNp a/kexgsss.c b/kexgsss.c
+ fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
+ min, nbits, max);
+ kex->dh = PRIVSEP(choose_dh(min, nbits, max));
+ if (kex->dh == NULL)
+ ssh_packet_disconnect(ssh, "Protocol error: no matching group found");
+ if (kex->dh == NULL) {
+ sshpkt_disconnect(ssh, "Protocol error: no matching group found");
+ fatal("Protocol error: no matching group found");
+ }
+
+ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
@ -2496,7 +2500,7 @@ diff --color -ruNp a/kexgsss.c b/kexgsss.c
+ fatal("sshpkt failed: %s", ssh_err(r));
+ break;
+ default:
+ ssh_packet_disconnect(ssh,
+ sshpkt_disconnect(ssh,
+ "Protocol error: didn't expect packet type %d",
+ type);
+ }

View File

@ -66,7 +66,7 @@
# Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1
%global openssh_ver 8.0p1
%global openssh_rel 29
%global openssh_rel 24
%global pam_ssh_agent_ver 0.10.3
%global pam_ssh_agent_rel 7
@ -292,27 +292,6 @@ Patch1018: openssh-9.6p1-CVE-2023-48795.patch
Patch1019: openssh-9.6p1-CVE-2023-51385.patch
# SCP kill switch
Patch1020: openssh-8.7p1-scp-kill-switch.patch
#upstream commit 96faa0de6c673a2ce84736eba37fc9fb723d9e5c
Patch1021: openssh-8.0p1-upstream-ignore-SIGPIPE.patch
#upstream commit 0832aac79517611dd4de93ad0a83577994d9c907
Patch1022: openssh-8.0p1-CVE-2025-26465.patch
# upstream 35d5917652106aede47621bb3f64044604164043
Patch1023: openssh-8.0p1-reject-cntrl-chars-in-username.patch
# upstream 43b3bff47bb029f2299bacb6a36057981b39fdb0
Patch1024: openssh-8.7p1-reject-null-char-in-url-string.patch
# upstream 487e8ac146f7d6616f65c125d5edb210519b833a
Patch1025: openssh-9.9p1-scp-clear-setuid.patch
# upstream c805b97b67c774e0bf922ffb29dfbcda9d7b5add
Patch1026: openssh-8.0p1-mux-askpass-check.patch
# upstream fd1c7e131f331942d20f42f31e79912d570081fa
Patch1027: openssh-8.0p1-ecdsa-incomplete-application.patch
# upstream fd1c7e131f331942d20f42f31e79912d570081fa
Patch1028: openssh-8.7p1-authorized-keys-principles-option.patch
# upstream 76685c9b09a66435cd2ad8373246adf1c53976d3
# upstream 0a0ef4515361143cad21afa072319823854c1cf6
# upstream 607bd871ec029e9aa22e632a22547250f3cae223
# upstream 1340d3fa8e4bb122906a82159c4c9b91584d65ce
Patch1029: openssh-8.0p1-proxyjump-username-validity-checks.patch
License: BSD
Group: Applications/Internet
@ -560,15 +539,6 @@ popd
%patch1018 -p1 -b .cve-2023-48795
%patch1019 -p1 -b .cve-2023-51385
%patch1020 -p1 -b .scp-kill-switch
%patch1021 -p1 -b .ignore-SIGPIPE
%patch1022 -p2 -b .cve-2025-26465
%patch1023 -p1 -b .reject-cntrl-chars-in-username
%patch1024 -p1 -b .reject-null-char-in-url-string
%patch1025 -p1 -b .scp-clear-setuid
%patch1026 -p1 -b .mux-askpass-check
%patch1027 -p1 -b .ecdsa-incomplete-application
%patch1028 -p1 -b .authorized-keys-principles-option
%patch1029 -p1 -b .proxyjump-username-validity-checks
autoreconf
pushd pam_ssh_agent_auth-%{pam_ssh_agent_ver}
@ -854,42 +824,6 @@ getent passwd sshd >/dev/null || \
%endif
%changelog
* Mon Apr 13 2026 Zoltan Fridrich <zfridric@redhat.com> - 8.0p1-29
- CVE-2026-35385: Fix privilege escalation via scp legacy protocol
when not in preserving file mode
Resolves: RHEL-164743
- CVE-2026-35388: Add connection multiplexing confirmation for proxy-mode
multiplexing sessions
Resolves: RHEL-166240
- CVE-2026-35387: Fix incomplete application of PubkeyAcceptedAlgorithms
and HostbasedAcceptedAlgorithms with regard to ECDSA keys
Resolves: RHEL-166224
- CVE-2026-35414: Fix mishandling of authorized_keys principals option
Resolves: RHEL-166192
- CVE-2026-35386: Add validation rules to usernames and hostnames
set for ProxyJump/-J on the commandline
Resolves: RHEL-166208
* Mon Mar 16 2026 Zoltan Fridrich <zfridric@redhat.com> - 8.0p1-28
- CVE-2026-3497: Fix information disclosure or denial of service due
to uninitialized variables in gssapi-keyex
Resolves: RHEL-155814
* Wed Dec 10 2025 Zoltan Fridrich <zfridric@redhat.com> - 8.0p1-27
- CVE-2025-61984: Reject usernames with control characters
Resolves: RHEL-128400
- CVE-2025-61985: Reject URL-strings with NULL characters
Resolves: RHEL-128390
* Wed Aug 20 2025 Antonio Vieiro <avieirov@redhat.com> - 8.0p1-26
- Fix missing invalid error code checks in OpenSSH. It prevents
a MITM attack when VerifyHostKeyDNS is on (CVE-2025-26465)
Resolves: RHEL-109228
* Tue Jun 25 2024 Stepan Broz <sbroz@redhat.com> - 8.0p1-25
- Upstream: Ignore SIGPIPE earlier in main()
Resolves: RHEL-37743
* Tue Feb 06 2024 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.0p1-24
- Providing a kill switch for scp to deal with CVE-2020-15778
Resolves: RHEL-22870

4
sources Normal file
View File

@ -0,0 +1,4 @@
SHA512 (openssh-8.0p1.tar.gz) = e280fa2d56f550efd37c5d2477670326261aa8b94d991f9eb17aad90e0c6c9c939efa90fe87d33260d0f709485cb05c379f0fd1bd44fc0d5190298b6398c9982
SHA512 (openssh-8.0p1.tar.gz.asc) = fe9e7383d9467e869762864f2b719165d9a3f2c5316c07067df1d45fc7819bd2cb8ef758454865595688804a4c160dd3d3aaee4c5f887859555d2c7bb8c4592b
SHA512 (DJM-GPG-KEY.gpg) = db1191ed9b6495999e05eed2ef863fb5179bdb63e94850f192dad68eed8579836f88fbcfffd9f28524fe1457aff8cd248ee3e0afc112c8f609b99a34b80ecc0d
SHA512 (pam_ssh_agent_auth-0.10.3.tar.bz2) = d75062c4e46b0b011f46aed9704a99049995fea8b5115ff7ee26dad7e93cbcf54a8af7efc6b521109d77dc03c6f5284574d2e1b84c6829cec25610f24fb4bd66

Some files were not shown because too many files have changed in this diff Show More