Skip to content

Conversation

vonzshik
Copy link
Contributor

Right now whenever we authenticate via SASL, we might either connect with channel binding (SCRAM-SHA-256-PLUS) or without (SCRAM-SHA-256), depending on whether the server supports it and ChannelBinding property in connection string. Now, in some cases (like the cert passed from the server has an unsupported hash algorithm) we might allow to downgrade from SCRAM-SHA-256-PLUS to SCRAM-SHA-256, as long as the server supports auth without channel binding. The problem is that it seems like we ignore ChannelBinding, so even if a user passes ChannelBinding.Require we can still potentially downgrade. This change makes sure that in that case we'll throw an exception instead of going through.

Shouldn't be much of a security issue as I fully expect that in case of a problematic cert we'll fail somewhere along TLS handshake.

@vonzshik vonzshik added the bug label Feb 21, 2025
@vonzshik vonzshik added this to the 9.0.3 milestone Feb 21, 2025
@vonzshik vonzshik self-assigned this Feb 21, 2025
@vonzshik vonzshik requested a review from roji as a code owner February 21, 2025 10:28
@@ -95,7 +95,7 @@ async Task AuthenticateSASL(List<string> mechanisms, string username, bool async
if (clientSupportsSha256Plus)
DataSource.TransportSecurityHandler.AuthenticateSASLSha256Plus(this, ref mechanism, ref cbindFlag, ref cbind, ref successfulBind);

if (!successfulBind && serverSupportsSha256)
if (!successfulBind && clientSupportsSha256)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to not just check both, in order to avoid attempting SCRAM-SHA-256 if the server doesn't support it? At the very least, it would probably help generate a more meaningful exception message?

BTW we can in general try to raise more informative/specific exception messages here for the precise condition. For example, if I read the code write, we always throw "unable to bind to SCRAM-SHA-256-PLUS" below, even if e.g. the client allows SCRAM-SHA-256 but the server doesn't etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to not just check both, in order to avoid attempting SCRAM-SHA-256 if the server doesn't support it? At the very least, it would probably help generate a more meaningful exception message?

clientSupportsSha256 has a bit misleading name, as it accounts for whether both the client (us) and the server support SCRAM-SHA-256. I just can't really think of a better name other than clientAndServerSupportSha256 or connectionSupportsSha256, and both of them are quite mouthful. Maybe allowSha256?

var serverSupportsSha256 = mechanisms.Contains("SCRAM-SHA-256");
var clientSupportsSha256 = serverSupportsSha256 && Settings.ChannelBinding != ChannelBinding.Require;

BTW we can in general try to raise more informative/specific exception messages here for the precise condition. For example, if I read the code write, we always throw "unable to bind to SCRAM-SHA-256-PLUS" below, even if e.g. the client allows SCRAM-SHA-256 but the server doesn't etc.

You mean, the client wants only SCRAM-SHA-256 but the server wants SCRAM-SHA-256-PLUS? Because we already handle that above, this check in particular is if and only if the server supports only SCRAM-SHA-256-PLUS but there was some issue with it (e.g. unsupported cert algorithm).

var serverSupportsSha256 = mechanisms.Contains("SCRAM-SHA-256");
var clientSupportsSha256 = serverSupportsSha256 && Settings.ChannelBinding != ChannelBinding.Require;
var serverSupportsSha256Plus = mechanisms.Contains("SCRAM-SHA-256-PLUS");
var clientSupportsSha256Plus = serverSupportsSha256Plus && Settings.ChannelBinding != ChannelBinding.Disable;
if (!clientSupportsSha256 && !clientSupportsSha256Plus)
{
if (serverSupportsSha256 && Settings.ChannelBinding == ChannelBinding.Require)
throw new NpgsqlException($"Couldn't connect because {nameof(ChannelBinding)} is set to {nameof(ChannelBinding.Require)} " +
"but the server doesn't support SCRAM-SHA-256-PLUS");
if (serverSupportsSha256Plus && Settings.ChannelBinding == ChannelBinding.Disable)
throw new NpgsqlException($"Couldn't connect because {nameof(ChannelBinding)} is set to {nameof(ChannelBinding.Disable)} " +
"but the server doesn't support SCRAM-SHA-256");
throw new NpgsqlException("No supported SASL mechanism found (only SCRAM-SHA-256 and SCRAM-SHA-256-PLUS are supported for now). " +
"Mechanisms received from server: " + string.Join(", ", mechanisms));
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clientSupportsSha256 has a bit misleading name, as it accounts for whether both the client (us) and the server support SCRAM-SHA-256. I just can't really think of a better name other than clientAndServerSupportSha256 or connectionSupportsSha256, and both of them are quite mouthful. Maybe allowSha256?

Ah I see, I was indeed misled... allowSha256 sounds pretty good...

Otherwise I didn't look too closely at the exception, if you think we're specific enough that's more than fine for me.

@vonzshik vonzshik enabled auto-merge (squash) February 24, 2025 10:56
@vonzshik vonzshik merged commit 01155b6 into main Feb 24, 2025
13 checks passed
@vonzshik vonzshik deleted the disallow-channel-binding-downgrade branch February 24, 2025 11:05
vonzshik added a commit that referenced this pull request Feb 24, 2025
vonzshik added a commit that referenced this pull request Feb 24, 2025
@vonzshik
Copy link
Contributor Author

Backported to 9.0.3 via 97dc173, 8.0.6 via a13fc41

rosicley pushed a commit to Brick-Abode/npgsql that referenced this pull request Aug 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants