22 Commits
Author SHA1 Message Date
Nicola Murino 86efde54dc ssh: reject unexpected message types on established channels
ch.msg is only read while the channel open or a channel request with a
reply is pending, so anything the default arm of channel.handlePacket
delivered to it was never consumed. The blocking send there let a
misbehaving peer fill the buffer with well-formed but unexpected message
types carrying a valid channel id and stall the mux read loop,
deadlocking the whole connection.

No conforming peer sends such messages during the connection protocol.
Treat them as a protocol error and tear the connection down, as
handleUnknownChannelPacket already does for the same messages when the
channel id is not in use.

Fixes CVE-2026-56855
Fixes golang/go#81317

Change-Id: I87420dfe68fcb62a17df4b47dc5ffb6ccd72ba26
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/826524
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Neal Patel <nealpatel@google.com>
Reviewed-by: Nicholas Husin <husin@google.com>
2026-09-02 11:02:47 -07:00
Nicola Murino a6cdac6084 ssh: drop traffic on undecided channels
A channel in the mux's chanList is not usable until it is established:
an outbound channel has no confirmed remote id until the peer's open
confirmation, and an inbound channel is not serviced by the application
until it is accepted. handlePacket processed any channel message on it,
so a misbehaving peer could flood channel requests and block the mux
read loop on the send to incomingRequests, deadlocking the connection,
or close an outbound channel before confirming it, making the victim
tear down a half-initialized channel and emit a close for remote id 0,
an unrelated channel of the peer.

No such packet can be legitimate: the peer learns an inbound channel's
local id only from the confirmation we have not sent yet, and on an
outbound channel RFC 4254 lets it answer the open request only with a
confirmation or a failure.

Add an established flag, set when the channel becomes usable: for an
outbound channel when the open response is received, for an inbound
channel by Accept before the confirmation is sent. Until then
handlePacket drops every packet other than the open response. The flag
is separate from decided, which Reject also sets: a rejected channel is
decided but must never carry traffic.

Fixes CVE-2026-78662
Fixes golang/go#81316

Change-Id: Ib0983bb216a49808a2db1f4a4d92ee9fe38a3c51
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/826504
Auto-Submit: Gopher Robot <gobot@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Neal Patel <nealpatel@google.com>
Reviewed-by: Nicholas Husin <husin@google.com>
2026-09-02 10:48:31 -07:00
Daniel McCarney 82adefa711 ssh: synchronize unexpected response test
Wait for an ordered channel request after sending the unsolicited
response flood. This ensures that the client mux has discarded the
entire flood before SendRequest opens its response gate, avoiding
confusion between an in-flight unsolicited response and the expected
reply.

This fixes TestChannelUnexpectedResponsesDiscarded from flaking, where
otherwise running 1000000 iterations of the test with GOMAXPROCS=16
produced ~15 distinct failures of the form  "expected success reply".

Change-Id: Ic1125c1f44bd919a5cf32b889d331bbc94c06c25
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/817140
Reviewed-by: Neal Patel <neal@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Neal Patel <nealpatel@google.com>
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
Auto-Submit: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: David Chase <drchase@google.com>
2026-08-19 13:42:46 -07:00
Nicholas S. Husin 0f039f3a4a ssh: sanitize various error messages
Similar to CL 780180, sanitize other error messages that are controlled
by the remote peer, to avoid potential injection of control characters.

Also remove sanitizeString usage as it is not strictly needed to escape
error messages safely, and for consistency with how we sanitize other
error messages throughout the codebase.

BannerDisplayStderr is left unsanitized for now, as it is one instance
where we might actually want control characters for valid reasons (see
golang/go#80302).

Updates golang/go#80302
Fixes golang/go#80301

Change-Id: Ie40f4ac2f76afed740dceb548a4b0fa96a6a6964
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/798420
Auto-Submit: Nicholas Husin <nsh@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Nicholas Husin <husin@google.com>
2026-07-13 12:23:17 -07:00
Nicola Murino e8ad5cb7aa ssh: initialize new channels fully before adding them to chanList
newChannel published the channel into the mux's chanList (via
chanList.add) before assigning its localId, and openChannel/Accept set
maxIncomingPayload only after newChannel returned. For an outbound
channel, whose newChannel runs on an application goroutine, the mux
loop could observe the channel through getChan before those fields
were initialized, reading a zero localId or maxIncomingPayload with no
happens-before relationship.

Assign localId inside add while the chanList lock is held, and set
maxIncomingPayload (always channelMaxPacket) in newChannel itself, so
a channel returned by getChan is always fully initialized.

Change-Id: Ib54a5d10dcb5c1ad8ed4dabe5d8d2790778be298
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/797061
Reviewed-by: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
2026-07-12 08:19:12 -07:00
Nicola Murino fe1af3a418 ssh: make channel.close idempotent
channel.close closes the internal Go channels (msg, incomingRequests);
calling it twice panics with "close of closed channel". Both current
callers run on the mux loop and are serialized, so the panic is not
reachable today. Harden close by guarding it with sync.Once, so that
the connection does not become fragile to any future bug that reaches
close twice.

Change-Id: I5d8d948367538ea5561724033c4ee4daa668e221
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/797060
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
2026-07-12 08:19:06 -07:00
Nicola Murino 4c4d20b72c ssh: fix spinloop in mux SendRequest drain on closed channel
The drain loop added in 4e7a738 ("ssh: fix deadlock on unexpected
global responses") receives from m.globalResponses without checking
the comma-ok flag. Once mux.loop closes globalResponses on connection
teardown, the loop spins forever on the zero value, pinning a CPU
core for every concurrent SendRequest caller.

Use the comma-ok idiom to detect the closed channel and break out.

Change-Id: Icf0a7419fe5b206b7a4f70429fa9f25d0b5af731
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/783681
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Neal Patel <nealpatel@google.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Neal Patel <neal@golang.org>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
2026-05-28 10:16:30 -07:00
Nicola Murino e3e62d9601 ssh: fix spinloop in channel SendRequest drain on closed channel
The drain loop added in 3c7c869 ("ssh: fix deadlock on unexpected
channel responses") receives from ch.msg without checking the comma-ok
flag. Once ch.msg is closed by channel.close(), receives succeed
immediately with the zero value, so the default arm is never taken
and the loop spins forever, pinning a CPU core for every concurrent
SendRequest caller on a torn-down channel.

Use the comma-ok idiom to detect the closed channel and break out.

Fixes golang/go#79658

Change-Id: Id80aa5d8429550c7a68e8759aed9ffba3aabb006
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/783680
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Reviewed-by: Neal Patel <neal@golang.org>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Neal Patel <nealpatel@google.com>
2026-05-28 10:16:26 -07:00
Nicola 3c7c86938f ssh: fix deadlock on unexpected channel responses
Previously, channel.handlePacket sent channelRequestSuccess and
channelRequestFailure messages to ch.msg unconditionally via the default
arm of its type switch. Because ch.msg is a bounded buffer (chanSize),
a peer that sends a burst of unsolicited channel request responses for
an open, idle channel fills the buffer and blocks the mux read loop on
the next send. That stalls all packet processing on the connection,
and because readLoop then backs up on t.incoming, closing the
underlying net.Conn does not unblock either goroutine: user code
observes Close() returning promptly while Wait() hangs and the mux,
readLoop, and kexLoop goroutines leak permanently.

This change mirrors the fix for the mux-level SendRequest path: a
sentRequestPending atomic gate is set while a SendRequest with
WantReply is in flight, handlePacket drops responses when the gate is
closed, and uses a non-blocking send otherwise. SendRequest drains
any spurious response that slipped through before discarding it, so
the caller always observes the reply to its own request.

This aligns with OpenSSH, which silently ignores channel confirm
messages that do not match a pending request.

Fixes golang/go#79564
Fixes CVE-2026-39830

Change-Id: I15e2add4bf7876bb0c6f921f8b57203d97e83f47
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/781664
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Neal Patel <nealpatel@google.com>
Reviewed-by: Neal Patel <nealpatel@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2026-05-21 17:09:12 -07:00
Nicola Murino 4e7a7384ec ssh: fix deadlock on unexpected global responses
Previously, the mux implementation handled global request responses by
blocking until the response could be sent to the globalResponses channel.
Since this channel has a buffer size of 1, unsolicited responses from a
server (or responses arriving after a timeout) would fill the buffer.
Subsequent unsolicited responses would block handleGlobalPacket, stalling
the entire connection's read loop and causing a denial of service.

This change modifies handleGlobalPacket to use a non-blocking send. If
no goroutine is waiting for a response (or the buffer is full), the
message is dropped. This aligns with OpenSSH behavior, which ignores
unexpected global responses.

Additionally, SendRequest now drains the globalResponses channel after
acquiring the mutex but before sending the request. This ensures that
any stale responses or "spam" buffered just before the lock was acquired
are discarded, preventing race conditions where a legitimate request
might otherwise consume an unrelated response.

This issue was found during a security audit by NCC Group Cryptography
Services, sponsored by Teleport.

Fixes golang/go#79564
Fixes CVE-2026-39830

Change-Id: Ia0c46355203d557eadcd432c10b87c8a044e1089
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/781640
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Neal Patel <nealpatel@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2026-05-21 17:02:53 -07:00
Nicola Murino 6c195c8a97 ssh: prevent memory leak when rejecting channels
When a server rejects an incoming channel request via
NewChannel.Reject, the channel is left in the multiplexer's
channel list. Because the channel is never explicitly removed or
closed, its internal buffers and sync primitives remain allocated
for the lifetime of the SSH connection.

A malicious client could exploit this behavior by repeatedly
requesting to open channels that are destined to be rejected,
causing unbounded memory growth and potentially leading to a
Denial of Service (DoS) via resource exhaustion.

This change fixes the leak by calling ch.mux.chanList.remove
within the Reject method, removing the channel from the list and allowing the
garbage collector to reclaim the associated memory immediately.

Fixes golang/go#35127
Fixes CVE-2026-3982

Change-Id: Iaa177f5dfd151812dd404e528a4a1c77527a0e29
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/781320
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Nicholas Husin <nsh@golang.org>
Reviewed-by: Nicholas Husin <husin@google.com>
2026-05-21 11:00:25 -07:00
Will Mortensen fdfe1f8531 ssh: defer channel window adjustment
Sending a window adjustment after every read is unnecessarily chatty,
especially with a series of small reads like with TTY interactions.

Copy OpenSSH's logic for deferring these, which seemingly hasn't changed
since 2007. Note that since channelWindowSize and c.maxIncomingPayload
are currently constants here, the two checks could be combined into a
single check for c.myWindow < 2 MiB - 96 KiB (with the current values
of the constants).

Fixes golang/go#57424.

Change-Id: Ifcef5be76fcc3f0b1a6dc396096bed9c50d64f21
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/459915
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Nicola Murino <nicola.murino@gmail.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Commit-Queue: Nicola Murino <nicola.murino@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-11-27 17:27:49 +00:00
Bryan C. Mills ff15cd57d1 ssh: eliminate some goroutine leaks in tests and examples
This should fix the "Log in goroutine" panic seen in
https://build.golang.org/log/e42bf69fc002113dbccfe602a6c67fd52e8f31df,
as well as a few other related leaks. It also helps to verify that
none of the functions under test deadlock unexpectedly.

See https://go.dev/wiki/CodeReviewComments#goroutine-lifetimes.

Updates golang/go#58901.

Change-Id: Ica943444db381ae1accb80b101ea646e28ebf4f9
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/541095
Auto-Submit: Bryan Mills <bcmills@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2023-11-09 17:49:15 +00:00
Nicola Murino edc325d13a ssh: fix call to Fatalf from a non-test goroutine
Also fix some redundant type declarations.

Change-Id: Iad2950b67b1ec2e2590c59393b8ad15421ed3add
GitHub-Last-Rev: 41cf552f11387208491dee7b867050475043b25e
GitHub-Pull-Request: golang/crypto#263
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/505798
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
2023-07-31 18:14:41 +00:00
cui fliter 35f4265a4b all: replace io/ioutil with io and os package
For golang/go#45557

Change-Id: I447530cc66896aef7a8d528ccb8d095b80e3cf47
GitHub-Last-Rev: 5f385ff46487ac318bd1147cdbbd26bb0ffd0426
GitHub-Pull-Request: golang/crypto#230
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/430797
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Meng Zhuo <mzh@golangcn.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
2022-09-19 17:36:07 +00:00
Erick Tryzelaar 279210d13f ssh: don't err out on channel request msgs to unknown channels
rfc4254 section 5.4 states that channel request messages sent to an
unrecognized channel should be replied with a `SSH_MSG_CHANNEL_FAILURE`,
rather than erring out and closing the mux. This can occur with servers
like openssh-portable, which can begin to close a channel and also use
that channel for keepalives before it has received a closed response
from the client.

Fixes golang/go#38908

Change-Id: Id68b77e16b2889d3a21d505ed8018f9f457e067a
GitHub-Last-Rev: 8a92f87dc30697d9e3805af695efdf1b1dc8409b
GitHub-Pull-Request: golang/crypto#136
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/232659
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2020-06-02 18:02:16 +00:00
Igor Zhilianin 45a5f77698 all: fix typos
Change-Id: I62cbcfcd0be5f6a74d93b85b24ff7607533bb239
GitHub-Last-Rev: 9967869e706e9fe7d13964bb32b30a44ba640869
GitHub-Pull-Request: golang/crypto#64
Reviewed-on: https://go-review.googlesource.com/c/145240
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2018-10-29 02:12:03 +00:00
Han-Wen Nienhuys b2aa35443f ssh: drop extra connection Close call in TestMuxReadWrite
The extra Close call runs in a separate goroutine, and races with the
deferred Close call, leading to test flakes if the extra call fails

Change-Id: Ia1a2fb3932906da7cd0a8854b55f82785bb33716
Reviewed-on: https://go-review.googlesource.com/104335
Run-TryBot: Han-Wen Nienhuys <hanwen@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2018-04-03 16:09:46 +00:00
Han-Wen Nienhuys b05791f2e1 ssh: add debug print at the lowest level
This is a simple minded, fast print, suitable for debugging timing
sensitive issues.

Change-Id: I9ae3df5fe86f1883c1fa9265b6f7f9a98d33747e
Reviewed-on: https://go-review.googlesource.com/36054
Run-TryBot: Han-Wen Nienhuys <hanwen@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2017-02-02 09:54:18 +00:00
Han-Wen Nienhuys 9e7f5dc375 x/crypto/ssh: interpret disconnect message as error in the transport layer.
This ensures that higher level parts (e.g. the client authentication
loop) never have to deal with disconnect messages.

Fixes https://github.com/coreos/fleet/issues/565.

Change-Id: Ie164b6c4b0982c7ed9af6d3bf91697a78a911a20
Reviewed-on: https://go-review.googlesource.com/20801
Reviewed-by: Anton Khramov <anton@endocode.com>
Reviewed-by: Adam Langley <agl@golang.org>
2016-03-29 14:38:23 +00:00
Han-Wen Nienhuys fc84ae5437 go.crypto/ssh: reuse packet buffer for channel writes.
Test that different extended data streams within a channel are
thread-safe.

benchmark             old MB/s     new MB/s     speedup
BenchmarkEndToEnd     79.26        87.98        1.11x

benchmark                          old allocs     new allocs     delta
BenchmarkEndToEnd                  110            73             -33.64%

benchmark                          old bytes     new bytes     delta
BenchmarkEndToEnd                  2605720       1299768       -50.12%

LGTM=dave, jpsugar
R=agl, dave, jpsugar
CC=golang-codereviews
https://golang.org/cl/136420043
2014-09-16 12:30:05 -07:00
Adam Langley fa50e7408b go.crypto/ssh: import gosshnew.
See https://groups.google.com/d/msg/Golang-nuts/AoVxQ4bB5XQ/i8kpMxdbVlEJ

R=hanwen
CC=golang-codereviews
https://golang.org/cl/86190043
2014-04-09 13:57:52 -07:00