Repository navigation
Conversation
fiorix
left a comment
There was a problem hiding this comment.
Requesting changes for two blockers:
-
Client-side upgrade is not gated on CEA negotiation.
handleCEAcallsStartTLSClientwheneverSettings.TLSConfigis non-nil and the connection is plain, butsmparser.CEAdoes not parseInband-Security-Id. A client with TLS config will send a TLS ClientHello after any successful plain CEA, even when the peer did not echoInband-Security-Id=1. -
The advertised
TLSUpgraderpath is unsafe for SCTP/multistream connections.responseimplementsStartTLS, sohandleCERaccepts in-band TLS for any response connection, but multistream connections skipc.bufinitialization innewConn.startTLSthen dereferencesw.conn.buf.Reader, which can panic after a success CEA.
go test ./diam/sm ./diam -count=1 passed. These are uncovered negative cases and should be fixed before merge.
|
Re-reviewed at df08762 against current main. Keeping changes requested. The use case is legitimate but narrow: RFC 6733 retains post-CER TLS only as a compatibility path for older peers and requires both sides to agree on it; ordinary deployments should keep using TLS-before-CER. For this to land it needs a revised implementation covering the four points below. The first three are reproduced defects, not style. 1. The client upgrades without any negotiation. In 2. Multistream connections panic after advertising TLS capability. 3. The upgrade races CloseNotify for the raw reader. Once a 4. The dispatch change contradicts "zero impact when unused". The branch also conflicts with current main in diam/sm/cer.go and diam/sm/sm.go and will need a rebase regardless. |
RFC 6733 retains the RFC 3588 in-band upgrade for backward compatibility (§1.1.3, §2.1, §5.6). §13: "For TLS/TCP and DTLS/SCTP connections to be established in the open state, the CER/CEA exchange MUST include an Inband-Security-ID AVP with a value of TLS/TCP and DTLS/SCTP. [...] If the handshake fails, both ends MUST move to the closed state." TLS before the CER remains the recommended configuration (§5.6, §13); this is opt-in through Settings.TLSConfig and off by default. Negotiation - Server: a CER asking for Inband-Security-Id=1 is accepted only if TLSConfig is set and the connection reports CanStartTLS(); otherwise the answer is DIAMETER_NO_COMMON_SECURITY. The success CEA carries Inband-Security-Id=1 and the handshake follows it. A failed handshake closes the connection. - Client: the requested mechanism is recorded on the connection and smparser.CEA now parses Inband-Security-Id. The client upgrades only when it asked for TLS and the CEA selects it. A CEA that omits the AVP or answers 0 fails the handshake rather than continuing in cleartext. A client that only advertises 1 (TLSConfig nil, as with Client.InbandSecurityID today) fails if the peer selects TLS, since the peer will then start a handshake; if the peer declines, nothing changes. Transport - New diam.TLSUpgrader, implemented by the server-side Conn. Its CanStartTLS() is false for a MultistreamConn (§2.1 runs DTLS, not TLS, over SCTP; there is also no buffered reader), for a connection already on TLS, and for a handler not running on the serve goroutine. The state machine checks it before answering, so an unsupported transport is refused rather than failing after the CEA. - StartTLS reads through the connection's active read chain: bytes already buffered, then the CloseNotify pipe if one is registered (starting it if still pending), then the socket. It no longer competes with the CloseNotify copy goroutine, and close notification keeps working after the upgrade. A failure draining buffered bytes is returned. - rwc and tlsState are swapped under a new RWMutex so Close, RemoteAddr, LocalAddr, TLS and Connection stay safe from other goroutines, e.g. the HandshakeTimeout timer or a watcher started in OnNewConnection. The lock is not held during the handshake, so the timer can still abort a stalled one. Dispatch - With MaxConcurrentHandlers != 0, the capabilities exchange is dispatched on the serve goroutine only if the Handler implements the new diam.InbandTLSHandler and reports it enabled (sm.StateMachine does when TLSConfig is set), and the connection can still be upgraded. This is a behaviour change for such Handlers only; every other Handler, connection and command dispatches as before. A Handler that wraps a StateMachine without forwarding InbandTLSEnabled keeps concurrent dispatch, and in-band TLS is then refused with DIAMETER_NO_COMMON_SECURITY rather than raced. Tests cover each point above; each fails if the corresponding change is reverted.
df08762 to
f996389
Compare
|
Thanks for the re-review. Rebased on main (92e4536) as a single commit. Each point below has a test that fails when the corresponding change is reverted. 1. Client upgrades only on agreement. A related case: a client that only advertises 2. Unsupported transports are refused before the CEA. The new 3. The upgrade takes over the active reader. Two further problems I found while working on this:
4. Dispatch is unchanged unless the feature is in use. Under RFC references now point to: §13 for the MUST on Inband-Security-Id in the CER/CEA exchange and on moving to the closed state if the handshake fails; §1.1.3, §2.1 and §5.6 for the backward-compatibility scenario; §2.1 for SCTP/DTLS; §6.10 for the AVP values and the default when it is omitted. The docs say TLS before the CER remains the recommended configuration.
|
RFC 6733 retains the RFC 3588 in-band upgrade for backward compatibility
(§1.1.3, §2.1, §5.6). §13: "For TLS/TCP and DTLS/SCTP connections to be
established in the open state, the CER/CEA exchange MUST include an
Inband-Security-ID AVP with a value of TLS/TCP and DTLS/SCTP. [...] If
the handshake fails, both ends MUST move to the closed state." TLS
before the CER remains the recommended configuration (§5.6, §13); this
is opt-in through Settings.TLSConfig and off by default.
Negotiation
TLSConfig is set and the connection reports CanStartTLS(); otherwise
the answer is DIAMETER_NO_COMMON_SECURITY. The success CEA carries
Inband-Security-Id=1 and the handshake follows it. A failed handshake
closes the connection.
smparser.CEA now parses Inband-Security-Id. The client upgrades only
when it asked for TLS and the CEA selects it. A CEA that omits the AVP
or answers 0 fails the handshake rather than continuing in cleartext.
A client that only advertises 1 (TLSConfig nil, as with
Client.InbandSecurityID today) fails if the peer selects TLS, since
the peer will then start a handshake; if the peer declines, nothing
changes.
Transport
CanStartTLS() is false for a MultistreamConn (§2.1 runs DTLS, not TLS,
over SCTP; there is also no buffered reader), for a connection already
on TLS, and for a handler not running on the serve goroutine. The
state machine checks it before answering, so an unsupported transport
is refused rather than failing after the CEA.
already buffered, then the CloseNotify pipe if one is registered
(starting it if still pending), then the socket. It no longer competes
with the CloseNotify copy goroutine, and close notification keeps
working after the upgrade. A failure draining buffered bytes is
returned.
LocalAddr, TLS and Connection stay safe from other goroutines, e.g.
the HandshakeTimeout timer or a watcher started in OnNewConnection.
The lock is not held during the handshake, so the timer can still
abort a stalled one.
Dispatch
dispatched on the serve goroutine only if the Handler implements the
new diam.InbandTLSHandler and reports it enabled (sm.StateMachine does
when TLSConfig is set), and the connection can still be upgraded. This
is a behaviour change for such Handlers only; every other Handler,
connection and command dispatches as before. A Handler that wraps a
StateMachine without forwarding InbandTLSEnabled keeps concurrent
dispatch, and in-band TLS is then refused with
DIAMETER_NO_COMMON_SECURITY rather than raced.
Tests cover each point above; each fails if the corresponding change is
reverted.