Version in base suite: 10.0p1-7+deb13u1 Version in overlay suite: 10.0p1-7+deb13u2 Base version: openssh_10.0p1-7+deb13u2 Target version: openssh_10.0p1-7+deb13u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/o/openssh/openssh_10.0p1-7+deb13u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/o/openssh/openssh_10.0p1-7+deb13u3.dsc .git-dpm | 4 changelog | 61 ++ patches/CVE-2025-61984-tests.patch | 2 patches/CVE-2025-61984.patch | 2 patches/CVE-2025-61985.patch | 2 patches/CVE-2026-35385.patch | 39 + patches/CVE-2026-35386-1.patch | 318 +++++++++++++++ patches/CVE-2026-35386-2.patch | 54 ++ patches/CVE-2026-35386-3.patch | 38 + patches/CVE-2026-35387.patch | 136 ++++++ patches/CVE-2026-35388.patch | 40 + patches/CVE-2026-35414.patch | 79 +++ patches/configure-cache-vars.patch | 2 patches/fix-max-startups-tracking.patch | 2 patches/ipqos-deprecate-tos-keywords.patch | 175 ++++++++ patches/ipqos-interactive-ef.patch | 86 ++++ patches/ipqos-set-at-runtime.patch | 595 +++++++++++++++++++++++++++++ patches/ipqos-set-extended-type.patch | 68 +++ patches/pam-avoid-unknown-host.patch | 2 patches/regress-conch-dev-zero.patch | 2 patches/revert-ipqos-defaults.patch | 93 ---- patches/series | 12 patches/skip-utimensat-test-on-zfs.patch | 2 patches/systemd-socket-activation.patch | 2 24 files changed, 1711 insertions(+), 105 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp8e5ijmoy/openssh_10.0p1-7+deb13u2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp8e5ijmoy/openssh_10.0p1-7+deb13u3.dsc: no acceptable signature found diff -Nru openssh-10.0p1/debian/.git-dpm openssh-10.0p1/debian/.git-dpm --- openssh-10.0p1/debian/.git-dpm 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/.git-dpm 2026-05-05 10:25:39.000000000 +0000 @@ -1,6 +1,6 @@ # see git-dpm(1) from git-dpm package -947d15f4b44cf7d4ce337c82ed7e1a167a4f4dc2 -947d15f4b44cf7d4ce337c82ed7e1a167a4f4dc2 +4207d8a7a4060cad77ec1b78ff08f3e0546c4fbd +4207d8a7a4060cad77ec1b78ff08f3e0546c4fbd 860fa104f07024318a40065f07708daa5753f55d 860fa104f07024318a40065f07708daa5753f55d openssh_10.0p1.orig.tar.gz diff -Nru openssh-10.0p1/debian/changelog openssh-10.0p1/debian/changelog --- openssh-10.0p1/debian/changelog 2026-04-04 23:29:20.000000000 +0000 +++ openssh-10.0p1/debian/changelog 2026-05-05 10:25:39.000000000 +0000 @@ -1,3 +1,64 @@ +openssh (1:10.0p1-7+deb13u3) trixie; urgency=medium + + * Backport minor security fixes from 10.3p1: + - ssh(1): the -J and equivalent -oProxyJump="..." options now validate + user and host names for ProxyJump/-J options passed via the + command-line (no such validation is performed for this option in + configuration files). This prevents shell injection in situations + where these were directly exposed to adversarial input, which would + have been a terrible idea to begin with. + - CVE-2026-35386: ssh(1): validation of shell metacharacters in user + names supplied on the command-line was performed too late to prevent + some situations where they could be expanded from %-tokens in + ssh_config. For certain configurations, such as those that use a "%u" + token in a "Match exec" block, an attacker who can control the user + name passed to ssh(1) could potentially execute arbitrary shell + commands. Reported by Florian Kohnhäuser (closes: #1132573). + We continue to recommend against directly exposing ssh(1) and other + tools' command-lines to untrusted input. Mitigations such as this can + not be absolute given the variety of shells and user configurations in + use. + - CVE-2026-35414: sshd(8): when matching an authorized_keys + principals="" option against a list of principals in a certificate, an + incorrect algorithm was used that could allow inappropriate matching + in cases where a principal name in the certificate contains a comma + character. Exploitation of the condition requires an authorized_keys + principals="" option that lists more than one principal *and* a CA + that will issue a certificate that encodes more than one of these + principal names separated by a comma (typical CAs strongly constrain + which principal names they will place in a certificate). This + condition only applies to user- trusted CA keys in authorized_keys, + the main certificate authentication path + (TrustedUserCAKeys/AuthorizedPrincipalsFile) is not affected. Reported + by Vladimir Tokarev (closes: #1132576). + - CVE-2026-35385: scp(1): when downloading files as root in legacy (-O) + mode and without the -p (preserve modes) flag set, scp did not clear + setuid/setgid bits from downloaded files as one might typically + expect. This bug dates back to the original Berkeley rcp program. + Reported by Christos Papakonstantinou of Cantina and Spearbit (closes: + #1132572). + - CVE-2026-35387: sshd(8): fix incomplete application of + PubkeyAcceptedAlgorithms and HostbasedAcceptedAlgorithms with regard + to ECDSA keys. Previously if one of these directives contains any + ECDSA algorithm name (say "ecdsa-sha2-nistp384"), then any other ECDSA + algorithm would be accepted in its place regardless of whether it was + listed or not. Reported by Christos Papakonstantinou of Cantina and + Spearbit (closes: #1132574). + - CVE-2026-35388: ssh(1): connection multiplexing confirmation + (requested using "ControlMaster ask/autoask") was not being tested for + proxy mode multiplexing sessions (i.e. "ssh -O proxy ..."). Reported + by Michalis Vasileiadis (closes: #1132575). + * Cherry-pick IPQoS handling updates from upstream: + - Set default IPQoS for interactive sessions to Expedited Forwarding + (EF). + - Deprecate support for IPv4 type-of-service (TOS) IPQoS keywords. + - Make ssh(1) and sshd(8) set IP QoS (aka IP_TOS, IPV6_TCLASS) + continually at runtime based on what sessions/channels are open. + - Correctly set extended type for client-side channels. Fixes + interactive vs bulk IPQoS for client->server traffic. + + -- Colin Watson Tue, 05 May 2026 11:25:39 +0100 + openssh (1:10.0p1-7+deb13u2) trixie-security; urgency=medium * CVE-2026-3497: Fix incorrect GSS-API error handling; Replace incorrect diff -Nru openssh-10.0p1/debian/patches/CVE-2025-61984-tests.patch openssh-10.0p1/debian/patches/CVE-2025-61984-tests.patch --- openssh-10.0p1/debian/patches/CVE-2025-61984-tests.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2025-61984-tests.patch 2026-05-05 10:25:39.000000000 +0000 @@ -1,4 +1,4 @@ -From 4a8b438b5a7cd0534dbfa11e953935ae24debbc6 Mon Sep 17 00:00:00 2001 +From 8644b923d0b735dafd16d80deb3576d7055d8cf2 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Thu, 4 Sep 2025 03:04:44 +0000 Subject: upstream: repair test after changes to percent expansion of usernames diff -Nru openssh-10.0p1/debian/patches/CVE-2025-61984.patch openssh-10.0p1/debian/patches/CVE-2025-61984.patch --- openssh-10.0p1/debian/patches/CVE-2025-61984.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2025-61984.patch 2026-05-05 10:25:39.000000000 +0000 @@ -1,4 +1,4 @@ -From 82a6200c6affd9a90b3fe8e2fdea93b839319aea Mon Sep 17 00:00:00 2001 +From 7f30210200b3f7f4cf1eddfcee49a435f8b68419 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Thu, 4 Sep 2025 00:29:09 +0000 Subject: upstream: Improve rules for %-expansion of username. diff -Nru openssh-10.0p1/debian/patches/CVE-2025-61985.patch openssh-10.0p1/debian/patches/CVE-2025-61985.patch --- openssh-10.0p1/debian/patches/CVE-2025-61985.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2025-61985.patch 2026-05-05 10:25:39.000000000 +0000 @@ -1,4 +1,4 @@ -From 51b9b26c9f76b2594ca93ce1ac49aa10931d098a Mon Sep 17 00:00:00 2001 +From db9b2c27baa9b28764685c1383ebda333ecc76ba Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Thu, 4 Sep 2025 00:30:06 +0000 Subject: upstream: don't allow \0 characters in url-encoded strings. diff -Nru openssh-10.0p1/debian/patches/CVE-2026-35385.patch openssh-10.0p1/debian/patches/CVE-2026-35385.patch --- openssh-10.0p1/debian/patches/CVE-2026-35385.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2026-35385.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,39 @@ +From 8be19efcbe7ca5e2eadde1a19dcb4763b380c8e5 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Thu, 2 Apr 2026 07:42:16 +0000 +Subject: upstream: when downloading files as root in legacy (-O) mode and + +without the -p (preserve modes) flag set, clear setuid/setgid bits from +downloaded files as one might expect. + +AFAIK this bug dates back to the original Berkeley rcp program. + +Reported by Christos Papakonstantinou of Cantina and Spearbit. + +OpenBSD-Commit-ID: 49e902fca8dd933a92a9b547ab31f63e86729fa1 + +Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=487e8ac146f7d6616f65c125d5edb210519b833a +Bug-Debian: https://bugs.debian.org/1132572 +Last-Update: 2026-05-01 + +Patch-Name: CVE-2026-35385.patch +--- + scp.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/scp.c b/scp.c +index ec2040a6c..2199a7376 100644 +--- a/scp.c ++++ b/scp.c +@@ -1693,8 +1693,10 @@ sink(int argc, char **argv, const char *src) + + setimes = targisdir = 0; + mask = umask(0); +- if (!pflag) ++ if (!pflag) { ++ mask |= 07000; + (void) umask(mask); ++ } + if (argc != 1) { + run_err("ambiguous target"); + exit(1); diff -Nru openssh-10.0p1/debian/patches/CVE-2026-35386-1.patch openssh-10.0p1/debian/patches/CVE-2026-35386-1.patch --- openssh-10.0p1/debian/patches/CVE-2026-35386-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2026-35386-1.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,318 @@ +From df454d9e693b6af926e7a61929c14adcbecb96b7 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Mon, 30 Mar 2026 07:18:24 +0000 +Subject: upstream: apply the same validity rules to usernames and hostnames + +set for ProxyJump/-J on the commandline as we do for destination user/host +names. + +Specifically, they are no longer allowed to contain most characters +that have special meaning for common shells. Special characters are +still allowed in ProxyJump commands that are specified in the config +files. + +This _reduces_ the chance that shell characters from a hostile -J +option from ending up in a shell execution context. + +Don't pass untrusted stuff to the ssh commandline, it's not intended +to be a security boundary. We try to make it safe where we can, but +we can't make guarantees, because we can't know the parsing rules +and special characters for all the shells in the world, nor can we +know what the user does with this data in their ssh_config wrt +percent expansion, LocalCommand, match exec, etc. + +While I'm in there, make ProxyJump and ProxyCommand first-match-wins +between each other. + +reported by rabbit; ok dtucker@ + +OpenBSD-Commit-ID: f05ad8a1eb5f6735f9a935a71a90580226759263 + +Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=0a0ef4515361143cad21afa072319823854c1cf6 +Bug-Debian: https://bugs.debian.org/1132573 +Last-Update: 2026-05-01 + +Patch-Name: CVE-2026-35386-1.patch +--- + readconf.c | 124 +++++++++++++++++++++++++++++++++++++---------------- + readconf.h | 4 +- + ssh.c | 48 +++------------------ + 3 files changed, 95 insertions(+), 81 deletions(-) + +diff --git a/readconf.c b/readconf.c +index fc625a00c..3bb7ac249 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -1585,9 +1585,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 (str == NULL) { + error("%.200s line %d: Missing argument.", +@@ -1608,7 +1605,7 @@ parse_command: + } + len = strspn(str, WHITESPACE "="); + /* XXX use argv? */ +- if (parse_jump(str + len, options, *activep) == -1) { ++ if (parse_jump(str + len, options, cmdline, *activep) == -1) { + error("%.200s line %d: Invalid ProxyJump \"%s\"", + filename, linenum, str + len); + goto out; +@@ -3444,65 +3441,116 @@ parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remo + } + + 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 r, 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; ++} + +- orig = sdup = xstrdup(s); ++int ++ssh_valid_ruser(const char *s) ++{ ++ size_t i; + +- /* Remove comment and trailing whitespace */ ++ 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); + +- 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'; + ++ 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_f("invalid hostname \"%s\"", tmp_host); ++ goto out; ++ } ++ if (tmp_user != NULL && !ssh_valid_ruser(tmp_user)) { ++ error_f("invalid username \"%s\"", tmp_user); ++ goto out; ++ } ++ } + if (first) { +- /* First argument and configuration is active */ +- r = parse_ssh_uri(cp, &user, &host, &port); +- if (r == -1 || (r == 1 && +- parse_user_host_port(cp, &user, &host, &port) != 0)) +- goto out; +- } else { +- /* Subsequent argument or inactive configuration */ +- r = parse_ssh_uri(cp, NULL, NULL, NULL); +- if (r == -1 || (r == 1 && +- parse_user_host_port(cp, NULL, NULL, NULL) != 0)) +- goto out; ++ 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 --git a/readconf.h b/readconf.h +index 368523dd7..58a44c2e8 100644 +--- a/readconf.h ++++ b/readconf.h +@@ -250,7 +250,9 @@ int process_config_line(Options *, struct passwd *, const char *, + int read_config_file(const char *, struct passwd *, const char *, + 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 --git a/ssh.c b/ssh.c +index 9a8267869..a2fe324f5 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -631,43 +631,6 @@ ssh_conn_info_free(struct ssh_conn_info *cinfo) + free(cinfo); + } + +-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. + */ +@@ -918,9 +881,9 @@ main(int ac, char **av) + } + 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) +@@ -1170,7 +1133,7 @@ main(int ac, char **av) + if (!host) + usage(); + +- if (!valid_hostname(host)) ++ if (!ssh_valid_hostname(host)) + fatal("hostname contains invalid characters"); + options.host_arg = xstrdup(host); + +@@ -1341,7 +1304,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; +@@ -1481,7 +1445,7 @@ main(int ac, char **av) + * via configuration (i.e. not expanded) are not subject to validation. + */ + if ((user_on_commandline || user_expanded) && +- !valid_ruser(options.user)) ++ !ssh_valid_ruser(options.user)) + fatal("remote username contains invalid characters"); + + /* Now User is expanded, store it and calculate hash. */ diff -Nru openssh-10.0p1/debian/patches/CVE-2026-35386-2.patch openssh-10.0p1/debian/patches/CVE-2026-35386-2.patch --- openssh-10.0p1/debian/patches/CVE-2026-35386-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2026-35386-2.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,54 @@ +From c03c0da2f6975aaab06ea98025213c8ea2fb5f8d Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Thu, 2 Apr 2026 07:50:55 +0000 +Subject: upstream: move username validity check for usernames specified on +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +the commandline to earlier in main(), specifically before some contexts where +a username with shell characters might be expanded by a %u directive in +ssh_config. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We continue to recommend against using untrusted input on +the SSH commandline. Mitigations like this are not 100% +guarantees of safety because we can't control every +combination of user shell and configuration where they are +used. + +Reported by Florian Kohnhäuser + +OpenBSD-Commit-ID: 25ef72223f5ccf1c38d307ae77c23c03f59acc55 + +Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=76685c9b09a66435cd2ad8373246adf1c53976d3 +Bug-Debian: https://bugs.debian.org/1132573 +Last-Update: 2026-05-01 + +Patch-Name: CVE-2026-35386-2.patch +--- + ssh.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/ssh.c b/ssh.c +index a2fe324f5..e668fb771 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -1133,8 +1133,15 @@ main(int ac, char **av) + if (!host) + usage(); + ++ /* ++ * 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"); ++ + options.host_arg = xstrdup(host); + + /* Initialize the command to execute on remote host. */ diff -Nru openssh-10.0p1/debian/patches/CVE-2026-35386-3.patch openssh-10.0p1/debian/patches/CVE-2026-35386-3.patch --- openssh-10.0p1/debian/patches/CVE-2026-35386-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2026-35386-3.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,38 @@ +From b2c2f35eb41467aad55636283a7f849260312a0d Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Thu, 2 Apr 2026 07:52:15 +0000 +Subject: upstream: adapt to username validity check change + +OpenBSD-Regress-ID: d22c66ca60f0d934a75e6ca752c4c11b9f4a5324 + +Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=5aa09926fbf050d484a79717fadec8360c5c5645 +Bug-Debian: https://bugs.debian.org/1132573 +Last-Update: 2026-05-01 + +Patch-Name: CVE-2026-35386-3.patch +--- + regress/percent.sh | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/regress/percent.sh b/regress/percent.sh +index c607c8d23..bf4e1f2e5 100644 +--- a/regress/percent.sh ++++ b/regress/percent.sh +@@ -140,7 +140,7 @@ done + FOO=bar + export FOO + for i in controlpath identityagent forwardagent localforward remoteforward \ +- user setenv userknownhostsfile; do ++ setenv userknownhostsfile; do + verbose $tid $i dollar + trial $i '${FOO}' $FOO + done +@@ -175,7 +175,7 @@ ${SSH} -F $OBJ/ssh_proxy -G "${FOO}@somehost" && fail "user-at expanded env" + + # Literal control characters in config is acceptable + verbose $tid user control-literal +-trial user "$FOO" "$FOO" ++#trial user "$FOO" "$FOO" + + # Control characters expanded from config aren't. + ${SSH} -F $OBJ/ssh_proxy -G '-oUser=${FOO}' somehost && \ diff -Nru openssh-10.0p1/debian/patches/CVE-2026-35387.patch openssh-10.0p1/debian/patches/CVE-2026-35387.patch --- openssh-10.0p1/debian/patches/CVE-2026-35387.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2026-35387.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,136 @@ +From 1f1029c07d64f668eccadb51b8e93ea250e9c62a Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Thu, 2 Apr 2026 07:48:13 +0000 +Subject: upstream: correctly match ECDSA signature algorithms against + +algorithm allowlists: HostKeyAlgorithms, PubkeyAcceptedAlgorithms and +HostbasedAcceptedAlgorithms. + +Previously, if any ECDSA type (say "ecdsa-sha2-nistp521") was +present in one of these lists, then all ECDSA algorithms would +be permitted. + +Reported by Christos Papakonstantinou of Cantina and Spearbit. + +OpenBSD-Commit-ID: c790e2687c35989ae34a00e709be935c55b16a86 + +[cjwatson: Committed upstream together with apparently-unrelated changes +for CVE-2026-35414. I've split them into separate patches for clarity.] + +Origin: backport, https://anongit.mindrot.org/openssh.git/commit/?id=fd1c7e131f331942d20f42f31e79912d570081fa +Bug-Debian: https://bugs.debian.org/1132574 +Last-Update: 2026-05-01 + +Patch-Name: CVE-2026-35387.patch +--- + auth2-hostbased.c | 7 ++++--- + auth2-pubkey.c | 7 ++++--- + sshconnect2.c | 26 +++++++++++++++++--------- + 3 files changed, 25 insertions(+), 15 deletions(-) + +diff --git a/auth2-hostbased.c b/auth2-hostbased.c +index eb21479a0..742bda567 100644 +--- a/auth2-hostbased.c ++++ b/auth2-hostbased.c +@@ -96,9 +96,10 @@ userauth_hostbased(struct ssh *ssh, const char *method) + error_f("cannot decode key: %s", pkalg); + goto done; + } +- if (key->type != pktype) { +- error_f("type mismatch for decoded key " +- "(received %d, expected %d)", key->type, pktype); ++ if (key->type != pktype || (sshkey_type_plain(pktype) == KEY_ECDSA && ++ sshkey_ecdsa_nid_from_name(pkalg) != key->ecdsa_nid)) { ++ error_f("key type mismatch for decoded key " ++ "(received %s, expected %s)", sshkey_ssh_name(key), pkalg); + goto done; + } + if (match_pattern_list(pkalg, options.hostbased_accepted_algos, 0) != 1) { +diff --git a/auth2-pubkey.c b/auth2-pubkey.c +index aa24fda05..60cf30c17 100644 +--- a/auth2-pubkey.c ++++ b/auth2-pubkey.c +@@ -154,9 +154,10 @@ userauth_pubkey(struct ssh *ssh, const char *method) + error_f("cannot decode key: %s", pkalg); + goto done; + } +- if (key->type != pktype) { +- error_f("type mismatch for decoded key " +- "(received %d, expected %d)", key->type, pktype); ++ if (key->type != pktype || (sshkey_type_plain(pktype) == KEY_ECDSA && ++ sshkey_ecdsa_nid_from_name(pkalg) != key->ecdsa_nid)) { ++ error_f("key type mismatch for decoded key " ++ "(received %s, expected %s)", sshkey_ssh_name(key), pkalg); + goto done; + } + if (auth2_key_already_used(authctxt, key)) { +diff --git a/sshconnect2.c b/sshconnect2.c +index 99ca84292..fa2625983 100644 +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -89,6 +89,7 @@ extern Options options; + static char *xxx_host; + static struct sockaddr *xxx_hostaddr; + static const struct ssh_conn_info *xxx_conn_info; ++static int key_type_allowed(struct sshkey *, const char *); + + static int + verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh) +@@ -98,6 +99,10 @@ verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh) + if ((r = sshkey_check_rsa_length(hostkey, + options.required_rsa_size)) != 0) + fatal_r(r, "Bad server host key"); ++ 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, + xxx_conn_info) != 0) + fatal("Host key verification failed."); +@@ -1738,34 +1743,37 @@ 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_accepted_algos, 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_accepted_algos, 0) == 1) ++ if (match_pattern_list("rsa-sha2-512", allowlist, 0) == 1) + return 1; +- if (match_pattern_list("rsa-sha2-256", +- options.pubkey_accepted_algos, 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_accepted_algos, 0) == 1) ++ allowlist, 0) == 1) + return 1; + if (match_pattern_list("rsa-sha2-256-cert-v01@openssh.com", +- options.pubkey_accepted_algos, 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_accepted_algos); ++} ++ + /* obtain a list of keys from the agent */ + static int + get_agent_identities(struct ssh *ssh, int *agent_fdp, diff -Nru openssh-10.0p1/debian/patches/CVE-2026-35388.patch openssh-10.0p1/debian/patches/CVE-2026-35388.patch --- openssh-10.0p1/debian/patches/CVE-2026-35388.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2026-35388.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,40 @@ +From 15b6d7bb9b5e81f4de1f7ff824066ff3f910e6c5 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Thu, 2 Apr 2026 07:39:57 +0000 +Subject: upstream: add missing askpass check when using + +ControlMaster=ask/autoask and "ssh -O proxy ..."; reported by Michalis +Vasileiadis + +OpenBSD-Commit-ID: 8dd7b9b96534e9a8726916b96d36bed466d3836a + +Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=c805b97b67c774e0bf922ffb29dfbcda9d7b5add +Bug-Debian: https://bugs.debian.org/1132575 +Last-Update: 2026-05-01 + +Patch-Name: CVE-2026-35388.patch +--- + mux.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/mux.c b/mux.c +index 415024f74..b48e69a12 100644 +--- a/mux.c ++++ b/mux.c +@@ -1136,6 +1136,16 @@ mux_master_process_proxy(struct ssh *ssh, u_int rid, + + debug_f("channel %d: proxy request", c->self); + ++ if (options.control_master == SSHCTL_MASTER_ASK || ++ options.control_master == SSHCTL_MASTER_AUTO_ASK) { ++ if (!ask_permission("Allow multiplex proxy connection?")) { ++ debug2_f("proxy refused by user"); ++ 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) diff -Nru openssh-10.0p1/debian/patches/CVE-2026-35414.patch openssh-10.0p1/debian/patches/CVE-2026-35414.patch --- openssh-10.0p1/debian/patches/CVE-2026-35414.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/CVE-2026-35414.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,79 @@ +From 8e9de40e4bec5c7ccf48c72f7b51d52aeb0d8df9 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Thu, 2 Apr 2026 07:48:13 +0000 +Subject: sshd(8): fix inappropriate matching of authorized_keys principals + +When matching an authorized_keys principals="" option against a list of +principals in a certificate, an incorrect algorithm was used that could +allow inappropriate matching in cases where a principal name in the +certificate contains a comma character. Exploitation of the condition +requires an authorized_keys principals="" option that lists more than +one principal *and* a CA that will issue a certificate that encodes more +than one of these principal names separated by a comma (typical CAs +strongly constrain which principal names they will place in a +certificate). This condition only applies to user- trusted CA keys in +authorized_keys, the main certificate authentication path +(TrustedUserCAKeys/AuthorizedPrincipalsFile) is not affected. + +Reported by Vladimir Tokarev. + +OpenBSD-Commit-ID: c790e2687c35989ae34a00e709be935c55b16a86 + +[cjwatson: Committed upstream together with apparently-unrelated changes +for CVE-2026-35387. I've split them into separate patches for clarity.] + +Origin: backport, https://anongit.mindrot.org/openssh.git/commit/?id=fd1c7e131f331942d20f42f31e79912d570081fa +Bug-Debian: https://bugs.debian.org/1132576 +Last-Update: 2026-05-01 + +Patch-Name: CVE-2026-35414.patch +--- + auth2-pubkeyfile.c | 24 ++++++++++++++---------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +diff --git a/auth2-pubkeyfile.c b/auth2-pubkeyfile.c +index 31e7481fb..827b71ed2 100644 +--- a/auth2-pubkeyfile.c ++++ b/auth2-pubkeyfile.c +@@ -50,6 +50,7 @@ + #include "authfile.h" + #include "match.h" + #include "ssherr.h" ++#include "xmalloc.h" + + int + auth_authorise_keyopts(struct passwd *pw, struct sshauthopt *opts, +@@ -146,20 +147,23 @@ auth_authorise_keyopts(struct passwd *pw, struct sshauthopt *opts, + 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; + } + diff -Nru openssh-10.0p1/debian/patches/configure-cache-vars.patch openssh-10.0p1/debian/patches/configure-cache-vars.patch --- openssh-10.0p1/debian/patches/configure-cache-vars.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/configure-cache-vars.patch 2026-05-05 10:25:39.000000000 +0000 @@ -1,4 +1,4 @@ -From 632c556fc44085e0cf62c92fbea312bc2ff01700 Mon Sep 17 00:00:00 2001 +From ab502cbb63f4665ed3963c9b2015468eeb3b0274 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Wed, 3 Apr 2024 11:52:04 +0100 Subject: Add Autoconf cache variables for OSSH_CHECK_*FLAG_* diff -Nru openssh-10.0p1/debian/patches/fix-max-startups-tracking.patch openssh-10.0p1/debian/patches/fix-max-startups-tracking.patch --- openssh-10.0p1/debian/patches/fix-max-startups-tracking.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/fix-max-startups-tracking.patch 2026-05-05 10:25:39.000000000 +0000 @@ -1,4 +1,4 @@ -From 947d15f4b44cf7d4ce337c82ed7e1a167a4f4dc2 Mon Sep 17 00:00:00 2001 +From 26128e0a946eff65c311f8c00f2449e1efb7fdff Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Fri, 4 Jul 2025 09:51:01 +0000 Subject: upstream: Fix mistracking of MaxStartups process exits in some diff -Nru openssh-10.0p1/debian/patches/ipqos-deprecate-tos-keywords.patch openssh-10.0p1/debian/patches/ipqos-deprecate-tos-keywords.patch --- openssh-10.0p1/debian/patches/ipqos-deprecate-tos-keywords.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/ipqos-deprecate-tos-keywords.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,175 @@ +From e93be079c0c564cf17acc18a7a6352c0f75e2bf0 Mon Sep 17 00:00:00 2001 +From: "job@openbsd.org" +Date: Thu, 31 Jul 2025 11:23:39 +0000 +Subject: upstream: Deprecate support for IPv4 type-of-service (TOS) IPQoS + +keywords + +Type of Service (ToS) was deprecated in the late nineties and replaced +with the Differentiated Services architecture. Diffserv has significant +advantages for operators because this mechanism offers more granularity. + +OpenSSH switched its default IPQoS from ToS to DSCP values in 2018. + +IPQoS configurations with 'lowdelay', 'reliability', or 'throughput' will be +ignored and instead the system default QoS settings apply. Additionally, a +debug message is logged about the deprecation with a suggestion to use DSCP. + +with/OK deraadt@ sthen@ djm@ + +OpenBSD-Commit-ID: 40c8c0c5cb20151a348728703536af2ec1c754ba + +Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=ec3465f59c651405e395092f3ad606f8992328d8 +Last-Update: 2026-05-03 + +Patch-Name: ipqos-deprecate-tos-keywords.patch +--- + misc.c | 6 +++--- + readconf.c | 12 ++++++++++++ + readconf.h | 4 ++-- + servconf.c | 12 ++++++++++++ + ssh_config.5 | 7 +++---- + sshd_config.5 | 7 +++---- + 6 files changed, 35 insertions(+), 13 deletions(-) + +diff --git a/misc.c b/misc.c +index 3b02281bb..3ff464573 100644 +--- a/misc.c ++++ b/misc.c +@@ -1935,9 +1935,9 @@ static const struct { + { "cs7", IPTOS_DSCP_CS7 }, + { "ef", IPTOS_DSCP_EF }, + { "le", IPTOS_DSCP_LE }, +- { "lowdelay", IPTOS_LOWDELAY }, +- { "throughput", IPTOS_THROUGHPUT }, +- { "reliability", IPTOS_RELIABILITY }, ++ { "lowdelay", INT_MIN }, /* deprecated */ ++ { "throughput", INT_MIN }, /* deprecated */ ++ { "reliability", INT_MIN }, /* deprecated */ + { NULL, -1 } + }; + +diff --git a/readconf.c b/readconf.c +index 796c16f23..2d1551420 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -2212,6 +2212,12 @@ parse_pubkey_algos: + filename, linenum, arg); + goto out; + } ++ if (value == INT_MIN) { ++ debug("%s line %d: Deprecated IPQoS value \"%s\" " ++ "ignored - using system default instead. Consider" ++ " using DSCP values.", filename, linenum, arg); ++ value = INT_MAX; ++ } + arg = argv_next(&ac, &av); + if (arg == NULL) + value2 = value; +@@ -2220,6 +2226,12 @@ parse_pubkey_algos: + filename, linenum, arg); + goto out; + } ++ if (value2 == INT_MIN) { ++ debug("%s line %d: Deprecated IPQoS value \"%s\" " ++ "ignored - using system default instead. Consider" ++ " using DSCP values.", filename, linenum, arg); ++ value2 = INT_MAX; ++ } + if (*activep && options->ip_qos_interactive == -1) { + options->ip_qos_interactive = value; + options->ip_qos_bulk = value2; +diff --git a/readconf.h b/readconf.h +index 58a44c2e8..c76960535 100644 +--- a/readconf.h ++++ b/readconf.h +@@ -55,8 +55,8 @@ typedef struct { + int strict_host_key_checking; /* Strict host key checking. */ + int compression; /* Compress packets in both directions. */ + int tcp_keep_alive; /* Set SO_KEEPALIVE. */ +- int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */ +- int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */ ++ int ip_qos_interactive; /* DSCP value for interactive */ ++ int ip_qos_bulk; /* DSCP value for bulk traffic */ + SyslogFacility log_facility; /* Facility for system logging. */ + LogLevel log_level; /* Level for logging. */ + u_int num_log_verbose; /* Verbose log overrides */ +diff --git a/servconf.c b/servconf.c +index 490ef074d..64caf0bab 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -2567,12 +2567,24 @@ process_server_config_line_depth(ServerOptions *options, char *line, + if ((value = parse_ipqos(arg)) == -1) + fatal("%s line %d: Bad %s value: %s", + filename, linenum, keyword, arg); ++ if (value == INT_MIN) { ++ debug("%s line %d: Deprecated IPQoS value \"%s\" " ++ "ignored - using system default instead. Consider" ++ " using DSCP values.", filename, linenum, arg); ++ value = INT_MAX; ++ } + arg = argv_next(&ac, &av); + if (arg == NULL) + value2 = value; + else if ((value2 = parse_ipqos(arg)) == -1) + fatal("%s line %d: Bad %s value: %s", + filename, linenum, keyword, arg); ++ if (value2 == INT_MIN) { ++ debug("%s line %d: Deprecated IPQoS value \"%s\" " ++ "ignored - using system default instead. Consider" ++ " using DSCP values.", filename, linenum, arg); ++ value2 = INT_MAX; ++ } + if (*activep) { + options->ip_qos_interactive = value; + options->ip_qos_bulk = value2; +diff --git a/ssh_config.5 b/ssh_config.5 +index 75c85ce48..2ee104bf8 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -1332,7 +1332,9 @@ or + block + to perform conditional inclusion. + .It Cm IPQoS +-Specifies the IPv4 type-of-service or DSCP class for connections. ++Specifies the ++.Em Differentiated Services Field Codepoint Pq DSCP ++value for connections. + Accepted values are + .Cm af11 , + .Cm af12 , +@@ -1356,9 +1358,6 @@ Accepted values are + .Cm cs7 , + .Cm ef , + .Cm le , +-.Cm lowdelay , +-.Cm throughput , +-.Cm reliability , + a numeric value, or + .Cm none + to use the operating system default. +diff --git a/sshd_config.5 b/sshd_config.5 +index a1afb4f89..8f51cd886 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -987,7 +987,9 @@ directive may appear inside a + block + to perform conditional inclusion. + .It Cm IPQoS +-Specifies the IPv4 type-of-service or DSCP class for the connection. ++Specifies the ++.Em Differentiated Services Field Codepoint Pq DSCP ++value for the connection. + Accepted values are + .Cm af11 , + .Cm af12 , +@@ -1011,9 +1013,6 @@ Accepted values are + .Cm cs7 , + .Cm ef , + .Cm le , +-.Cm lowdelay , +-.Cm throughput , +-.Cm reliability , + a numeric value, or + .Cm none + to use the operating system default. diff -Nru openssh-10.0p1/debian/patches/ipqos-interactive-ef.patch openssh-10.0p1/debian/patches/ipqos-interactive-ef.patch --- openssh-10.0p1/debian/patches/ipqos-interactive-ef.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/ipqos-interactive-ef.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,86 @@ +From 2a343893126e8fb4a6504c94a012e3a6e1dc4ac0 Mon Sep 17 00:00:00 2001 +From: "job@openbsd.org" +Date: Thu, 31 Jul 2025 09:38:41 +0000 +Subject: upstream: Set default IPQoS for interactive sessions to Expedited + +Forwarding (EF) + +Marking interactive session data with DSCP value EF (RFC3246, RFC3247) +helps inform the network on relative priority compared to other traffic. +This is especially useful for differentiated treatment over wireless media. + +Following the reconciled IETF Diffserv to IEEE 802.11 mappings (RFC 8325), +traffic marked with DSCP value EF maps to User Priority 6 in QoS Control, +in turn mapping to the high priority WMM AC_VO access category. + +OK djm@ + +OpenBSD-Commit-ID: aadda7b9da794d70d7c6b381a861a0610afce1b3 + +Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=65909fa114e7dd7511800db2b7bacb8774afe887 +Last-Update: 2026-05-03 + +Patch-Name: ipqos-interactive-ef.patch +--- + readconf.c | 2 +- + servconf.c | 2 +- + ssh_config.5 | 4 ++-- + sshd_config.5 | 4 ++-- + 4 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/readconf.c b/readconf.c +index 3bb7ac249..796c16f23 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -3009,7 +3009,7 @@ fill_default_options(Options * options) + if (options->visual_host_key == -1) + options->visual_host_key = 0; + if (options->ip_qos_interactive == -1) +- options->ip_qos_interactive = IPTOS_DSCP_AF21; ++ options->ip_qos_interactive = IPTOS_DSCP_EF; + if (options->ip_qos_bulk == -1) + options->ip_qos_bulk = IPTOS_DSCP_CS1; + if (options->request_tty == -1) +diff --git a/servconf.c b/servconf.c +index 4891a43d6..490ef074d 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -485,7 +485,7 @@ fill_default_server_options(ServerOptions *options) + if (options->permit_tun == -1) + options->permit_tun = SSH_TUNMODE_NO; + if (options->ip_qos_interactive == -1) +- options->ip_qos_interactive = IPTOS_DSCP_AF21; ++ options->ip_qos_interactive = IPTOS_DSCP_EF; + if (options->ip_qos_bulk == -1) + options->ip_qos_bulk = IPTOS_DSCP_CS1; + if (options->version_addendum == NULL) +diff --git a/ssh_config.5 b/ssh_config.5 +index d8452237d..75c85ce48 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -1367,8 +1367,8 @@ If one argument is specified, it is used as the packet class unconditionally. + If two values are specified, the first is automatically selected for + interactive sessions and the second for non-interactive sessions. + The default is +-.Cm af21 +-(Low-Latency Data) ++.Cm ef ++(Expedited Forwarding) + for interactive sessions and + .Cm cs1 + (Lower Effort) +diff --git a/sshd_config.5 b/sshd_config.5 +index a5594102f..a1afb4f89 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -1022,8 +1022,8 @@ If one argument is specified, it is used as the packet class unconditionally. + If two values are specified, the first is automatically selected for + interactive sessions and the second for non-interactive sessions. + The default is +-.Cm af21 +-(Low-Latency Data) ++.Cm ef ++(Expedited Forwarding) + for interactive sessions and + .Cm cs1 + (Lower Effort) diff -Nru openssh-10.0p1/debian/patches/ipqos-set-at-runtime.patch openssh-10.0p1/debian/patches/ipqos-set-at-runtime.patch --- openssh-10.0p1/debian/patches/ipqos-set-at-runtime.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/ipqos-set-at-runtime.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,595 @@ +From 62e161b58f41afdf8d3bdca5ec2a84c348775169 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Mon, 18 Aug 2025 03:43:01 +0000 +Subject: upstream: Make ssh(1) and sshd(8) set IP QoS (aka IP_TOS, + IPV6_TCLASS) + +continually at runtime based on what sessions/channels are open. + +Previously, ssh(1) and sshd(8) would pick a QoS value when they +were started and use it for the whole connection. This could +produce suboptimal choices for the QoS value, e.g. for multiplexed +sessions that started interactive but picked up a sftp client, +or sessions that moved large amounts of data via port forwarding. + +Now the QoS value will change to the non-interactive IPQoS whenever +a "non-interactive" channel is open; basically any channel that lacks +a tty other than agent forwarding. + +This is important now that the default interactive IPQoS is EF +(Expedited Forwarding), as many networks are configured to allow +only relatively small amounts of traffic of this class and they will +aggressively deprioritise the entire connection if this is exceeded. + +NB. because ssh(1) and sshd(8) now change IP_TOS/IPV6_TCLASS +continually via setsockopt(), this commit requires a recent pledge(2) +change that landed recently in the OpenBSD kernel. Please ensure +you have updated to a kernel from within the last two weeks before +updating OpenSSH. + +with job@ deraadt@ + +OpenBSD-Commit-ID: 325fc41717eecdf5e4b534bfa8d66817425b840f + +Origin: backport, https://anongit.mindrot.org/openssh.git/commit/?id=289239046b2c4b0076c14394ae9703a879e78706 +Last-Update: 2026-05-03 + +Patch-Name: ipqos-set-at-runtime.patch +--- + channels.c | 44 ++++++++++++++++++++++++++++--- + channels.h | 7 +++++ + clientloop.c | 11 +++++--- + misc.c | 4 +++ + mux.c | 2 ++ + packet.c | 71 ++++++++++++++++++++++++++++++++++---------------- + packet.h | 3 ++- + serverloop.c | 7 +++++ + session.c | 5 ---- + ssh.c | 25 +++++------------- + sshd-auth.c | 2 ++ + sshd-session.c | 2 ++ + 12 files changed, 130 insertions(+), 53 deletions(-) + +diff --git a/channels.c b/channels.c +index bfe2e3b2d..1a8f8ad4a 100644 +--- a/channels.c ++++ b/channels.c +@@ -212,6 +212,10 @@ struct ssh_channels { + /* Global timeout for all OPEN channels */ + int global_deadline; + time_t lastused; ++ /* pattern-lists used to classify channels as bulk */ ++ char *bulk_classifier_tty, *bulk_classifier_notty; ++ /* Number of active bulk channels (set by channel_handler) */ ++ u_int nbulk; + }; + + /* helper */ +@@ -239,6 +243,8 @@ channel_init_channels(struct ssh *ssh) + sc->channels_alloc = 10; + sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); + sc->IPv4or6 = AF_UNSPEC; ++ sc->bulk_classifier_tty = xstrdup(CHANNEL_BULK_TTY); ++ sc->bulk_classifier_notty = xstrdup(CHANNEL_BULK_NOTTY); + channel_handler_init(sc); + + ssh->chanctxt = sc; +@@ -357,6 +363,17 @@ lookup_timeout(struct ssh *ssh, const char *type) + return 0; + } + ++static void ++channel_classify(struct ssh *ssh, Channel *c) ++{ ++ struct ssh_channels *sc = ssh->chanctxt; ++ const char *type = c->xctype == NULL ? c->ctype : c->xctype; ++ const char *classifier = c->isatty ? ++ sc->bulk_classifier_tty : sc->bulk_classifier_notty; ++ ++ c->bulk = type != NULL && match_pattern_list(type, classifier, 0) == 1; ++} ++ + /* + * Sets "extended type" of a channel; used by session layer to add additional + * information about channel types (e.g. shell, login, subsystem) that can then +@@ -375,6 +392,7 @@ channel_set_xtype(struct ssh *ssh, int id, const char *xctype) + c->xctype = xstrdup(xctype); + /* Type has changed, so look up inactivity deadline again */ + c->inactive_deadline = lookup_timeout(ssh, c->xctype); ++ channel_classify(ssh, c); + debug2_f("labeled channel %d as %s (inactive timeout %u)", id, xctype, + c->inactive_deadline); + } +@@ -411,6 +429,13 @@ channel_get_expiry(struct ssh *ssh, Channel *c) + return expiry; + } + ++/* Returns non-zero if there is an open, non-interactive channel */ ++int ++channel_has_bulk(struct ssh *ssh) ++{ ++ return ssh->chanctxt != NULL && ssh->chanctxt->nbulk != 0; ++} ++ + /* + * Register filedescriptors for a channel, used when allocating a channel or + * when the channel consumer/producer is ready, e.g. shell exec'd +@@ -478,6 +503,7 @@ channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, + } + /* channel might be entering a larval state, so reset global timeout */ + channel_set_used_time(ssh, NULL); ++ channel_classify(ssh, c); + } + + /* +@@ -537,11 +563,19 @@ channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd, + c->delayed = 1; /* prevent call to channel_post handler */ + c->inactive_deadline = lookup_timeout(ssh, c->ctype); + TAILQ_INIT(&c->status_confirms); ++ channel_classify(ssh, c); + debug("channel %d: new %s [%s] (inactive timeout: %u)", + found, c->ctype, remote_name, c->inactive_deadline); + return c; + } + ++void ++channel_set_tty(struct ssh *ssh, Channel *c) ++{ ++ c->isatty = 1; ++ channel_classify(ssh, c); ++} ++ + int + channel_close_fd(struct ssh *ssh, Channel *c, int *fdp) + { +@@ -1019,7 +1053,7 @@ channel_format_status(const Channel *c) + char *ret = NULL; + + xasprintf(&ret, "t%d [%s] %s%u %s%u i%u/%zu o%u/%zu e[%s]/%zu " +- "fd %d/%d/%d sock %d cc %d %s%u io 0x%02x/0x%02x", ++ "fd %d/%d/%d sock %d cc %d %s%u io 0x%02x/0x%02x %s%s", + c->type, c->xctype != NULL ? c->xctype : c->ctype, + c->have_remote_id ? "r" : "nr", c->remote_id, + c->mux_ctx != NULL ? "m" : "nm", c->mux_downstream_id, +@@ -1028,7 +1062,8 @@ channel_format_status(const Channel *c) + channel_format_extended_usage(c), sshbuf_len(c->extended), + c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan, + c->have_ctl_child_id ? "c" : "nc", c->ctl_child_id, +- c->io_want, c->io_ready); ++ c->io_want, c->io_ready, ++ c->isatty ? "T" : "", c->bulk ? "B" : "I"); + return ret; + } + +@@ -2606,10 +2641,13 @@ channel_handler(struct ssh *ssh, int table, struct timespec *timeout) + time_t now; + + now = monotime(); +- for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { ++ for (sc->nbulk = i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { + c = sc->channels[i]; + if (c == NULL) + continue; ++ /* Count open channels in bulk state */ ++ if (c->type == SSH_CHANNEL_OPEN && c->bulk) ++ sc->nbulk++; + /* Try to keep IO going while rekeying */ + if (ssh_packet_is_rekeying(ssh) && c->type != SSH_CHANNEL_OPEN) + continue; +diff --git a/channels.h b/channels.h +index 134528d59..a84c9dfdd 100644 +--- a/channels.h ++++ b/channels.h +@@ -82,6 +82,10 @@ + #define FORWARD_ADM 0x100 + #define FORWARD_USER 0x101 + ++/* default pattern-lists used to classify channel types as bulk */ ++#define CHANNEL_BULK_TTY "" ++#define CHANNEL_BULK_NOTTY "direct-*,forwarded-*,tun-*,x11-*,session*" ++ + struct ssh; + struct Channel; + typedef struct Channel Channel; +@@ -180,6 +184,7 @@ struct Channel { + + char *ctype; /* const type - NB. not freed on channel_free */ + char *xctype; /* extended type */ ++ int bulk; /* channel is non-interactive */ + + /* callback */ + channel_open_fn *open_confirm; +@@ -289,6 +294,7 @@ Channel *channel_new(struct ssh *, char *, int, int, int, int, + u_int, u_int, int, const char *, int); + void channel_set_fds(struct ssh *, int, int, int, int, int, + int, int, u_int); ++void channel_set_tty(struct ssh *, Channel *); + void channel_free(struct ssh *, Channel *); + void channel_free_all(struct ssh *); + void channel_stop_listening(struct ssh *); +@@ -308,6 +314,7 @@ void channel_register_status_confirm(struct ssh *, int, + void channel_cancel_cleanup(struct ssh *, int); + int channel_close_fd(struct ssh *, Channel *, int *); + void channel_send_window_changes(struct ssh *); ++int channel_has_bulk(struct ssh *); + + /* channel inactivity timeouts */ + void channel_add_timeout(struct ssh *, const char *, int); +diff --git a/clientloop.c b/clientloop.c +index 407b76b4c..34a94fdb5 100644 +--- a/clientloop.c ++++ b/clientloop.c +@@ -1451,7 +1451,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, + struct pollfd *pfd = NULL; + u_int npfd_alloc = 0, npfd_active = 0; + double start_time, total_time; +- int channel_did_enqueue = 0, r; ++ int interactive = -1, channel_did_enqueue = 0, r; + u_int64_t ibytes, obytes; + int conn_in_ready, conn_out_ready; + sigset_t bsigset, osigset; +@@ -1620,6 +1620,12 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, + * sender. + */ + if (conn_out_ready) { ++ if (interactive != !channel_has_bulk(ssh)) { ++ interactive = !channel_has_bulk(ssh); ++ debug2_f("session QoS is now %s", interactive ? ++ "interactive" : "non-interactive"); ++ ssh_packet_set_interactive(ssh, interactive); ++ } + if ((r = ssh_packet_write_poll(ssh)) != 0) { + sshpkt_fatal(ssh, r, + "%s: ssh_packet_write_poll", __func__); +@@ -2704,9 +2710,6 @@ client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, + if ((c = channel_lookup(ssh, id)) == NULL) + fatal_f("channel %d: unknown channel", id); + +- ssh_packet_set_interactive(ssh, want_tty, +- options.ip_qos_interactive, options.ip_qos_bulk); +- + if (want_tty) { + struct winsize ws; + +diff --git a/misc.c b/misc.c +index 3ff464573..28c3f150d 100644 +--- a/misc.c ++++ b/misc.c +@@ -297,6 +297,10 @@ set_sock_tos(int fd, int tos) + #ifndef IP_TOS_IS_BROKEN + int af; + ++ if (tos < 0 || tos == INT_MAX) { ++ debug_f("invalid TOS %d", tos); ++ return; ++ } + switch ((af = get_sock_af(fd))) { + case -1: + /* assume not a socket */ +diff --git a/mux.c b/mux.c +index b48e69a12..c5eaf1007 100644 +--- a/mux.c ++++ b/mux.c +@@ -460,6 +460,8 @@ mux_master_process_new_session(struct ssh *ssh, u_int rid, + nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING, + new_fd[0], new_fd[1], new_fd[2], window, packetmax, + CHAN_EXTENDED_WRITE, "client-session", CHANNEL_NONBLOCK_STDIO); ++ if (cctx->want_tty) ++ channel_set_tty(ssh, nc); + + nc->ctl_chan = c->self; /* link session -> control channel */ + c->ctl_child_id = nc->self; /* link control -> session channel */ +diff --git a/packet.c b/packet.c +index 9dea2cfc5..51135847a 100644 +--- a/packet.c ++++ b/packet.c +@@ -210,8 +210,8 @@ struct session_state { + /* Used in ssh_packet_send_mux() */ + int mux; + +- /* Used in packet_set_interactive */ +- int set_interactive_called; ++ /* QoS handling */ ++ int qos_interactive, qos_other; + + /* Used in packet_set_maxsize */ + int set_maxsize_called; +@@ -219,6 +219,9 @@ struct session_state { + /* One-off warning about weak ciphers */ + int cipher_warning_done; + ++ /* Nagle disabled on socket */ ++ int nodelay_set; ++ + /* Hook for fuzzing inbound packets */ + ssh_packet_hook_fn *hook_in; + void *hook_in_ctx; +@@ -247,6 +250,8 @@ ssh_alloc_session_state(void) + state->connection_out = -1; + state->max_packet_size = 32768; + state->packet_timeout_ms = -1; ++ state->interactive_mode = 1; ++ state->qos_interactive = state->qos_other = -1; + state->p_send.packets = state->p_read.packets = 0; + state->initialized = 1; + /* +@@ -2206,37 +2211,44 @@ ssh_packet_interactive_data_to_write(struct ssh *ssh) + sshbuf_len(ssh->state->output) < 256; + } + +-void +-ssh_packet_set_tos(struct ssh *ssh, int tos) ++static void ++apply_qos(struct ssh *ssh) + { +- if (!ssh_packet_connection_is_on_socket(ssh) || tos == INT_MAX) ++ struct session_state *state = ssh->state; ++ int qos = state->interactive_mode ? ++ state->qos_interactive : state->qos_other; ++ ++ if (!ssh_packet_connection_is_on_socket(ssh)) + return; +- set_sock_tos(ssh->state->connection_in, tos); ++ if (!state->nodelay_set) { ++ set_nodelay(state->connection_in); ++ state->nodelay_set = 1; ++ } ++ set_sock_tos(ssh->state->connection_in, qos); + } + +-/* Informs that the current session is interactive. Sets IP flags for that. */ +- ++/* Informs that the current session is interactive. */ + void +-ssh_packet_set_interactive(struct ssh *ssh, int interactive, int qos_interactive, int qos_bulk) ++ssh_packet_set_interactive(struct ssh *ssh, int interactive) + { + struct session_state *state = ssh->state; + +- if (state->set_interactive_called) +- return; +- state->set_interactive_called = 1; +- +- /* Record that we are in interactive mode. */ + state->interactive_mode = interactive; ++ apply_qos(ssh); ++} + +- /* Only set socket options if using a socket. */ +- if (!ssh_packet_connection_is_on_socket(ssh)) +- return; +- set_nodelay(state->connection_in); +- ssh_packet_set_tos(ssh, interactive ? qos_interactive : qos_bulk); ++/* Set QoS flags to be used for interactive and non-interactive sessions */ ++void ++ssh_packet_set_qos(struct ssh *ssh, int qos_interactive, int qos_other) ++{ ++ struct session_state *state = ssh->state; ++ ++ state->qos_interactive = qos_interactive; ++ state->qos_other = qos_other; ++ apply_qos(ssh); + } + + /* Returns true if the current connection is interactive. */ +- + int + ssh_packet_is_interactive(struct ssh *ssh) + { +@@ -2415,6 +2427,7 @@ ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m) + struct session_state *state = ssh->state; + int r; + ++#define ENCODE_INT(v) (((v) < 0) ? 0xFFFFFFFF : (u_int)v) + if ((r = kex_to_blob(m, ssh->kex)) != 0 || + (r = newkeys_to_blob(m, ssh, MODE_OUT)) != 0 || + (r = newkeys_to_blob(m, ssh, MODE_IN)) != 0 || +@@ -2429,9 +2442,12 @@ ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m) + (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 || ++ (r = sshbuf_put_u32(m, ENCODE_INT(state->interactive_mode))) != 0 || ++ (r = sshbuf_put_u32(m, ENCODE_INT(state->qos_interactive))) != 0 || ++ (r = sshbuf_put_u32(m, ENCODE_INT(state->qos_other))) != 0) + return r; +- ++#undef ENCODE_INT + return 0; + } + +@@ -2550,6 +2566,7 @@ ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m) + const u_char *input, *output; + size_t ilen, olen; + int r; ++ u_int interactive, qos_interactive, qos_other; + + if ((r = kex_from_blob(m, &ssh->kex)) != 0 || + (r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 || +@@ -2586,6 +2603,16 @@ ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m) + (r = sshbuf_put(state->output, output, olen)) != 0) + return r; + ++ if ((r = sshbuf_get_u32(m, &interactive)) != 0 || ++ (r = sshbuf_get_u32(m, &qos_interactive)) != 0 || ++ (r = sshbuf_get_u32(m, &qos_other)) != 0) ++ return r; ++#define DECODE_INT(v) ((v) > INT_MAX ? -1 : (v)) ++ state->interactive_mode = DECODE_INT(interactive); ++ state->qos_interactive = DECODE_INT(qos_interactive); ++ state->qos_other = DECODE_INT(qos_other); ++#undef DECODE_INT ++ + if (sshbuf_len(m)) + return SSH_ERR_INVALID_FORMAT; + debug3_f("done"); +diff --git a/packet.h b/packet.h +index 49bb87f07..85d292aa1 100644 +--- a/packet.h ++++ b/packet.h +@@ -111,8 +111,9 @@ int ssh_packet_check_rekey(struct ssh *); + void ssh_packet_set_protocol_flags(struct ssh *, u_int); + u_int ssh_packet_get_protocol_flags(struct ssh *); + void ssh_packet_set_tos(struct ssh *, int); +-void ssh_packet_set_interactive(struct ssh *, int, int, int); ++void ssh_packet_set_interactive(struct ssh *, int); + int ssh_packet_is_interactive(struct ssh *); ++void ssh_packet_set_qos(struct ssh *, int, int); + void ssh_packet_set_server(struct ssh *); + void ssh_packet_set_authenticated(struct ssh *); + void ssh_packet_set_mux(struct ssh *); +diff --git a/serverloop.c b/serverloop.c +index 40ddfb042..395a7e317 100644 +--- a/serverloop.c ++++ b/serverloop.c +@@ -285,8 +285,15 @@ static void + process_output(struct ssh *ssh, int connection_out) + { + int r; ++ static int interactive = -1; + + /* Send any buffered packet data to the client. */ ++ if (interactive != !channel_has_bulk(ssh)) { ++ interactive = !channel_has_bulk(ssh); ++ debug2_f("session QoS is now %s", interactive ? ++ "interactive" : "non-interactive"); ++ ssh_packet_set_interactive(ssh, interactive); ++ } + if ((r = ssh_packet_write_poll(ssh)) != 0) { + sshpkt_fatal(ssh, r, "%s: ssh_packet_write_poll", + __func__); +diff --git a/session.c b/session.c +index 6614d8560..dfa97ee1c 100644 +--- a/session.c ++++ b/session.c +@@ -524,9 +524,6 @@ do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) + #endif + + s->pid = pid; +- /* Set interactive/non-interactive mode. */ +- ssh_packet_set_interactive(ssh, s->display != NULL, +- options.ip_qos_interactive, options.ip_qos_bulk); + + /* + * Clear loginmsg, since it's the child's responsibility to display +@@ -654,8 +651,6 @@ do_exec_pty(struct ssh *ssh, Session *s, const char *command) + + /* Enter interactive session. */ + s->ptymaster = ptymaster; +- ssh_packet_set_interactive(ssh, 1, +- options.ip_qos_interactive, options.ip_qos_bulk); + session_set_fds(ssh, s, ptyfd, fdout, -1, 1, 1); + return 0; + } +diff --git a/ssh.c b/ssh.c +index e668fb771..ff60ae569 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -710,7 +710,6 @@ main(int ac, char **av) + fatal("Couldn't allocate session state"); + channel_init_channels(ssh); + +- + /* Parse command-line arguments. */ + args = argv_assemble(ac, av); /* logged later */ + host = NULL; +@@ -1344,6 +1343,8 @@ main(int ac, char **av) + if (options.port == 0) + options.port = default_ssh_port(); + channel_set_af(ssh, options.address_family); ++ ssh_packet_set_qos(ssh, options.ip_qos_interactive, ++ options.ip_qos_bulk); + + /* Tidy and check options */ + if (options.host_key_alias != NULL) +@@ -2168,7 +2169,7 @@ ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg) + { + extern char **environ; + const char *display, *term; +- int r, interactive = tty_flag; ++ int r; + char *proto = NULL, *data = NULL; + + if (!success) +@@ -2187,7 +2188,6 @@ ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg) + data, 1); + client_expect_confirm(ssh, id, "X11 forwarding", CONFIRM_WARN); + /* XXX exit_on_forward_failure */ +- interactive = 1; + } + + check_agent_present(); +@@ -2198,10 +2198,6 @@ ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg) + fatal_fr(r, "send packet"); + } + +- /* Tell the packet module whether this is an interactive session. */ +- ssh_packet_set_interactive(ssh, interactive, +- options.ip_qos_interactive, options.ip_qos_bulk); +- + if ((term = lookup_env_in_list("TERM", options.setenv, + options.num_setenv)) == NULL || *term == '\0') + term = getenv("TERM"); +@@ -2238,8 +2234,9 @@ ssh_session2_open(struct ssh *ssh) + "session", SSH_CHANNEL_OPENING, in, out, err, + window, packetmax, CHAN_EXTENDED_WRITE, + "client-session", CHANNEL_NONBLOCK_STDIO); +- +- debug3_f("channel_new: %d", c->self); ++ if (tty_flag) ++ channel_set_tty(ssh, c); ++ debug3_f("channel_new: %d%s", c->self, tty_flag ? " (tty)" : ""); + + channel_send_open(ssh, c->self); + if (options.session_type != SESSION_TYPE_NONE) +@@ -2252,7 +2249,7 @@ ssh_session2_open(struct ssh *ssh) + static int + ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo) + { +- int r, interactive, id = -1; ++ int r, id = -1; + char *cp, *tun_fwd_ifname = NULL; + + /* XXX should be pre-session */ +@@ -2308,14 +2305,6 @@ ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo) + + if (options.session_type != SESSION_TYPE_NONE) + id = ssh_session2_open(ssh); +- else { +- interactive = 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); +- } + + /* If we don't expect to open a new session, then disallow it */ + if (options.control_master == SSHCTL_MASTER_NO && +diff --git a/sshd-auth.c b/sshd-auth.c +index 362abc92c..371a4b795 100644 +--- a/sshd-auth.c ++++ b/sshd-auth.c +@@ -672,6 +672,8 @@ main(int ac, char **av) + /* Fill in default values for those options not explicitly set. */ + fill_default_server_options(&options); + options.timing_secret = timing_secret; /* XXX eliminate from unpriv */ ++ ssh_packet_set_qos(ssh, options.ip_qos_interactive, ++ options.ip_qos_bulk); + + /* Reinit logging in case config set Level, Facility or Verbose. */ + log_init(__progname, options.log_level, options.log_facility, 1); +diff --git a/sshd-session.c b/sshd-session.c +index 2b6d2a98b..989e21734 100644 +--- a/sshd-session.c ++++ b/sshd-session.c +@@ -1215,6 +1215,8 @@ main(int ac, char **av) + fatal("Unable to create connection"); + the_active_state = ssh; + ssh_packet_set_server(ssh); ++ ssh_packet_set_qos(ssh, options.ip_qos_interactive, ++ options.ip_qos_bulk); + + check_ip_options(ssh); + diff -Nru openssh-10.0p1/debian/patches/ipqos-set-extended-type.patch openssh-10.0p1/debian/patches/ipqos-set-extended-type.patch --- openssh-10.0p1/debian/patches/ipqos-set-extended-type.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssh-10.0p1/debian/patches/ipqos-set-extended-type.patch 2026-05-05 10:25:39.000000000 +0000 @@ -0,0 +1,68 @@ +From 4207d8a7a4060cad77ec1b78ff08f3e0546c4fbd Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Sun, 19 Apr 2026 23:37:22 +0000 +Subject: upstream: correctly set extended type for client-side channels. + +Fixes interactive vs bulk IPQoS for client->server traffic. ok job@ + +OpenBSD-Commit-ID: 34f5131face8d6dc4ae6955196e5fcafb3570cfe + +Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=45b30e0a5439a02417a4fe982a4b16a9c126ba6b +Last-Update: 2026-05-03 + +Patch-Name: ipqos-set-extended-type.patch +--- + clientloop.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/clientloop.c b/clientloop.c +index 34a94fdb5..5979d5803 100644 +--- a/clientloop.c ++++ b/clientloop.c +@@ -2702,7 +2702,7 @@ client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, + { + size_t i, j, len; + int matched, r; +- char *name, *val; ++ char *type = NULL, *cmdstring = NULL, *name, *val; + Channel *c = NULL; + + debug2_f("id %d", id); +@@ -2777,19 +2777,21 @@ client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, + + len = sshbuf_len(cmd); + if (len > 0) { ++ if ((cmdstring = sshbuf_dup_string(cmd)) == NULL) ++ fatal_f("sshbuf_dup_string failed"); + if (len > 900) + len = 900; + if (want_subsystem) { +- debug("Sending subsystem: %.*s", +- (int)len, (const u_char*)sshbuf_ptr(cmd)); ++ debug("Sending subsystem: %.*s", (int)len, cmdstring); + channel_request_start(ssh, id, "subsystem", 1); + client_expect_confirm(ssh, id, "subsystem", + CONFIRM_CLOSE); ++ xasprintf(&type, "session:subsystem:%s", cmdstring); + } else { +- debug("Sending command: %.*s", +- (int)len, (const u_char*)sshbuf_ptr(cmd)); ++ debug("Sending command: %.*s", (int)len, cmdstring); + channel_request_start(ssh, id, "exec", 1); + client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); ++ xasprintf(&type, "session:command"); + } + if ((r = sshpkt_put_stringb(ssh, cmd)) != 0 || + (r = sshpkt_send(ssh)) != 0) +@@ -2799,7 +2801,11 @@ client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, + client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send shell"); ++ xasprintf(&type, "session:shell"); + } ++ channel_set_xtype(ssh, id, type); ++ free(cmdstring); ++ free(type); + + session_setup_complete = 1; + client_repledge(); diff -Nru openssh-10.0p1/debian/patches/pam-avoid-unknown-host.patch openssh-10.0p1/debian/patches/pam-avoid-unknown-host.patch --- openssh-10.0p1/debian/patches/pam-avoid-unknown-host.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/pam-avoid-unknown-host.patch 2026-05-05 10:25:39.000000000 +0000 @@ -1,4 +1,4 @@ -From ccbb3efb1598cde11bb76d6045cd73c8f1773fd0 Mon Sep 17 00:00:00 2001 +From f9d483dc7e0e250e7e9f2117c129c768239827c3 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 20 Mar 2023 20:22:14 +0100 Subject: Only set PAM_RHOST if the remote host is not "UNKNOWN" diff -Nru openssh-10.0p1/debian/patches/regress-conch-dev-zero.patch openssh-10.0p1/debian/patches/regress-conch-dev-zero.patch --- openssh-10.0p1/debian/patches/regress-conch-dev-zero.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/regress-conch-dev-zero.patch 2026-05-05 10:25:39.000000000 +0000 @@ -1,4 +1,4 @@ -From 9db329d6764879915981e5ace3acd02534922b1d Mon Sep 17 00:00:00 2001 +From f5998f35b86e5927add370e915ae0ed0c2a4e8f6 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Sun, 31 Mar 2024 00:24:11 +0000 Subject: regress: Redirect conch stdin from /dev/zero diff -Nru openssh-10.0p1/debian/patches/revert-ipqos-defaults.patch openssh-10.0p1/debian/patches/revert-ipqos-defaults.patch --- openssh-10.0p1/debian/patches/revert-ipqos-defaults.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/revert-ipqos-defaults.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -From 88dc4a66e9c8fd350152080713f33e26fd7df202 Mon Sep 17 00:00:00 2001 -From: Colin Watson -Date: Mon, 8 Apr 2019 10:46:29 +0100 -Subject: Revert "upstream: Update default IPQoS in ssh(1), sshd(8) to DSCP - AF21 for" - -This reverts commit 5ee8448ad7c306f05a9f56769f95336a8269f379. - -The IPQoS default changes have some unfortunate interactions with -iptables (see https://bugs.debian.org/923880) and VMware, so I'm -temporarily reverting them until those have been fixed. - -Bug-Debian: https://bugs.debian.org/923879 -Bug-Debian: https://bugs.debian.org/926229 -Bug-Ubuntu: https://bugs.launchpad.net/bugs/1822370 -Last-Update: 2019-04-08 - -Patch-Name: revert-ipqos-defaults.patch ---- - readconf.c | 4 ++-- - servconf.c | 4 ++-- - ssh_config.5 | 6 ++---- - sshd_config.5 | 6 ++---- - 4 files changed, 8 insertions(+), 12 deletions(-) - -diff --git a/readconf.c b/readconf.c -index fc625a00c..09b8ca33c 100644 ---- a/readconf.c -+++ b/readconf.c -@@ -3012,9 +3012,9 @@ fill_default_options(Options * options) - if (options->visual_host_key == -1) - options->visual_host_key = 0; - if (options->ip_qos_interactive == -1) -- options->ip_qos_interactive = IPTOS_DSCP_AF21; -+ options->ip_qos_interactive = IPTOS_LOWDELAY; - if (options->ip_qos_bulk == -1) -- options->ip_qos_bulk = IPTOS_DSCP_CS1; -+ options->ip_qos_bulk = IPTOS_THROUGHPUT; - if (options->request_tty == -1) - options->request_tty = REQUEST_TTY_AUTO; - if (options->session_type == -1) -diff --git a/servconf.c b/servconf.c -index 4891a43d6..a51370a6d 100644 ---- a/servconf.c -+++ b/servconf.c -@@ -485,9 +485,9 @@ fill_default_server_options(ServerOptions *options) - if (options->permit_tun == -1) - options->permit_tun = SSH_TUNMODE_NO; - if (options->ip_qos_interactive == -1) -- options->ip_qos_interactive = IPTOS_DSCP_AF21; -+ options->ip_qos_interactive = IPTOS_LOWDELAY; - if (options->ip_qos_bulk == -1) -- options->ip_qos_bulk = IPTOS_DSCP_CS1; -+ options->ip_qos_bulk = IPTOS_THROUGHPUT; - if (options->version_addendum == NULL) - options->version_addendum = xstrdup(""); - if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) -diff --git a/ssh_config.5 b/ssh_config.5 -index d8452237d..df12ef118 100644 ---- a/ssh_config.5 -+++ b/ssh_config.5 -@@ -1367,11 +1367,9 @@ If one argument is specified, it is used as the packet class unconditionally. - If two values are specified, the first is automatically selected for - interactive sessions and the second for non-interactive sessions. - The default is --.Cm af21 --(Low-Latency Data) -+.Cm lowdelay - for interactive sessions and --.Cm cs1 --(Lower Effort) -+.Cm throughput - for non-interactive sessions. - .It Cm KbdInteractiveAuthentication - Specifies whether to use keyboard-interactive authentication. -diff --git a/sshd_config.5 b/sshd_config.5 -index a5594102f..998837edf 100644 ---- a/sshd_config.5 -+++ b/sshd_config.5 -@@ -1022,11 +1022,9 @@ If one argument is specified, it is used as the packet class unconditionally. - If two values are specified, the first is automatically selected for - interactive sessions and the second for non-interactive sessions. - The default is --.Cm af21 --(Low-Latency Data) -+.Cm lowdelay - for interactive sessions and --.Cm cs1 --(Lower Effort) -+.Cm throughput - for non-interactive sessions. - .It Cm KbdInteractiveAuthentication - Specifies whether to allow keyboard-interactive authentication. diff -Nru openssh-10.0p1/debian/patches/series openssh-10.0p1/debian/patches/series --- openssh-10.0p1/debian/patches/series 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/series 2026-05-05 10:25:39.000000000 +0000 @@ -20,7 +20,6 @@ gnome-ssh-askpass2-icon.patch debian-config.patch restore-authorized_keys2.patch -revert-ipqos-defaults.patch systemd-socket-activation.patch skip-utimensat-test-on-zfs.patch regress-conch-dev-zero.patch @@ -30,3 +29,14 @@ CVE-2025-61985.patch CVE-2025-61984-tests.patch fix-max-startups-tracking.patch +CVE-2026-35388.patch +CVE-2026-35385.patch +CVE-2026-35387.patch +CVE-2026-35414.patch +CVE-2026-35386-1.patch +CVE-2026-35386-2.patch +CVE-2026-35386-3.patch +ipqos-interactive-ef.patch +ipqos-deprecate-tos-keywords.patch +ipqos-set-at-runtime.patch +ipqos-set-extended-type.patch diff -Nru openssh-10.0p1/debian/patches/skip-utimensat-test-on-zfs.patch openssh-10.0p1/debian/patches/skip-utimensat-test-on-zfs.patch --- openssh-10.0p1/debian/patches/skip-utimensat-test-on-zfs.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/skip-utimensat-test-on-zfs.patch 2026-05-05 10:25:39.000000000 +0000 @@ -1,4 +1,4 @@ -From 3d83f47df49d9b38dd014ef87089b14b42060250 Mon Sep 17 00:00:00 2001 +From 0bed1d6ebd6374ee22c3e3e25b27b044f4e95ae2 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 11 Mar 2024 16:24:49 +0000 Subject: Skip utimensat test on ZFS diff -Nru openssh-10.0p1/debian/patches/systemd-socket-activation.patch openssh-10.0p1/debian/patches/systemd-socket-activation.patch --- openssh-10.0p1/debian/patches/systemd-socket-activation.patch 2026-04-04 23:27:07.000000000 +0000 +++ openssh-10.0p1/debian/patches/systemd-socket-activation.patch 2026-05-05 10:25:39.000000000 +0000 @@ -1,4 +1,4 @@ -From 7f825ab75842dd91ad2ac00acabc5ea0350c6794 Mon Sep 17 00:00:00 2001 +From 3785943259d3d91473571aea827e3b09ecc53a23 Mon Sep 17 00:00:00 2001 From: Steve Langasek Date: Thu, 1 Sep 2022 16:03:37 +0100 Subject: Support systemd socket activation