Skip to content

Conversation

@DAlperin
Copy link
Member

@DAlperin DAlperin commented Aug 30, 2025

This implements the SASL/SCRAM protocol as implemented by postgres. The goal is to be compatible with postgres clients that support/expect SASL/SCRAM.

Motivation

Tips for reviewer

Checklist

  • This PR has adequate test coverage / QA involvement has been duly considered. (trigger-ci for additional test/nightly runs)
  • This PR has an associated up-to-date design doc, is a design doc (template), or is sufficiently small to not require a design.
  • If this PR evolves an existing $T ⇔ Proto$T mapping (possibly in a backwards-incompatible way), then it is tagged with a T-proto label.
  • If this PR will require changes to cloud orchestration or tests, there is a companion cloud PR to account for those changes that is tagged with the release-blocker label (example).
  • If this PR includes major user-facing behavior changes, I have pinged the relevant PM to schedule a changelog post.

Copy link
Contributor

@jasonhernandez jasonhernandez left a comment

Choose a reason for hiding this comment

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

I did a preliminary review and wanted to give some guidance on timing attacks and input validation. I assume some of these are already on your mental to-do list even if not called out.

@DAlperin DAlperin force-pushed the dov/sasl-scram branch 4 times, most recently from a5bf100 to fde3947 Compare September 8, 2025 21:00
@DAlperin DAlperin marked this pull request as ready for review September 8, 2025 21:00
@DAlperin DAlperin requested a review from a team as a code owner September 8, 2025 21:00
@DAlperin DAlperin requested a review from SangJunBak September 8, 2025 21:00
@SangJunBak
Copy link
Contributor

Sorry I just saw this! Looking over it now

@DAlperin DAlperin force-pushed the dov/sasl-scram branch 4 times, most recently from 48ee1a6 to ae20c1f Compare September 18, 2025 18:33
Copy link
Contributor

@SangJunBak SangJunBak left a comment

Choose a reason for hiding this comment

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

Overall the code looks great, just recommendations on documentation and testing. Nits are optional but I think we should do the comments related to testing. Lemme know if you want me to clarify anything in this review

) {
let role = self.catalog().try_get_role_by_name(role_name.as_str());
let role_auth = role
.as_ref()
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think the as_refs here, line 292, and line 297 are redundant

let real_hash = role_auth
.as_ref()
.and_then(|auth| auth.password_hash.as_ref());
let hash_ref: &str = real_hash.map(|s| s.as_str()).unwrap_or(&mock_hash);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think the explicit type annotation is redundant

Comment on lines 197 to 207
let signing_key = openssl::pkey::PKey::hmac(&server_key)
.map_err(|e| VerifyError::Hash(HashError::Openssl(e)))?;
let mut signer =
openssl::sign::Signer::new(openssl::hash::MessageDigest::sha256(), &signing_key)
.map_err(|e| VerifyError::Hash(HashError::Openssl(e)))?;
signer
.update(auth_message.as_bytes())
.map_err(|e| VerifyError::Hash(HashError::Openssl(e)))?;
let server_signature = signer
.sign_to_vec()
.map_err(|e| VerifyError::Hash(HashError::Openssl(e)))?;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Can probably de-duplicate this code w/ the client signature code by making it a helper function or anonymous function

pub enum SASLServerFinalMessageKinds {
Verifier(String),
// The spec specifies an Error kind here but PG just uses
// it's own error handling
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: its*

Err(_) => {
send_mock_challenge(
role_name,
self.catalog().state().mock_authentication_nonce(),
Copy link
Contributor

Choose a reason for hiding this comment

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

It's not obvious the purpose of mock_authentication_nonce. We should probably document this above the definition of send_mock_challenge and here as well

let opts1 = mock_sasl_challenge(username, mock);
let opts2 = mock_sasl_challenge(username, mock);
assert_eq!(opts1, opts2);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd actually like to see more unit tests for sasl_verify! Especially since it's a pure function. Maybe something like:

  • Positive case
  • Client proof doesn't match

&user,
&response.proof,
&auth_message,
&mock_hash,
Copy link
Contributor

@SangJunBak SangJunBak Sep 22, 2025

Choose a reason for hiding this comment

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

I'm guessing mock_hash is the hash generated from the mock_nonce and mock_sasl_challenge? Not immediately obvious the purpose of it and where it comes from, especially since the control flow goes jumps back and forth between coord messages. Should probably document where it comes from and the purpose it serves!

@SangJunBak
Copy link
Contributor

SangJunBak commented Sep 22, 2025

I think the next step is rigging up the orchestratord changes. Not sure if you'd like to do that or delegate that to Alex. Regardless should prolly keep the self managed peeps updated!

@DAlperin DAlperin force-pushed the dov/sasl-scram branch 2 times, most recently from cb26950 to 3fcdb9f Compare September 22, 2025 16:21
@DAlperin DAlperin enabled auto-merge (squash) September 22, 2025 16:21
Copy link
Contributor

@jasonhernandez jasonhernandez left a comment

Choose a reason for hiding this comment

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

Thanks for addressing my comments / review. I think this looks ok from a security standpoint. More comments / test cases in line with @SangJunBak's recommendations are nice to add but not blocking re: security.

@DAlperin DAlperin merged commit 2c17d81 into MaterializeInc:main Sep 22, 2025
130 checks passed
DAlperin added a commit to DAlperin/materialize that referenced this pull request Sep 23, 2025
The
(SASL/SCRAM)[MaterializeInc#33468]
PR introduces the need for some stable, cluster wide, cryptographically
random key material. We use this material to be able to present
deterministic challenges for even users that don't exist to guard
against enumeration attacks. Old versions of the catalog won't have this
so we have to add it.
DAlperin added a commit to DAlperin/materialize that referenced this pull request Sep 23, 2025
The
(SASL/SCRAM)[MaterializeInc#33468]
PR introduces the need for some stable, cluster wide, cryptographically
random key material. We use this material to be able to present
deterministic challenges for even users that don't exist to guard
against enumeration attacks. 

However that PR made a bad assumption that the initialize step of
catalog opening would always add this new key. But old versions that
have already been initialized wouldn't have it! This PR add code to
generate it for old versions
DAlperin added a commit to DAlperin/materialize that referenced this pull request Sep 23, 2025
The
(SASL/SCRAM)[MaterializeInc#33468]
PR introduces the need for some stable, cluster wide, cryptographically
random key material. We use this material to be able to present
deterministic challenges for even users that don't exist to guard
against enumeration attacks. 

However that PR made a bad assumption that the initialize step of
catalog opening would always add this new key. But old versions that
have already been initialized wouldn't have it! This PR add code to
generate it for old versions
DAlperin added a commit to DAlperin/materialize that referenced this pull request Sep 23, 2025
The
[SASL/SCRAM](MaterializeInc#33468)
PR introduces the need for some stable, cluster wide, cryptographically
random key material. We use this material to be able to present
deterministic challenges for even users that don't exist to guard
against enumeration attacks. 

However that PR made a bad assumption that the initialize step of
catalog opening would always add this new key. But old versions that
have already been initialized wouldn't have it! This PR add code to
generate it for old versions
petrosagg added a commit to petrosagg/materialize that referenced this pull request Sep 24, 2025
petrosagg added a commit to petrosagg/materialize that referenced this pull request Sep 24, 2025
DAlperin added a commit to DAlperin/materialize that referenced this pull request Sep 24, 2025
The
[SASL/SCRAM](MaterializeInc#33468)
PR introduces the need for some stable, cluster wide, cryptographically
random key material. We use this material to be able to present
deterministic challenges for even users that don't exist to guard
against enumeration attacks. 

However that PR made a bad assumption that the initialize step of
catalog opening would always add this new key. But old versions that
have already been initialized wouldn't have it! This PR add code to
generate it for old versions
teskje pushed a commit to teskje/materialize that referenced this pull request Sep 25, 2025
The
[SASL/SCRAM](MaterializeInc#33468)
PR introduces the need for some stable, cluster wide, cryptographically
random key material. We use this material to be able to present
deterministic challenges for even users that don't exist to guard
against enumeration attacks. 

However that PR made a bad assumption that the initialize step of
catalog opening would always add this new key. But old versions that
have already been initialized wouldn't have it! This PR add code to
generate it for old versions
DAlperin added a commit that referenced this pull request Sep 25, 2025
The
[SASL/SCRAM](#33468)
PR introduces the need for some stable, cluster wide, cryptographically
random key material. We use this material to be able to present
deterministic challenges for even users that don't exist to guard
against enumeration attacks. 

However that PR made a bad assumption that the initialize step of
catalog opening would always add this new key. But old versions that
have already been initialized wouldn't have it! This PR add code to
generate it for old versions

<!--
Describe the contents of the PR briefly but completely.

If you write detailed commit messages, it is acceptable to copy/paste
them
here, or write "see commit messages for details." If there is only one
commit
in the PR, GitHub will have already added its commit message above.
-->

### Motivation

Fixes MaterializeInc/database-issues#9724

<!--
Which of the following best describes the motivation behind this PR?

  * This PR fixes a recognized bug.

    [Ensure issue is linked somewhere.]

  * This PR adds a known-desirable feature.

    [Ensure issue is linked somewhere.]

  * This PR fixes a previously unreported bug.

    [Describe the bug in detail, as if you were filing a bug report.]

  * This PR adds a feature that has not yet been specified.

[Write a brief specification for the feature, including justification
for its inclusion in Materialize, as if you were writing the original
     feature specification.]

   * This PR refactors existing code.

[Describe what was wrong with the existing code, if it is not obvious.]
-->

### Tips for reviewer

<!--
Leave some tips for your reviewer, like:

    * The diff is much smaller if viewed with whitespace hidden.
    * [Some function/module/file] deserves extra attention.
* [Some function/module/file] is pure code movement and only needs a
skim.

Delete this section if no tips.
-->

### Checklist

- [x] This PR has adequate test coverage / QA involvement has been duly
considered. ([trigger-ci for additional test/nightly
runs](https://trigger-ci.dev.materialize.com/))
- [ ] This PR has an associated up-to-date [design
doc](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/design/README.md),
is a design doc
([template](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/design/00000000_template.md)),
or is sufficiently small to not require a design.
  <!-- Reference the design in the description. -->
- [ ] If this PR evolves [an existing `$T ⇔ Proto$T`
mapping](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/command-and-response-binary-encoding.md)
(possibly in a backwards-incompatible way), then it is tagged with a
`T-proto` label.
- [ ] If this PR will require changes to cloud orchestration or tests,
there is a companion cloud PR to account for those changes that is
tagged with the release-blocker label
([example](MaterializeInc/cloud#5021)).
<!-- Ask in #team-cloud on Slack if you need help preparing the cloud
PR. -->
- [ ] If this PR includes major [user-facing behavior
changes](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/guide-changes.md#what-changes-require-a-release-note),
I have pinged the relevant PM to schedule a changelog post.
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.

3 participants